基于C++的TCP通信方式传输BASE64编码的图像

 · 2023-12-29 · 次阅读


基于C++的TCP通信方式传输BASE64编码的图像

  • 工期紧张,仅记录代码,具体原理解析后续再补充

windows服务器端 进行decode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <winsock2.h>
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>

#pragma comment(lib, "ws2_32.lib")

static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

// 检查字符是否属于Base64编码字符集
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}

// 清理Base64字符串,移除非Base64字符
std::string cleanBase64String(const std::string& input) {
std::string output;
output.reserve(input.size());

std::copy_if(input.begin(), input.end(), std::back_inserter(output), is_base64);

return output;
}

// Base64解码函数
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;

while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);

char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}

if (i) {
for (j = i; j < 4; j++)
char_array_4[j] = 0;

for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);

char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}

return ret;
}

int main() {
WSADATA wsaData;
SOCKET serverSocket, clientSocket;
struct sockaddr_in server, client;
char buffer[1024] = {0};
int clientSize = sizeof(client);
std::stringstream ss;

// 初始化Winsock
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) {
std::cerr << "Winsock初始化失败。错误代码: " << WSAGetLastError() << std::endl;
return 1;
}

// 创建套接字
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
std::cerr << "套接字创建失败。错误代码: " << WSAGetLastError() << std::endl;
WSACleanup();
return 1;
}

// 准备sockaddr_in结构
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr("192.168.50.218"); // 特定的IP地址
server.sin_port = htons(8888);

// 绑定套接字
if (bind(serverSocket, (struct sockaddr *)&server, sizeof(server)) == SOCKET_ERROR) {
std::cerr << "绑定失败。错误代码: " << WSAGetLastError() << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}

// 监听套接字
listen(serverSocket, 3);

// 等待传入连接
std::cout << "等待传入连接..." << std::endl;

clientSocket = accept(serverSocket, (struct sockaddr *)&client, &clientSize);
if (clientSocket == INVALID_SOCKET) {
std::cerr << "接受连接失败。错误代码: " << WSAGetLastError() << std::endl;
closesocket(serverSocket);
WSACleanup();
return 1;
}

std::cout << "连接建立!" << std::endl;

// 主循环
while (true) {
memset(buffer, 0, 1024);
int bytesReceived = recv(clientSocket, buffer, 1024, 0);
if (bytesReceived == SOCKET_ERROR || bytesReceived == 0) {
std::cerr << "客户端断开连接" << std::endl;
break;
}

ss << buffer;

if (ss.str().find("END") != std::string::npos) {
std::string encoded_str = ss.str();
size_t end_pos = encoded_str.find("END");
encoded_str = encoded_str.substr(0, end_pos);

// 在解码前清理Base64字符串
std::string clean_encoded_str = cleanBase64String(encoded_str);

std::string decoded_data = base64_decode(clean_encoded_str);
std::ofstream output_file("output.jpg", std::ios::binary);
if (output_file.is_open()) {
output_file.write(decoded_data.c_str(), decoded_data.size());
output_file.close();
std::cout << "解码图像已写入output.jpg" << std::endl;
} else {
std::cerr << "打开输出文件失败。" << std::endl;
}

break; // 结束循环
}
}

// 清理并关闭套接字
closesocket(clientSocket);
closesocket(serverSocket);
WSACleanup();

return 0;
}

Linux端TCP客户端并进行图片的BASE64编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <fstream>
#include <vector>
#include <string>

static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";

std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];

while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;

for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}

if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';

char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;

for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];

while((i++ < 3))
ret += '=';
}

return ret;
}

int main() {
int sock;
struct sockaddr_in server;

// 创建套接字
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1) {
std::cerr << "Could not create socket" << std::endl;
return 1;
}

server.sin_addr.s_addr = inet_addr("192.168.50.218");
server.sin_family = AF_INET;
server.sin_port = htons(8888);

// 连接到服务器
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0) {
std::cerr << "Connect failed. Error" << std::endl;
return 1;
}

std::cout << "Connected to server" << std::endl;

std::ifstream file("./image0.jpg", std::ios::binary);
if (!file.is_open()) {
std::cerr << "Failed to open file." << std::endl;
return 1;
}

// Read the file into a vector
std::vector<unsigned char> buffer(std::istreambuf_iterator<char>(file), {});

// Encode to base64
std::string encoded = base64_encode(&buffer[0], buffer.size());


// 发送Base64编码的图像
send(sock, encoded.c_str(), encoded.length(), 0);

// 发送结束标志 “END”
send(sock, "END", 3, 0);

// 关闭套接字
close(sock);

return 0;
}

工作效果

生成正确

image-20231229040348477

过程中的记录

因为TCP分包问题导致产生额外的转义字符,通过设计消除函数,消除了转义字符,从而正确工作

通信协议中的转义字符 - ChuckLu - 博客园 (cnblogs.com)

image-20231229040649642

只显示了第一个转义字符之前的部分

image-20231229040716668

修复后正常工作

image-20231229040811606

![](https://azure-home.oss-cn-shenzhen.aliyuncs.com/img/DALL·E 2023-12-29 12.36.57 - A modern and sleek logo for ‘AzureWave’%2C a technology project involving AI and TCP-based image processing. The logo should symbolize AI%2C connectivity%2C.png)