

**前引:**在Linux占据核心地位的服务器与嵌入式领域,网络通信始终是技术架构的"主动脉"。TCP协议凭借其面向连接、可靠传输的特性,成为互联网通信的基石,但通用化的设计也使其在工业控制、金融交易、物联网等特定场景中,难以满足业务对协议轻量化、语义定制化的需求------这正是自定义协议的价值所在。自定义协议并非对TCP的替代,而是基于TCP字节流传输能力的"上层重构",通过封装业务专属的指令、数据格式与交互逻辑,实现通信效率与业务适配性的双重提升。本文将从Linux网络栈工作机制出发,厘清自定义协议与TCP的协同关系,既剖析底层数据传输的原理,又结合实战案例讲解协议设计、TCP连接管理、数据编解码等核心环节,为开发者提供一套从理论到落地的完整解决方案!
目录
【一】TCP协议-服务端
如果要自定义协议后续肯定需要TCP的客户端和服务端的通信,所以我们先完成TCP通信接口封装
(1)网络套接字
cpp
//网络套接字
int Socket()
{
socket_ = socket(AF_INET,SOCK_STREAM,0);
if(fd==-1)
{
log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"错误码:%d,错误信息:%s",errno,strerror(errno));
exit(LOG_LEVEL_SOCKET);
}
else
{
log_message(LOG_LEVEL_SUCCESS,__FILE__,__LINE__,"socket is successful");
}
return socket_;
}
(2)绑定IP和端口
cpp
//绑定IP和端口
void Bind()
{
struct sockaddr_in ser_addr;
memset(&ser_addr,0,sizeof(ser_addr));
ser_addr.sin_family=AF_INET;
ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);
ser_addr.sin_port=htons(8080);
int bin =bind(socket_,(const sockaddr*)&ser_addr,sizeof(ser_addr));
if(bin==-1)
{
log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"错误码:%d,错误信息:%s",errno,strerror(errno));
exit(LOG_LEVEL_BIND);
}
else
{
log_message(LOG_LEVEL_SUCCESS,__FILE__,__LINE__,"bind is successful");
}
}
(3)监听
cpp
//监听
void Listen()
{
int lis =listen(socket_,max_listen);
if(lis==-1)
{
log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"错误码:%d,错误信息:%s",errno,strerror(errno));
exit(LOG_LEVEL_LISTEN);
}
else
{
log_message(LOG_LEVEL_SUCCESS,__FILE__,__LINE__,"listen is successful");
}
}
(4)接收客户端请求
cpp
//接收客户端请求
int Accept()
{
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
socklen_t sz = sizeof(addr);
new_socket_ =accept(socket_,(sockaddr*)&addr,&sz);
if(new_socket_==-1)
{
log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"错误码:%d,错误信息:%s",errno,strerror(errno));
exit(LOG_LEVEL_ACCEPT);
}
else
{
log_message(LOG_LEVEL_SUCCESS,__FILE__,__LINE__,"accept is successful");
}
return new_socket_;
}
(5)读取客户端内容
cpp
//读取客户端内容
std::string Recv(const int& new_token)
{
char buf_ip[50]={0};
//inet_ntop(AF_INET,&(addr.sin_addr.s_addr),buf_ip,sizeof(buf_ip));
char buffer[max_buffer]={0};
size_t t =recv(new_token, buffer, sizeof(buffer)-1, 0);
if(t>0)
{
log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"Recv is successful");
}
else if(t==0)
{
std::cout<<"对方关闭了连接"<<buf_ip<<std::endl;
}
else
{
std::cout<<"服务端读取错误"<<std::endl;
}
return buffer;
}
【二】TCP协议-客户端
(1)网络套接字
cpp
//网络套接字
int Socket()
{
socket_ = socket(AF_INET,SOCK_STREAM,0);
if(socket_==-1)
{
log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"错误码:%d,错误信息:%s",errno,strerror(errno));
exit(LOG_LEVEL_SOCKET);
}
else
{
log_message(LOG_LEVEL_SUCCESS,__FILE__,__LINE__,"socket is successful");
}
return socket_;
}
(2)客户端发送请求
cpp
//客户端发送请求
void Connect()
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family=AF_INET;
inet_pton(AF_INET, "120.48.75.223", &addr.sin_addr.s_addr);
addr.sin_port=htons(atoi("8080"));
socklen_t ip_addr=sizeof(addr);
int can = connect(socket_,(const sockaddr*)&addr,ip_addr);
if(can==-1)
{
log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"错误码:%d,错误信息:%s",errno,strerror(errno));
exit(LOG_LEVEL_CONNECT);
}
}
(3)读取服务端内容
cpp
//读取服务端端内容
std::string Recv()
{
char buf_ip[50]={0};
//inet_ntop(AF_INET,&(addr.sin_addr.s_addr),buf_ip,sizeof(buf_ip));
char buffer[max_buffer]={0};
size_t t =recv(socket_, buffer, sizeof(buffer)-1, 0);
if(t>0)
{
log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"Recv is successful");
}
else if(t==0)
{
std::cout<<"对方关闭了连接"<<buf_ip<<std::endl;
}
else
{
std::cout<<"客户端读取错误"<<std::endl;
}
return buffer;
}
(4)发送给服务端
cpp
//发送给服务端
void Send(const std::string& se)
{
size_t sz = send(socket_,se.c_str(),se.size(),0);
if(sz>0)
{
log_message(LOG_LEVEL_ERROR,__FILE__,__LINE__,"Send is successful");
}
}
【三】自定义协议
(1)自定义报文
协议:一种约定,只有遵守这个协议,双方才能正常交流。那么以"计算器"方案为例,定制协议:
我们知道报文=报头+有效载荷。那么客户端和服务端在发送、接收前后都需要处理拿到的报文,我们称发送前的处理和发送后的解析为:序列化和反序列化。假设我现在要模拟计算器,个性化使用如下的报文设计:


