程序员行业有一些奇怪的错误的观点(误解),这些误解非常之流行,而且持有这些错误观点的人经常言之凿凿,打死也不相信自己有错,实在让人啼笑皆非。究其原因,还是因为这些错误观点所对应的正确观点不符合人的正常思维习惯,是扭曲人的直观感受的。

有两个错误观点非常之经典,一而再,再而三的出现,就跟韭菜一样,割不完,还越长越多。一是经典的"服务器最多65536个连接"误解,打开链接看介绍。另一个就是这里要讲的TCP"粘包"和"拆包"问题。

基于前面的思路,我们先介绍人的正常思维习惯,然后再介绍扭曲的正确观点,这样你就会印象深刻了。

首先,人的正常思维习惯是,数据传输应该是基于报文的,人们不论是对话还是写信,肯定是一句一句的话或者一封一封的信,这就是所谓的报文。而 TCP 是什么东西呢?TCP 是一种流式协议,简单说,你使用它的时候,根本就没有所谓的报文,无论是聊天说的话还是发的图片,对于 TCP 来说,通通都是没有边界的,TCP 根本不认识这些东西,不知道你按换行时是一句话,你发的图片是一张图片,所有的东西都是数据流没有任何边界。这显然不符合人的正常思维,是扭曲的。

而无论是人的思维,还是实际的东西,一定是要有边界的。这就有矛盾了。

所以,对于程序员,在使用 TCP 之前,你必须进行报文协议设计。要么你使用别人设计好的报文协议,要么你自己设计。如果你自己不设计,也不使用别人设计好的,那么你肯定错了,一定会遇到所谓的粘包和拆包。

关于报文协议设计,这篇文章有介绍 http://www.ideawu.net/blog/archives/429.html

一般的程序员当然不愿意自己设计,一是没能力,二是不需要重新发明。那么设计完之后呢,必然是实现。因为自身水平的问题,还是会遇到 TCP 粘包和拆包问题。所以,我的建议是,不要自己去实现,应该去使用别人已经写好的代码。

接下来讲一讲如果自己直接使用 TCP 的接口,为什么会遇到粘包和拆包问题呢?TCP 的接口简单说只有两个:

send(data, length);
recv(buff);

不懂的人会基于人的正常思维想当然地认为 send() 就是发送报文,recv() 就是接收报文。但是,前面已经提到了,TCP 是没有所谓的报文边界的,所以你错了。为什么错?因为 send() 和 recv() 不是一一对应的。也就是说,send() 的调用次数和 recv() 的调用次数是独立的,有时是相等的(你在自己机器上和内网测试时基本都是相等的),有时是不相等的(互联网上非常容易出现)。

现在明白了吧,TCP 的 send() 和 recv() 不是一一对应的,理所当然会粘应用层的包,拆应用层的包。明白了之后怎么解决?简单,用别人封装好的代码。或者,你自己慢慢琢磨去吧!

正确地从TCP socket中读取报文的代码必须长这个样子,如果不长这个样子,就是错的!

char recv_buf[];
Buffer buffer;
// 网络循环:必须在一个循环中读取网络,因为网络数据是源源不断的。
while(1){
// 从TCP流中读取不定长度的一段流数据,不能保证读到的数据是你期望的长度
tcp.read(recv_buf);
// 将这段流数据和之前收到的流数据拼接到一起
buffer.append(recv_buf);
// 解析循环:必须在一个循环中解析报文,避免所谓的粘包
while(1){
// 尝试解析报文
msg = parse(buffer);
if(!msg){
// 报文还没有准备好,糟糕,我们遇到拆包了!跳出解析循环,继续读网络。
break;
}
// 将解析过的报文对应的流数据清除
buffer.remove(msg.length);
// 业务处理
process(msg);
}
}

这段代码包含两个循环:网络循环和解析循环。

注意了!如果你的代码不长这个样子,绝对是错的!一定是错的!不要狡辩!正确的做法只有一种,错误的做法成千上万。

如果你想真正的解决所谓的TCP粘包和拆包问题,你写的代码必须和我的一样。如果不一样,就是错的,说明你根本就没弄懂。

Related posts:

  1. Master-Workers 模式处理高负载
  2. 通过 HTTP POST 发送二进制数据
  3. 炮打TCP – 关于一而再再而三的粘包拆包问题的大字报
  4. 数据传输中的停止等待机制的实现
  5. 使用 Channel 进行可靠传输
Posted by ideawu at 2017-06-02 15:02:56

