最简单得方法解决TCP分包粘包问题

文章介绍了TCP传输中可能出现的分包和粘包现象,指出这是由于程序员在处理数据边界时的疏忽导致。为了解决这个问题,文章提出了一个简单的解决方案:在发送数据前添加字符串长度作为包头,使得接收端能准确识别数据包的开始和结束,从而正确解析接收到的信息。这种方法确保了即使在缓冲区有大量数据的情况下,也能避免分包和粘包的问题。
摘要由CSDN通过智能技术生成

如何用最简单的方法解决TCP传输中的分包粘包问题?

在这里插入图片描述
首先需要说明一点,分包粘包等等一系列的问题并不是协议本身存在的问题,而是程序员在写代码的时候,没有搞清楚数据的边界导致的。

看个简单的例子,TCP客户端不断的向服务器发送字符串,每次发送完成随机睡眠一会。

char *buf[] = {
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaa",
    "bbbbbbbbbbb",
    "ccccccccccccccccccc",
    "dddddddddddddddddddddddddddddddddddddddddddd",
    "eeeeeeeeeeeeeeeeeeeeeeee",
    "ffffffff",
    "gggggggggggggggggggggggggggggggggggg",
    "hhhhhhhhhhhhhhhhhhhhhhhhhhhhh",
    "iii",
    "jjjjjjj",
    "kkkkkkkkkkkkkkkkkkkkkk"
};

srand(time(NULL));

for (int i = 0; i < sizeof(buf) / sizeof(buf[0]); i++)
{   
    if (send(sockfd, buf[i], strlen(buf[i]), 0) == -1)
    {
        perror("send");
        break;
    }
    usleep(1000 * 10);
}

服务器端接收数据的时候同样如此。

char buf[1024] = {0};

srand(time(NULL));

while (1) 
{   
    size = recv(fd, buf, sizeof(buf), 0); 
    if (size == -1) 
    {   
        perror("recv");
        break;
    }   
    else if (size == 0)
    {
        printf("客户端断开连接 ...\n");
        break;
    }

    printf("收到一条数据 %s\n", buf);

    bzero(buf, 1024);

    usleep(1000 * (rand() % 100 + 1));
}

我们希望看到的现象是,服务器端收到的数据和客户端一样。

运行程序,客户端发送完成,但是服务器端收到的数据却不是我们想要的。

root@Turbo:test# ./1.tcp-server 
等待客户端的连接 ...
接受客户端的连接 4
收到一条数据 aaaaaaaaaaaaaaaaaaaaaaaaaaaa
收到一条数据 bbbbbbbbbbbcccccccccccccccccccddddddddddddddddddddddddddddddddddd
dddddddddeeeeeeeeeeeeeeeeeeeeeeeeffffffffgggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiijjjjjjj收到一条数据 kkkkkkkkkkkkkkkkkkkkkk
客户端断开连接 ...
root@Turbo:test#

数据内容没有变,出现了多个字符串连接在一起的现象。

原因就是发送数据过快,或者接收数据太慢,导致TCP缓冲区中积累了很多数据,调用recv函数读数据的时候,就会一下子全部读出来。

想要解决这个问题,最简单的办法就是分清楚数据包的边界。发送字符串之前,在数据包的前面加上字符串的长度。

char *sendMsg = (char *)malloc(1024);
int len = 0;

srand(time(NULL));

for (int i = 0; i < sizeof(buf) / sizeof(buf[0]); i++)
{
    len = strlen(buf[i]);
    memcpy(sendMsg, &len, sizeof(int));
    memcpy(sendMsg + sizeof(int), buf[i], len);

    if (send(sockfd, sendMsg, strlen(buf[i]) + sizeof(int), 0) == -1)
    {
        perror("send");
        break;
    }
    memset(sendMsg, 0, 1024);

    usleep(1000 * 10);
}

接收数据的时候,先读取4个字节的整型数据,得到接下来字符串的长度,再读取对应长度的字符串。

char buf[1024] = {0};
ssize_t size;
int len = 0;

srand(time(NULL));

while (1) 
{   
    size = recv(fd, &len, sizeof(int), 0); 

    size = recv(fd, buf, len, 0); 
    if (size == -1) 
    {   
        perror("recv");
        break;
    }
    else if (size == 0)
    {
        printf("客户端断开连接 ...\n");
        break;
    }

    printf("收到一条数据 %s\n", buf);

    bzero(buf, 1024);

    usleep(1000 * (rand() % 100 + 1));
}

再次运行程序,不管睡眠时间怎么变化,服务器端收到的数据和客户端一样,也没有出现粘在一起的现象。

root@Turbo:test# ./1.tcp-server 
等待客户端的连接 ...
接受客户端的连接 4
收到一条数据 aaaaaaaaaaaaaaaaaaaaaaaaaaaa
收到一条数据 bbbbbbbbbbb
收到一条数据 ccccccccccccccccccc
收到一条数据 dddddddddddddddddddddddddddddddddddddddddddd
收到一条数据 eeeeeeeeeeeeeeeeeeeeeeee
收到一条数据 ffffffff
收到一条数据 gggggggggggggggggggggggggggggggggggg
收到一条数据 hhhhhhhhhhhhhhhhhhhhhhhhhhhhh
收到一条数据 iii
收到一条数据 jjjjjjj
收到一条数据 kkkkkkkkkkkkkkkkkkkkkk

方法很简单,也只是加了一个包头,其实目的就是为了告诉接收端,数据包从哪开始,到哪结束,这样就算缓冲区中有大量数据,也能分得清楚。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值