网络协议
# 认识协议
网络协议(Network Protocol)是一套预先定义的规则、标准和约定的集合。它规定了网络中的设备(如计算机、服务器、路由器等)之间如何交换信息、如何进行通信,协议最终都需要通过计算机语言的方式表示出来。只有通信计算机双方都遵守相同的协议,计算机之间才能互相通信交流。
# 结构化数据的传输

- 序列化 (Serialization): 将程序中的对象或数据结构转换成一种可以存储或传输的格式(通常是一个字节序列)的过程
- 反序列化 (Deserialization): 将序列化后得到的格式(字节序列)重新转换回程序中的对象或数据结构的过程
# 实现网络计算器
protocol.hpp
#pragma once
#include <json/json.h>
#include <cstring>
#include <iostream>
#include <string>
#include "log.hpp"
#define SEP " "
#define SEP_LEN strlen(SEP)
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)
// Request & Response
enum { SUCCESS = 0, DIV_ZERO, MOD_ZERO, OP_ERR };
// 协议定制:
// 为了每次都读到一个报文
// 增加长度头部,形成完整报文:"num1 op num2\r\n" -> "content_len\r\n""num1 op num2\r\n"
string enLength(const string& text) {
string send_string = to_string(text.size()) + LINE_SEP + text + LINE_SEP;
return send_string;
}
// 去除长度头部:"content_len\r\n""num1 op num2\n\r" -> "num1 op num2\n\r"
bool deLength(string& text, string* out) {
int pos = text.find(LINE_SEP);
if (pos == string::npos)
return false;
int content_len = stoi(text.substr(0, pos));
*out = text.substr(pos + LINE_SEP_LEN, content_len);
return true;
}
// 从socket接收完整报文(解决TCP粘包问题),保证获取到完整的报文:
// "content_len\r\n""num1 op num2\n\r""content_len\r\n""num1 op num2\n\r"
bool recvRequset(int sock, string& inbuffer, string* out) {
char buffer[1024];
while (true) {
ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (n == 0)
return false;
else {
buffer[n] = 0;
inbuffer += buffer;
int pos = inbuffer.find(LINE_SEP);
if (pos == string::npos)
continue;
string text_len_string = inbuffer.substr(0, pos);
int text_len = stoi(text_len_string);
int total_len = text_len_string.size() + 2 * LINE_SEP_LEN + text_len;
if (inbuffer.size() < total_len)
continue;
// 至少有一个完整的报文
*out = inbuffer.substr(0, total_len);
inbuffer.erase(0, total_len);
break;
}
}
return true;
}
// 网络计算器
class Request {
public:
void serialize(string* out) {
#ifdef MySelf
// "num1 op num2"
string num1_string = to_string(num1);
string num2_string = to_string(num2);
*out = num1_string + SEP + op + SEP + num2_string;
#else
// Json
Json::Value root;
root["num1"] = num1;
root["num2"] = num2;
root["op"] = op;
Json::StyledWriter writer;
*out = writer.write(root);
#endif
}
// "num1 op num2\n\r"
bool deserialize(const string& in) {
#ifdef MySelf
auto left = in.find(SEP);
auto right = in.rfind(SEP);
// 异常处理
if (left == string::npos || right == string::npos || left == right)
return false;
string num1_string = in.substr(0, left);
string num2_string = in.substr(right + SEP_LEN);
op = in[left + SEP_LEN];
num1 = stoi(num1_string);
num2 = stoi(num2_string);
#else
// Json
Json::Value root;
Json::Reader reader;
reader.parse(in, root);
num1 = root["num1"].asInt();
num2 = root["num2"].asInt();
op = root["op"].asInt();
#endif
return true;
}
public:
Request() {}
Request(int _num1, int _num2, char _op) : num1(_num1), num2(_num2), op(_op) {}
int num1;
int num2;
char op;
};
class Response {
public:
Response() {}
Response(int _exitcode, int _result) : exitcode(_exitcode), result(_result) {}
void serialize(string* out) {
#ifdef MySelf
string exitcode_string = to_string(exitcode);
string result_string = to_string(result);
*out = exitcode_string + SEP + result_string;
#else
// Json
Json::Value root;
root["exitcode"] = exitcode;
root["result"] = result;
Json::StyledWriter writer;
*out = writer.write(root);
#endif
}
bool deserialize(string& in) {
#ifdef MySelf
int pos = in.find(SEP);
if (pos == string::npos)
return false;
string exitcode_string = in.substr(0, pos);
string result_string = in.substr(pos + SEP_LEN, in.size());
if (result_string.empty() || exitcode_string.empty())
return false;
exitcode = stoi(exitcode_string);
result = stoi(result_string);
#else
// Json
Json::Value root;
Json::Reader reader;
reader.parse(in, root);
exitcode = root["exitcode"].asInt();
result = root["result"].asInt();
#endif
return true;
}
int exitcode; // 0表示成功,!0表示出错
int result;
};
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
163
164
165
166
167
168
169
170
171
172
173
174
175
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
163
164
165
166
167
168
169
170
171
172
173
174
175
代码中完整报文:"Content-Length\r\nRequest/Response-Body\r\n",可以序列化和反序列化,只用于简单来简单地解协议。
上次更新: 2025/11/11, 22:03:54