关于TCP粘包和拆包的终极解答的更多相关文章

  1. netty 解决TCP粘包与拆包问题(一)

    1.什么是TCP粘包与拆包 首先TCP是一个"流"协议,犹如河中水一样连成一片,没有严格的分界线.当我们在发送数据的时候就会出现多发送与少发送问题,也就是TCP粘包与拆包.得不到我 ...

  2. tcp粘包和拆包的处理方案

    随着智能硬件越来越流行,很多后端开发人员都有可能接触到socket编程.而很多情况下,服务器与端上需要保证数据的有序,稳定到达,自然而然就会选择基于tcp/ip协议的socekt开发.开发过程中,经常 ...

  3. 【Netty】TCP粘包和拆包

    一.前言 前面已经基本上讲解完了Netty的主要内容,现在来学习Netty中的一些可能存在的问题,如TCP粘包和拆包. 二.粘包和拆包 对于TCP协议而言,当底层发送消息和接受消息时,都需要考虑TCP ...

  4. TCP粘包和拆包问题

    问题产生 一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和封包问题. 下面可以看一张图,是客户端向服务端发送包: 1. 第一种情况 ...

  5. TCP粘包,拆包及解决方法

    在进行Java NIO学习时,发现,如果客户端连续不断的向服务端发送数据包时,服务端接收的数据会出现两个数据包粘在一起的情况,这就是TCP协议中经常会遇到的粘包以及拆包的问题.我们都知道TCP属于传输 ...

  6. TCP粘包、拆包

    TCP粘包.拆包 熟悉tcp编程的可能都知道,无论是服务端还是客户端,当我们读取或发送数据的时候,都需要考虑TCP底层的粘包/拆包机制. TCP是一个“流”协议,所谓流就是没有界限的遗传数据.可以想象 ...

  7. 【游戏开发】网络编程之浅谈TCP粘包、拆包问题及其解决方案

    引子 现如今手游开发中网络编程是必不可少的重要一环,如果使用的是TCP协议的话,那么不可避免的就会遇见TCP粘包和拆包的问题,马三觉得haifeiWu博主的 TCP 粘包问题浅析及其解决方案 这篇博客 ...

  8. netty 解决TCP粘包与拆包问题(二)

    TCP以流的方式进行数据传输,上层应用协议为了对消息的区分,采用了以下几种方法. 1.消息固定长度 2.第一篇讲的回车换行符形式 3.以特殊字符作为消息结束符的形式 4.通过消息头中定义长度字段来标识 ...

  9. TCP粘包的拆包处理

    因为TCP是流式处理的,所以包没有边界,必须设计一个包头,里面表示包的长度(一般用字节表示),根据这个来逐个拆包.如果对于发送/接收频率不高的话,一般也就不做拆包处理了,因为不大可能有粘包现象. 以下 ...

随机推荐

  1. java架构

    技术架构是以Spring Framework为核心容器,Spring MVC为模型视图控制器,MyBatis作为数据访问层, Apache Shiro为权限授权层,使用Ehcahe对常用数据进行缓存. ...

  2. redis和memcached有什么区别?redis的线程模型是什么?为什么单线程的redis比多线程的memcached效率要高得多(为什么redis是单线程的但是还可以支撑高并发)?

    1.redis和memcached有什么区别? 这个事儿吧,你可以比较出N多个区别来,但是我还是采取redis作者给出的几个比较吧 1)Redis支持服务器端的数据操作:Redis相比Memcache ...

  3. 《你们都是魔鬼吗》第八次团队作业:第四天Alpha冲刺

    <你们都是魔鬼吗>第八次团队作业:Alpha冲刺 项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 作业链接地址 团队名称 你们都是魔鬼吗 作业学习目标 完成最 ...

  4. set_index

    Signature: df.set_index( ['keys', 'drop=True', 'append=False', 'inplace=False', 'verify_integrity=Fa ...

  5. Go语言 - 关于常用插件不能安装的处理办法

    解决办法 这里的是Windows的环境下的解决办法 在GOPATH的src目录下面创建github.com\golang文件夹,若文件夹存请忽略本步骤 在vs code终端执行: cd %GOPATH ...

  6. php 程序执行时间检测

    我们有的时经常需要做程序的执行时间执行效率判断.大理石平台检定规程 实现的思路如下: <?php //记录开始时间 //记录结整时 // 开始时间  减去(-)  结束时间  得到程序的运行时间 ...

  7. win10 安装Borland C++Builder 6后编译运行出

    win10系统安装bcb后打开bcb后显示 Unable to rename ‘c:\Program Files(x86)\Borland\CBuilder6\Bin\bcb.$$$'to‘cc:\P ...

  8. COGS 1583. [POJ3237]树的维护

    二次联通门 : COGS 1583. [POJ3237]树的维护 /* COGS 1583. [POJ3237]树的维护 树链剖分 + 边权化点权 线段树 单点修改 + 区间取相反数 + 查询区间最大 ...

  9. Ultra Edit中的数据对齐

    有时会用到Ultra Edit的数据对齐功能.比如,要求64个符号一组,从低位开始对齐.这时,如果数据长度不是一行长度的整数, 就会产生高位对齐.低位不足的问题.为了调整,往往需要逐行调整,很不方便. ...

  10. Tkinter 之NoteBook选项卡标签

    一.参数说明 参数 作用 width 选项卡宽度,单位像素 height 选项卡高度 cursor 鼠标停留的样式 padding  外部空间填充,是个最多4个元素的列表 style 设置menubo ...