• 欢迎浏览“String me = Creater\忠实的资深Linux玩家;”,请文明浏览,理性发言,有侵犯你的权益请邮件我(creater@vip.qq.com).
  • 把任何的失败都当作一次尝试,不要自卑;把所有的成功都想成是一种幸运,不要自傲。
  •    3年前 (2015-12-17)  网络 |   抢沙发  24 
    文章评分 0 次,平均分 0.0

    TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。即面向流的通信是无消息保护边界的。

    UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
    由于TCP无消息保护边界, 需要在消息接收端处理消息边界问题。也就是为什么我们以前使用UDP没有此问题。反而使用TCP后,出现少包的现象。

    粘包的分析
    上面说了原理,但可能有人使用TCP通信会出现多包/少包,而一些人不会。那么我们具体分析一下,少包,多包的情况。
    正常情况,发送及时每消息发送,接收也不繁忙,及时处理掉消息。像UDP一样.
    TCP粘包、多包和少包, 断包
    发送粘包,多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包. 这种情况和客户端处理繁忙,接收缓存区积压,用户一次从接收缓存区多个数据包的接收端处理一样。
    TCP粘包、多包和少包, 断包
    发送粘包或接收缓存区积压,但用户缓冲区大于接收缓存区数据包总大小。此时需要考虑处理一次处理多数据包的情况,但每个数据包都是完整的。TCP粘包、多包和少包, 断包
    发送粘包或接收缓存区积压, 用户缓存区不是数据包大小的整数倍。此时需要考虑处理一次处理多数据包的情况,同时也需要考虑数据包不完整。
    TCP粘包、多包和少包, 断包

    两种情况下会发生粘包:
    1.发送端需要等缓冲区满才发送出去,造成粘包;
    2.接收方不及时接收缓冲区的包,造成多个包接收。

    不是所有的粘包都需要处理。 我们先列举一下:
    1.连续的数据流不需要处理。如一个在线视频,它是一个连续不断的流, 不需要考虑分包。
    2.每发一个消息,建一次连接的情况。
    3.发送端使用了TCP强制数据立即传送的操作指令push。
    4.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文不好的话, 可参考http://freemart.iteye.com/blog/836654。 它在判断包是否完整时,有个小缺陷,它没使用IOBuffer的prefixedDataAvailable。但注释写的比较好。
    把官网上的代码,也在这展示一下。

    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,则表明数据流内容尚不够构成一完整结构数据,需留待与下一包数据合并后再行处理。
    参考
    http://blog.csdn.net/binghuazh/article/details/4222516
    http://www.cnblogs.com/alon/archive/2009/04/16/1437600.html
    http://hi.baidu.com/chongerfeia/blog/item/b1e572f631dd7e28bd310965.html
    http://freemart.iteye.com/blog/836654
    http://blianchen.blog.163.com/blog/static/1310562992010101891522100/
    http://mina.apache.org/chapter-11-codec-filter.html

     

    除特别注明外,本站所有文章均为String me = "Creater\忠实的资深Linux玩家";原创,转载请注明出处来自http://unix8.net/home.php/4416.html

    关于

    发表评论

    暂无评论

    切换注册

    登录

    忘记密码 ?

    切换登录

    注册

    扫一扫二维码分享