**客户端序列化需要:**将用户输入的"左操作数"、"运算符"、"右操作数"拼接成目标报文
**客户端反序列化需要:**解析服务端发送的报文,拿到结果
**服务端序列化需要:**将客户端发送的报文解析拿到"左操作数"、"运算符"、"右操作数"
**服务端反序列化需要:**将处理好的结果打包成报文发给客户端
注意:客户端或者服务端拿到数据需要判断该条报文的完整性,比如定长报文、标识符
客户端类里面设计了如下变量:2个操作数、1个操作符、计算结果result、准确性believe_

(2)客户端序列化实现
cpp
//客户端序列化
std::string Orderly()
{
//拼接有效载荷
std::string load(std::to_string(left_));
load+=Spe_char;
load+=oper_;
load+=Spe_char;
load+=std::to_string(right_);
load+=New_char;
//拼接报头
std::string head(std::to_string(load.size()));
head+=New_char;
//拼接报文
return std::string(head+load);
}
(3)客户端反序列化实现
cpp
//客户端反序列化
int Unorderly(const std::string& s)
{
//首先找到结果位置
std::size_t tom = s.rfind(Spe_char);
if(tom != std::string::npos)
{
//获取计算结果正确性
std::string bel = s.substr(tom+1,1);
believe_ = atoi(bel.c_str());
if(believe_ != 0)
{
//获取运算结果
std::string res = s.substr(0,tom);
result_ = atoi(res.c_str());
return result_;
}
}
else
{
std::cout<<"报文错误"<<std::endl;
}
return 0;
}
服务端类里面设计了如下变量:2个操作数、1个操作符、计算结果result、准确性believe_

(4)服务端序列化实现
cpp
//服务端序列化
void Orderly(const std::string& s)
{
//获取左操作数
std::size_t sz =s.find(Spe_char);
if(sz != std::string::npos)
{
std::string le = s.substr(0,sz);
left_ = atoi(le.c_str());
}
else
{
std::cout<<"左操作数获取失败"<<std::endl;
return;
}
//获取操作符
std::string op_str = s.substr(sz+1,1);
oper_ = op_str[0];
//获取右操作数
std::size_t r_pos =s.rfind(New_char);
std::size_t l_pos=s.rfind(Spe_char);
if(l_pos != std::string::npos && r_pos != std::string::npos)
{
std::string right_str = s.substr(l_pos+1,r_pos-(l_pos+1));
right_ = atoi(right_str.c_str());
}
else
{
std::cout<<"右操作数获取失败"<<std::endl;
}
}
(5)服务端反序列化实现
cpp
//服务端反序列化
std::string Unorderly()
{
std::pair<bool,int> pair= calculate(left_,right_,oper_);
if(pair.first==false)
{
return "";
}
believe_ = pair.first;
std::string message= std::to_string(pair.second);
message+=Spe_char;
message+=std::to_string(believe_);
message+=New_char;
return message;
}
计算函数:
cpp
//获取计算结果
std::pair<bool,int> calculate(int left,int right,char oper)
{
switch(oper)
{
case '+':return std::make_pair(true,left+right);
case '-':return std::make_pair(true,left-right);
case '*':return std::make_pair(true,left*right);
case '/':
{
if(right==0)
{
return std::make_pair(false,0);
}
else
{
return std::make_pair(true,left/right);
}
}
case '%': return std::make_pair(true,left%right);
default: return std::make_pair(false, 0);
}
}
