Socket 编程中,TCP 流的结束标志与粘包问题

摘要:
因为TCP本身是无边界的协议,因此它并没有结束标志,也无法分包。socket和文件不一样,从文件中读,读到末尾就到达流的结尾了,所以会返回-1或null,循环结束,但是socket是连接两个主机的桥梁,一端无法知道另一端到底还有没有数据要传输。约定结束标志,当读到该结束标志时退出不再read。参考关于java网络编程中获取输入流中数据的问题Linux编程之socket:tcp流协议产生的粘包问题及解决方法终端如何与服务器通信——玩转通信协议

因为 TCP 本身是无边界的协议,因此它并没有结束标志,也无法分包。「包」的界定,是更上层的协议的事了(比如 HTTP)。

socket和文件不一样,从文件中读,读到末尾就到达流的结尾了,所以会返回-1或null,循环结束,但是socket是连接两个主机的桥梁,一端无法知道另一端到底还有没有数据要传输。
socket如果不关闭的话,read之类的阻塞函数会一直等待它发送数据,就是所谓的阻塞。

如果发送的东西非常多必须要用循环读,可以有以下解决方案:

  • 调用socket的 shutdownOutput 方法(Java)关闭输出流,该方法的文档说明为,将此套接字的输出流置于“流的末尾”,这样另一端的输入流上的read操作就会返回-1。
  • 约定结束标志,当读到该结束标志时退出不再read。 (Http 的 Transfer-Encoding: Chunked 首部,表示将以一个 length 为 0 的 chunk 做结束标志)
  • 设置超时(timeout),会在设置的超时时间到达后抛出SocketTimeoutException异常而不再阻塞。
  • 双方定义好通信协议,在协议头部约定好数据的长度。当读取到的长度等于这个长度时就不再继续调用read方法。(Http 的 content-length 首部,会给出主体的长度)

而如果需要发送多个相互独立的内容,内容之间就需要有明确的分界,方法有:

  1. 像 multipart-form 一样,使用 boundary 字串做分割,使用 Content-Type 等首部做内容标识。
  2. 用二进制帧做分割,在帧首部定义好帧的长度和其他信息。

参考

关于java网络编程中获取输入流中数据的问题
Linux编程之socket:tcp流协议产生的粘包问题及解决方法
终端如何与服务器通信——玩转通信协议(源码下载)

免责声明:文章转载自《Socket 编程中,TCP 流的结束标志与粘包问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇jQuery快速入门jQuery多图上传Uploadify插件使用及传参详解下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

PHP socket 接收 java端口 netty 网络字节序

java 服务端测试代码: @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throwsException { buffer.writeShort(5); buffer.writeI...

oracle之 单实例监听修改端口

Oracle 单一主机多个实例多个监听器配置要点   1. 一台服务器主机, 有多个实例, 如: TSDB/ORCL; 又需要配置多个监听器 2. 需要指定不同的LISTENER端口   3.pmon只会动态注册port等于1521的监听器, 其它端口则pmon不能动态注册listener, 要想让pmon动态注册listener, 需要设置local_l...

iptables关键学习总结

iptables技术推荐参考这位仁兄的博客:http://www.zsythink.net/archives/category/%E8%BF%90%E7%BB%B4%E7%9B%B8%E5%85%B3/%E9%98%B2%E7%81%AB%E5%A2%99/page/2/ iptables指南参见:https://www.frozentux.net/ipt...

java nio--采用Selector实现Socket通信

server: 1 /** 2 * 选择器服务端 3 * Created by ascend on 2017/6/9 9:30. 4 */ 5 public class SelectorServer { 6 // public final static String REMOTE_IP = "192.168.0....

[置顶]C#中Socket服务端代码分享

最近在对项目中Socket通讯中的服务端代码进行优化,保证能接受尽可能多的客户端的连接,并且不会丢掉连接,不会掉数据包。经过一段时间的反复测试和修改,终于达到了这一要求。服务端代码采用了异步通讯的方式,并使用ManualResetEvent来对线程进行控制。在程序中,ManualResetEvent 的使用很关键。 ManualResetEvent 允许线...

关于socket通信bind()返回值错误:10049

前几天偶尔收到一个朋友的求救,要编写一个IPV6的socket编程 开始以为这个问题很容易,因为socket已经兼容IPV4和IPV6的,改下那几个接口就可以了 然后....被卡住了.... 修改了参数为IPV6的参数以后,在bind的时候始终包10049错误. 按照MSDN的说法,10049的意思表示找不到那个IP. 但是我服务器端bind监听的sock...