网络编程-TCP粘包

关于TCP

首先TCP和UDP一样属于传输层的协议,特点是面向连接、传输安全、基于流式传输协议。由于传输是基于流,所以发送方和接收方每次处理的数据量可以不一样,处理的频率也可以不一样,这不会影响数据的传输。

粘包问题

考虑一个需求如下:
有一个客户端和一个服务端,它们通过TCP套接字进行连接,客户端将数据发送给服务端,服务端来接收数据并进行解析。由前面所述,由于tcp是基于流的协议,因此,对于服务端在接收数据的时候,可能会存在以下几种情况:

  1. 最理想情况,Server一次收到一个完整的数据包
  2. 一次收到多个数据包
  3. 一次收到多个数据包 + 下个包的部分数据
  4. 一次收到半个数据包

上述现象就是TCP粘包问题,它并不是TCP的问题,而是我们程序员的问题,需要我们在使用的方式上进行一些约束定义,保证数据能够正确解析

  1. 使用标准的应用层协议(如http,https,websocket)来封装不定长的数据包,来避免粘包
  2. 使用特殊字符,如包头包尾使用特殊字符(效率低,需要对每个字节进行判断)
  3. 约定数据发送格式:数据头(存放数据体的大小,int类型,固定4个字节) + 数据体

处理粘包(方法3)

发送端处理方式

  1. 待发送数据长度N,动态申请一块内存,大小尾N+4(4是包头大小)
  2. 前4个字节写入数据的总长度(本地:小端序)(需要转换为网络字节序-大端序)
  3. 待发送数据使用memcpy到包头后边的地址空间中(不需要考虑字节序)
  4. 发送完整数据包,释放内存

客户端发送数据包,需要将一个数据包完全发送出去,因此在发送端需要一个发送一个完整数据包的函数,如下:

// 发送指定字节数
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;
}

// 发送数据包
// msg待发送数据,len待发送数据长度
int sendMessag(int fd, char* msg, int len){
	if(!msg || len <= 0 || fc <= 0) return -1;
	char* data = (char*)malloc(len + 4);
	int bigEndianLen = htonl(len);
	memcpy(data, &bigEndiaLen, 4);
	memcpy(data+4, msg, len);
	int ret = wrinten(fd, data, len + 4);
	free(data);
	return ret;
}

服务端处理方式

对应于客户端的处理方式,服务端也有类似的处理操作:

  1. 首先接收4个字节,获取数据体长度(考虑转字节序)
  2. 申请堆内存,存储接收数据
  3. 保存数据,释放内存

上述操作需要保证能接收到一个完整的数据包

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;
		if(len == 0) 
			return size - count; // socket close,return number bytes of read
		pt += len;
		count -=len;
	}
	return size;
}

// 接收带数据头的数据包
int recvPacket(int fd, char** msg)
{
	int len = 0;
	readn(fd, (char*)&len, 4);
	len = ntohl(len);
	// 这个地方特殊,+1 是为了在最后面存放 '\0'
	char* buf = (char*)malloc(len + 1);
	int ret = readn(fd, buf, len);
	if(ret != len){
		close(fd);
		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、付费专栏及课程。

余额充值