《Linux内核视角:自定义协议与TCP的协同通信之道》

**前引:**在Linux占据核心地位的服务器与嵌入式领域,网络通信始终是技术架构的"主动脉"。TCP协议凭借其面向连接、可靠传输的特性,成为互联网通信的基石,但通用化的设计也使其在工业控制、金融交易、物联网等特定场景中,难以满足业务对协议轻量化、语义定制化的需求------这正是自定义协议的价值所在。自定义协议并非对TCP的替代,而是基于TCP字节流传输能力的"上层重构",通过封装业务专属的指令、数据格式与交互逻辑,实现通信效率与业务适配性的双重提升。本文将从Linux网络栈工作机制出发,厘清自定义协议与TCP的协同关系,既剖析底层数据传输的原理,又结合实战案例讲解协议设计、TCP连接管理、数据编解码等核心环节,为开发者提供一套从理论到落地的完整解决方案!

目录

【一】TCP协议-服务端

(1)网络套接字

(2)绑定IP和端口

(3)监听

(4)接收客户端请求

(5)读取客户端内容

【二】TCP协议-客户端

(1)网络套接字

(2)客户端发送请求

(3)读取服务端内容

(4)发送给服务端

【三】自定义协议

(1)自定义报文

(2)客户端序列化实现

(3)客户端反序列化实现

(4)服务端序列化实现

(5)服务端反序列化实现


【一】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);
    }
}
相关推荐
广东大榕树信息科技有限公司4 小时前
如何通过动环监控系统提升机房安全与管理效率?
运维·网络·物联网·国产动环监控系统·动环监控系统
qq13267029404 小时前
grafana 未授权访问漏洞设置iptables指定IP访问,拒绝其他所有IP
linux·服务器·网络·iptables·防火墙策略
while(1){yan}4 小时前
HTTP的数据报格式
java·开发语言·网络·网络协议·http·青少年编程·面试
白完就是肥4 小时前
QT编程之TCP编程
开发语言·qt·tcp/ip
极客范儿4 小时前
华为HCIP网络工程师认证—MAC地址与网络层
网络·华为
yenggd5 小时前
锐捷gre over ipsec结合ospf配置案例
运维·网络·笔记
Neolnfra5 小时前
跨站请求伪造攻击(CSRF)解析
服务器·网络·系统安全·网络攻击模型·安全威胁分析·csrf·安全架构
2301_807288635 小时前
MPRPC项目(第七天,rpcprovider分发rpc服务)
网络·分布式·rpc
橘颂TA5 小时前
【Linux】不允许你还不会——信号保存(3)
linux·服务器·网络·数据库