Socket/TCP粘包、多包和少包, 断包
2015-09-11 23:32
441 查看
原文转载自:http://zgc168.iteye.com/blog/1880620,感谢原作者。
为什么TCP 会粘包
前几天,调试mina的TCP通信, 第一个协议包解析正常,第二个数据包不完整。为什么会这样吗,我们用mina这样通信框架,还会出现这种问题?带者问题,我们先分析一下问题。
提到通信, 我们面临都通信协议,数据协议的选择。 通信协议我们可选择TCP/UDP:
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。即面向流的通信是无消息保护边界的。
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
由于TCP无消息保护边界, 需要在消息接收端处理消息边界问题。也就是为什么我们以前使用UDP没有此问题。反而使用TCP后,出现少包的现象。
粘包的分析
上面说了原理,但可能有人使用TCP通信会出现多包/少包,而一些人不会。那么我们具体分析一下,少包,多包的情况。
正常情况,发送及时每消息发送,接收也不繁忙,及时处理掉消息。像UDP一样.
发送粘包,多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包. 这种情况和客户端处理繁忙,接收缓存区积压,用户一次从接收缓存区多个数据包的接收端处理一样。
发送粘包或接收缓存区积压,但用户缓冲区大于接收缓存区数据包总大小。此时需要考虑处理一次处理多数据包的情况,但每个数据包都是完整的。
发送粘包或接收缓存区积压, 用户缓存区是数据包大小的整数倍。此时需要考虑处理一次处理多数据包的情况,但每个数据包都是完整的。
发送粘包或接收缓存区积压, 用户缓存区不是数据包大小的整数倍。此时需要考虑处理一次处理多数据包的情况,同时也需要考虑数据包不完整。
我们的情况就属于最后一种,发生了数据包不完整的情况。
啰嗦了这么多,总结 一下, 就两种情况下会发生粘包。
发送端需要等缓冲区满才发送出去,造成粘包
接收方不及时接收缓冲区的包,造成多个包接收
如何应对
先卖个关子, 不是所有的粘包都需要处理。 我们先列举一下,免得在编码过程中,因为知道了粘包的情况下,都处理粘包。
连续的数据流不需要处理。如一个在线视频,它是一个连续不断的流, 不需要考虑分包。
每发一个消息,建一次连接的情况。
发送端使用了TCP强制数据立即传送的操作指令push。
UDP, 前面已说明白了。在这在强调一下,UDP不需要处理,免的忘记了。
如果用socket编写编程的话, 我就不多说我, 可参考下面的资料:
Grizzly: http://grizzly.java.net/nonav/docs/docbkx2.0/html/coreframework-samples.html User Guide 第二章的样例:解析收到的消息。
xSocket:http://xsocket.sourceforge.net/core/tutorial/V2/TutorialCore.htm 第 18 节。
Netty: http://netty.io/docs/3.2.6.Final/api/org/jboss/netty/handler/codec/frame/FrameDecoder.html FrameDecoder 的API 文档。Netty 抽象了一个“消息桢解码器”的类来处理这些。
Mina 2:http://mina.apache.org/chapter-11-codec-filter.html
Mina 2:如果En文不好的话, 可参考/content/2980231.html。 它在判断包是否完整时,有个小缺陷,它没使用IOBuffer的prefixedDataAvailable。但注释写的比较好。
把官网上的代码,也在这展示一下。
Java代码
public class ImageResponseDecoder extends CumulativeProtocolDecoder {
/**
* 返回值的解释:
* 1、false, 继续接收下一批数据,有两种情形,如缓冲区数据刚刚就是一个完整消息,或不够一条消息时。如果不够一条消息,那么会将下一批数据和剩余消息进行合并
* 2、true, 当缓冲区的消息多于一条消息时,剩余消息会再会推送至doDecode
*/
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)throws Exception {
//发送数据时,头四个字节记录了消息的长度。 此方法会读四个字节,并和实现流长度对比。返回前,将流reset.
if (in.prefixedDataAvailable(4)) {
int length = in.getInt();
byte [] bytes =newbyte[length];
in.get(bytes);
ByteArrayInputStream bais =new ByteArrayInputStream(bytes);
BufferedImage image = ImageIO.read(bais);
out.write(image);
return true;//如果读取内容后还粘了包,系统会自动处理。
}else{
returnfalse;//继续接收数据,以待数据完整
}
}
}
再总结一下处理流程: 就发送数据时,包开始写入消息长度n, 当接收到的缓存区数据m,各处理流程如下:
1)若n<m,则表明数据流包含多包数据,从其头部截取n个字节存入临时缓冲区,剩余部分数据依此继续循环处理,直至结束。或n>m
2)若n=m,则表明数据流内容恰好是一完整结构数据,直接将其存入临时缓冲区即可。
3)若n >m,则表明数据流内容尚不够构成一完整结构数据,需留待与下一包数据合并后再行处理。
参考
/article/9406433.html
/article/6449810.html
http://hi.baidu.com/chongerfeia/blog/item/b1e572f631dd7e28bd310965.html
/content/2980231.html
http://blianchen.blog.163.com/blog/static/1310562992010101891522100/
http://mina.apache.org/chapter-11-codec-filter.html
为什么TCP 会粘包
前几天,调试mina的TCP通信, 第一个协议包解析正常,第二个数据包不完整。为什么会这样吗,我们用mina这样通信框架,还会出现这种问题?带者问题,我们先分析一下问题。
提到通信, 我们面临都通信协议,数据协议的选择。 通信协议我们可选择TCP/UDP:
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。即面向流的通信是无消息保护边界的。
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
由于TCP无消息保护边界, 需要在消息接收端处理消息边界问题。也就是为什么我们以前使用UDP没有此问题。反而使用TCP后,出现少包的现象。
粘包的分析
上面说了原理,但可能有人使用TCP通信会出现多包/少包,而一些人不会。那么我们具体分析一下,少包,多包的情况。
正常情况,发送及时每消息发送,接收也不繁忙,及时处理掉消息。像UDP一样.
发送粘包,多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包. 这种情况和客户端处理繁忙,接收缓存区积压,用户一次从接收缓存区多个数据包的接收端处理一样。
发送粘包或接收缓存区积压,但用户缓冲区大于接收缓存区数据包总大小。此时需要考虑处理一次处理多数据包的情况,但每个数据包都是完整的。
发送粘包或接收缓存区积压, 用户缓存区是数据包大小的整数倍。此时需要考虑处理一次处理多数据包的情况,但每个数据包都是完整的。
发送粘包或接收缓存区积压, 用户缓存区不是数据包大小的整数倍。此时需要考虑处理一次处理多数据包的情况,同时也需要考虑数据包不完整。
我们的情况就属于最后一种,发生了数据包不完整的情况。
啰嗦了这么多,总结 一下, 就两种情况下会发生粘包。
发送端需要等缓冲区满才发送出去,造成粘包
接收方不及时接收缓冲区的包,造成多个包接收
如何应对
先卖个关子, 不是所有的粘包都需要处理。 我们先列举一下,免得在编码过程中,因为知道了粘包的情况下,都处理粘包。
连续的数据流不需要处理。如一个在线视频,它是一个连续不断的流, 不需要考虑分包。
每发一个消息,建一次连接的情况。
发送端使用了TCP强制数据立即传送的操作指令push。
UDP, 前面已说明白了。在这在强调一下,UDP不需要处理,免的忘记了。
如果用socket编写编程的话, 我就不多说我, 可参考下面的资料:
Grizzly: http://grizzly.java.net/nonav/docs/docbkx2.0/html/coreframework-samples.html User Guide 第二章的样例:解析收到的消息。
xSocket:http://xsocket.sourceforge.net/core/tutorial/V2/TutorialCore.htm 第 18 节。
Netty: http://netty.io/docs/3.2.6.Final/api/org/jboss/netty/handler/codec/frame/FrameDecoder.html FrameDecoder 的API 文档。Netty 抽象了一个“消息桢解码器”的类来处理这些。
Mina 2:http://mina.apache.org/chapter-11-codec-filter.html
Mina 2:如果En文不好的话, 可参考/content/2980231.html。 它在判断包是否完整时,有个小缺陷,它没使用IOBuffer的prefixedDataAvailable。但注释写的比较好。
把官网上的代码,也在这展示一下。
Java代码
public class ImageResponseDecoder extends CumulativeProtocolDecoder {
/**
* 返回值的解释:
* 1、false, 继续接收下一批数据,有两种情形,如缓冲区数据刚刚就是一个完整消息,或不够一条消息时。如果不够一条消息,那么会将下一批数据和剩余消息进行合并
* 2、true, 当缓冲区的消息多于一条消息时,剩余消息会再会推送至doDecode
*/
protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)throws Exception {
//发送数据时,头四个字节记录了消息的长度。 此方法会读四个字节,并和实现流长度对比。返回前,将流reset.
if (in.prefixedDataAvailable(4)) {
int length = in.getInt();
byte [] bytes =newbyte[length];
in.get(bytes);
ByteArrayInputStream bais =new ByteArrayInputStream(bytes);
BufferedImage image = ImageIO.read(bais);
out.write(image);
return true;//如果读取内容后还粘了包,系统会自动处理。
}else{
returnfalse;//继续接收数据,以待数据完整
}
}
}
再总结一下处理流程: 就发送数据时,包开始写入消息长度n, 当接收到的缓存区数据m,各处理流程如下:
1)若n<m,则表明数据流包含多包数据,从其头部截取n个字节存入临时缓冲区,剩余部分数据依此继续循环处理,直至结束。或n>m
2)若n=m,则表明数据流内容恰好是一完整结构数据,直接将其存入临时缓冲区即可。
3)若n >m,则表明数据流内容尚不够构成一完整结构数据,需留待与下一包数据合并后再行处理。
参考
/article/9406433.html
/article/6449810.html
http://hi.baidu.com/chongerfeia/blog/item/b1e572f631dd7e28bd310965.html
/content/2980231.html
http://blianchen.blog.163.com/blog/static/1310562992010101891522100/
http://mina.apache.org/chapter-11-codec-filter.html
相关文章推荐
- 黑马程序员----网络编程
- 黑马程序员——TCP协议传输(下)
- HTTPS那些事(一)HTTPS原理
- Linux基本网络知识
- Linux内核工程导论——网络:内核特性
- Linux内核工程导论——网络:Socket
- 使用dup2重定向了标准输出后,使用什么方法恢复对终端的输出?? http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=359433&page=
- 黑马程序员——TCP协议传输(上)
- dup() 和 dup2() http://blog.163.com/crosslandy@126/blog/static/12798713420119710598844/
- 我的openwrt学习笔记(二十九)webserver之 thttpd
- 1.Linux链接概念 http://www.cnblogs.com/itech/archive/2009/04/10/1433052.html
- fstat、stat和lstat 区别(转) http://www.cnitblog.com/guopingleee/archive/2008/11/13/51411.aspx
- 我的openwrt学习笔记(二十八)webserver之lighttpd
- 腾迅股票数据接口 http/javascript
- android 开发--抓取网页解析网页内容的若干方法(网络爬虫)(正则表达式)
- 网络协议
- 达到HTTP合约Get、Post和文件上传功能——采用WinHttp介面
- Android网络连接---WebView
- 安卓之网络连接
- Android网络编程随想录(2)