Socket实现模拟TCP通信粘包问题

本文探讨了计算机网络中的粘包现象,尤其是在TCP协议下,分析了粘包的常见原因,如缓冲区限制、延迟ACK和Nagle算法,并提供了如使用消息长度前缀、消息边界等策略来避免和解决粘包问题。同时给出了简单的服务器和客户端示例代码。
摘要由CSDN通过智能技术生成

😎 作者介绍:我是程序员行者孙,一个热爱分享技术的制能工人。计算机本硕,人工制能研究生。公众号:AI Sun,视频号:AI-行者Sun
🎈 本文专栏:本文收录于《网络编程实战》系列专栏,相信一份耕耘一份收获,我会手把手教分享网络编程实战内容,届时可以拳打字节,脚踢腾讯
🤓 欢迎大家关注其他专栏,我将分享Web前后端开发、人工智能、机器学习、深度学习从0到1系列文章。
🖥 随时欢迎您跟我沟通,一起交流,一起成长、进步!

在计算机网络通信中,粘包是指在传输过程中,发送方发送的多个小数据包被接收方粘合在一起,形成一个大的数据包。这种现象通常出现在使用流式传输协议(如TCP)进行数据传输的情况下。
具体来说,TCP是一种面向连接的协议,它通过将数据划分为小的数据块(通常称为段)进行传输。发送方将这些数据块发送到网络,而接收方则负责将它们重新组装成原始的数据。然而,由于网络的不确定性和各种因素,接收方有时会在处理数据时将多个小数据块合并成一个大的数据块,从而产生粘包现象。

 造成粘包的原因可能是多方面的,其中一些常见的因素包括:

  1. 缓冲区大小限制: 接收方的缓冲区有限,可能无法及时处理到达的多个小数据块,导致合并成一个大的数据块。
  2. 延迟 ACK: 接收方可能延迟发送应答信号(ACK),使得发送方继续发送数据,导致数据合并。
  3. Nagle算法: Nagle算法会将小的数据块合并成更大的数据块一起发送,以提高网络利用率,但这也可能引发粘包问题。

为了解决粘包问题,通常采取以下策略:

  • 消息长度前缀: 在每个数据包前添加消息长度信息,接收方通过这个信息来准确地拆分数据包。
  • 消息边界: 使用特定的消息边界符或标记来标识数据包的边界。
  • 固定长度的消息: 确定一个固定的消息长度,确保每个数据包都符合这个长度。
  • 使用应用层协议: 利用应用层协议进行消息的封装和解析,如使用JSON或XML格式。

这些方法有助于在数据传输过程中有效地处理粘包问题。选择哪种方法取决于具体的应用场景和需求。

分包解决粘包问题------------------------下一篇博客

解决TCP粘包问题-CSDN博客

完整代码:

server:

#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>

int main(int, char**){
    //1.创建套接字
    int listen_sock=socket(AF_INET,SOCK_STREAM,0);

    if (listen_sock==-1)
    {
        std::cerr<<"Fiald to create socket"<<std::endl;
        return 1;
    }
    

    //2.绑定IP地址
    struct sockaddr_in server_addr;
    memset(&server_addr,0,sizeof(server_addr));

    server_addr.sin_addr.s_addr=INADDR_ANY;
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(9999);

    if(bind(listen_sock,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1)
    {
        std::cerr<<"Fiald to bind socket"<<std::endl;
        return 1;
    }
    //3.监听套接字

    if (listen(listen_sock,5)==-1)
    {
        std::cerr<<"Fiald to listen socket"<<std::endl;
        return 1;
    }
    
    std::cout<<"server is listening"<<std::endl;

    //4.接受客户端的连接

    struct sockaddr_in client_addr;
    socklen_t client_addr_len=sizeof(client_addr);

    int client_sock=accept(listen_sock,(struct sockaddr*)&client_addr,&client_addr_len);

    if (client_sock==-1)
    {
        std::cerr<<"Fiald to accept socket"<<std::endl;
        return 1;
    }
    std::cout<<"a client connected"<<std::endl;

    //5.数据交互
    
    //接受消息
    while (1)
    {
        char buffer[1024];
        int read_size=read(client_sock,buffer,sizeof(buffer));
    
        if (read_size<0) 
        {
            std::cerr<<"Fiald to read"<<std::endl;
            close(client_sock);
            close(listen_sock);
            exit(0);
        }
        std::cout<<"Received msg :"<<buffer<<std::endl;
    }
    // std::cout<<"Received to client :"<<buffer<<std::endl;
    // std::string res_msg="Hello Client!";
    // int wr=write(client_sock,res_msg.c_str(),res_msg.length());

    // if (wr==-1)
    // {
    //     std::cerr<<"Fiald to write"<<std::endl;
    //     return 1;
    // }
    
    close(client_sock);
    close(listen_sock);
}

client:

#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>

int main(){

    //1.创建socket
    int client_sock=socket(AF_INET,SOCK_STREAM,0);
    if (client_sock==-1)
    {
        std::cerr<<"Faild to create socket"<<std::endl;
        return -1;
    }

    //2.连接服务器

    struct sockaddr_in server_addr;
    server_addr.sin_family=AF_INET;
    //server_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    inet_pton(AF_INET,"127.0.0.1",&server_addr.sin_addr.s_addr);
    server_addr.sin_port=htons(9999);

    if(connect(client_sock,(struct sockaddr*)&server_addr,sizeof(server_addr))==-1){
        std::cerr<<"Faild to connect socket"<<std::endl;
        return -1;
    }

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

    //3.数据交互

    //发送消息
    
    std::string msg1="hi";
    std::string msg2="jack";

    if(write(client_sock,msg1.c_str(),msg1.length())==-1){
        std::cerr<<"Faild to write "<<std::endl;
        return -1;
    }

    if(write(client_sock,msg2.c_str(),msg2.length())==-1){
        std::cerr<<"Faild to write "<<std::endl;
        return -1;
    }

    //接受消息

    // char buffer[1024];
    // if(read(client_sock,buffer,sizeof(buffer))==-1){
    //     std::cerr<<"Faild to read"<<std::endl;
    //     return -1;
    // }

    // printf("Receive to server :%s",buffer);

    close(client_sock);
    

}
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员行者孙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值