使用 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;
}