TCP 数据传输粘包的处理

使用 TCP 进行套接字通信,数据交互时多个数据包粘连到一起无法拆分,这种情况一般是我们的需求过于复杂造成的,是程序猿的问题而不是协议的问题,TCP 协议表示这锅它不想背。

解决方案:

1.使用标准的应用层协议(比如:http、https)来封装要传输的不定长的数据包

2.在每条数据的尾部添加特殊字符,如果遇到特殊字符,代表当条数据接收完毕了
有缺陷:效率低,需要一个字节一个字节接收,接收一个字节判断一次,判断是不是那个特殊字符串

3.通信数据封包处理,在发送数据块之前,在数据块最前边添加一个固定大小的数据头,这时候数据由两部分组成:数据头 + 数据块
数据头:存储当前数据包的总字节数,接收端先接收数据头,然后在根据数据头接收对应大小的字节
数据块:当前数据包的内容

通信数据封包

在这里插入图片描述
发送的数据包粘连到一起导致接收端无法解析,我们通常使用添加包头的方式轻松地解决掉这个问题。

发送端

在这里插入图片描述

数据发送函数

/*
 * @description	: 发送指定的字节数
 * @param - fd  :通信的文件描述符(套接字)
 * @param - msg :待发送的原始数据
 * @param - size: 待发送的原始数据的总字节数
 * @return 		: 函数调用成功返回实际发送的字节数;失败则返回-1;
 */	
int writen(int fd, const char* msg, int size)
{
    const char* buf = msg;
    int count = size;
    while (count > 0)
    {
        int len = send(fd, buf, count, 0);
        if (len == -1)
        {
            close(fd);
            return -1;
        }
        else if (len == 0)
        {
            continue;
        }
        buf += len;
        count -= len;
    }
    return size;
}

数据包发送函数:

/*
 * @description	: 发送带有数据头的数据包
 * @param - fd  :通信的文件描述符(套接字)
 * @param - msg :待发送的原始数据
 * @param - size: 待发送的原始数据的总字节数
 * @return 		: 函数调用成功返回实际发送的字节数;失败则返回-1;
 */	
int sendMsg(int cfd, char* msg, int len)
{
   if(msg == NULL || len <= 0 || cfd <=0)
   {
       return -1;
   }
   // 申请内存空间: 数据长度 + 包头4字节(存储数据长度)
   char* data = (char*)malloc(len+4);
   int bigLen = htonl(len);
   memcpy(data, &bigLen, 4);
   memcpy(data+4, msg, len);
   // 发送数据
   int ret = writen(cfd, data, len+4);
   // 释放内存
   free(data);
   return ret;
}

接收端:

在这里插入图片描述

数据包接收函数

/*
 * @description	: 接收指定的字节数
 * @param - fd  :通信的文件描述符(套接字)
 * @param - buf :存储待接收数据的内存的起始地址
 * @param - size: 指定要接收的字节数
 * @return 		: 函数调用成功返回实际接收的字节数;失败则返回-1;
 */	
int readn(int fd, char* buf, int size)
{
    char* pt = buf;
    int count = size;
    while (count > 0)
    {
        int len = recv(fd, pt, count, 0);
        if (len == -1)
        {
            return -1;
        }
        else if (len == 0)
        {
            return size - count;
        }
        pt += len;
        count -= len;
    }
    return size;
}

数据接收函数

/*
 * @description	: 接收带数据头的数据包
 * @param - cfd :通信的文件描述符(套接字)
 * @param - msg :一级指针的地址,函数内部会给这个指针分配内存,用于存储待接收的数据,这块内存需要使用者释放
 * @return 		: 函数调用成功返回实际接收的字节数;失败则返回-1;
 */	
int recvMsg(int cfd, char** msg)
{
    // 接收数据
    // 1. 读数据头
    int len = 0;
    readn(cfd, (char*)&len, 4);
    len = ntohl(len); //(本地->网络) 转IP
    printf("数据块大小: %d\n", len);

    // 根据读出的长度分配内存,+1 -> 这个字节存储\0
    char *buf = (char*)malloc(len+1);
    int ret = readn(cfd, buf, len);
    if(ret != len)
    {
        close(cfd);
        free(buf);
        return -1;
    }
    buf[len] = '\0';
    *msg = buf;

    return ret;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值