转帖:粘包、丢包及TCP信息收发

来源:互联网 发布:windows主辅域搭建 编辑:程序博客网 时间:2024/06/06 07:01
粘包、丢包及TCP信息收发 

转载自 http://blog.vckbase.com/arong/archive/2010/01/03/40672.html

初涉socket编程的朋友经常有下面一些疑惑:
1. 为什么我发了3次,另一端只收到2次?
2. 我每次发送都成功了,为什么对方收到的信息不完整?

这些疑惑往往是对send和recv这两个函数理解不准确所致。send和recv都提供了一个长度参数。对于send而言,这是你希望发送的字节数,而对于recv而言,则是希望收到的最大字节数。

1。 send

send函数的原型是:int send(SOCKET sd, const char * buffer, int len, int flag). 其中len指出buffer中包含的实际字节数,也是程序员希望发出的最大字节数。而这个函数的返回值是实际发送出去的字节数。在网络程序中,正常情况是这个返回值小于len,也就是说buffer中的内容没有完全被发送出去。

为了确保一个缓冲区内的内容被完全被发送出去,我们需要如下代码:

int res;
int pos = 0; //下一次发送需要开始的位置:
while(pos < len)
{
res = send(sd, buffer + pos , len - pos, 0);
if(res <=0) goto err_handler; //去错误处理
pos += res;
}

这样经过多次send,可以确保buffer内的内容都别发送出去。

为了避免发送线程被阻塞,应该考虑把上述代码放到一个子线程中,并通过队列来缓冲所有收发。

2. recv

recv的原型是:int recv(SOCKET sd, char * buffer, int len, int flag),其各个参数的含义同前面send。需要注意的是,系统并不会等待bufer被填满了再返回,而是一旦有数据被收到,就立刻返回。因此不要期望实际收到的数据长度就等于len。

你可以使用前面send的循环算法确保收到len个字节,也可以使用内容驱动的方法实现分段数据分析。不过这个就超出本文内容,也就不再赘述。

3. 粘包
所谓粘包,是指发送端发送的两个报文,在接收端被拼在一起。由于TCP是面向流的协议,报文与报文之间是没有分界符号的。在接收端,所有的数据都逻辑上拼在一起给你。举例来说,你分10次发送10个长度为10的报文,在接收端,你可能只收到一个长度为100的报文,而不会收到10个消息。

为了解决这个问题,你必须在接收端有能力把这些报文分隔开来。如果消息长度总是固定的,这就比较容易,只要按长度取出即可。如果长度不固定,一般有两种方法解决:

a)使用特征字节。例如:如果是聊天程序,发送的是普通文本,一些字符是绝对不可能出现在正文中的,你可以使用这些字符做分隔符隔离不同消息。我们可以使用'\0'做分隔,一般对于全文本传输是比较安全的。

b) 在发送方发送正文前,先发送一个长度。例如你要发送2345字节的内容,你可以先发送一个2字节的长度给对方,然后再发正文。接收放只要收到这个长度信息,就可以正确的分包。需要注意的,到底用多少字节来发送长度是应该预先约定的,一般2字节就足够,不过你约定4字节也是可以的。还要注意的是,如果接收一次报文后,解包完毕还剩下一部分内容,这些内容应该留给下次报文分包使用,而不能扔掉。

4. 丢包
丢包一般都是由于对上面说的理解不足引起的,因为TCP本身是确保不丢包的。

原创粉丝点击