Socket编程 Tcp和粘包

大多数程序员都要接触网络编程,Web开发天天和http打交道。稍微底层一点的程序员,就是TCP/UDP 。

对程序员来说,Tcp/udp的核心是Socket编程。

我的浅薄的观点---------理解socket tcp编程除了基础知识外,1是异步IO模型,2是粘包。 

今天讨论下粘包。  便于理解用同步接口来实现  。本文侧重于代码来探讨和解决问题。理论可以看其他博客

先来思考一个问题:如下代码,发送端每次发送一个固定字符串 have connected to you!   。那么设想一下,接收端接收到的数据是咋样的?

是否每一次接受,收到的都是一段 have connected to you!          似乎,应该是吧??    

答案是否定的!!! 下面是输出的结果

本次接受:have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!
本次接受:have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!
本次接受:have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!
本次接受:have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you!have connected to you

  1. 思考为什么一次接受,会有这么多的have connected to you!          ? 
  2. 这会对程序造成什么影响?
  3. 如何解决这种问题?

上面3个问题明白了,基本上粘包也就明白了

第一个问题为什么一次接受会有这么多的have connected to you! 

  我也一度以为接收一次,就是一个have connected to you!    上学那会尽管常常用到Socket 来建立tcp 连接,七层模型和三次握手背的有模有样,但是我确实不知道Tcp粘包。

  因为我对传输的要求比较低,常常是过一段时间(比如好几秒钟)才发送一次这样的场景。这种情况下不大会出现粘包。(可以试下再send 下面Sleep(1000))

  此时的场景 大概是这样的。  客户端服务Send  -----服务端Recv   

  过一段时间                            客户端再 Send ----服务端Recv   如此循环

  这样的话,服务端每一次接收,刚好能接收到 上一次客户端发过来的全部数据。

  这里我们发送的间隔非常短,每一次Send 其实系统只是把要发送的数据拷贝到发送缓冲区,系统处理发送缓冲区时可能会把我们的多次发送,合并为一次发送 。即Send(1),Send(2),Send(3)  最终一次发送Send(123).

  而接收端就直接一次就接收到了   Recv(123) .

第二个问题这会对程序造成什么影响?

  服务端该如何解析这个123那?是算一次指令,内容是123  ? 还是算两次指令,第一次是12,第二次是3?

  事实上服务端无法判断接受到数据据该解析为 1-2-3 还是 12-3 还是 1-23 还是123.

  如果你正在打某网络游戏,某秒钟你产生了20次操作指令到服务端。按目前的情况,服务端是无法正确还原出这20次指令的具体的内容的

第三个问题3如何解决 。

  下面拷贝了一块他人博客总结的解决方案

  1. 发送定长包。如果每个消息的大小都是一样的,那么在接收对等方只要累计接收数据,直到数据等于一个定长的数值就将它作为一个消息。
  2. 包尾加上 标记。FTP协议正是这么做的。但问题在于如果数据正文中也含有 ,则会误判为消息的边界。
  3. 包头加上包体长度。包头是定长的4个字节,说明了包体的长度。接收对等方先接收包体长度,依据包体长度来接收包体。
  4. 使用更加复杂的应用层协议。

来实践一下 2,搞明白了2以后,3,4其实也是类似的 。

对发送端来说,改动是很小的。服务端则需要做一些调整 。先约定每次发送,包尾加上 @#$%^&  作为结束

 执行结果如下。

可以看到,只要给每次发送的消息加上一个约定的尾巴。接收端就可以根据这个尾巴,把每次发送的消息给分解出来!!

UDP有没有这个问题那? 

因为UDP不是面向流的,每一次都有单独的数据报。表现在代码中就是 。Send10次,必须Recv10次,才能把数据读完(不严谨,应该是大于等于10次,但便于理解是这个意思)。

简单点可以理解为,UDP的每一次发送的数据,都是单独的,不会和上一次发送的数据连在一起。

UDP我就不写代码验证了。有兴趣可以自己试试看。

下次再讨论一下SOCKET 的IO模型把




原文地址:https://www.cnblogs.com/CSSZBB/p/14513064.html