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

有两个错误观点非常之经典,一而再,再而三的出现,就跟韭菜一样,割不完,还越长越多。一是经典的"服务器最多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. 网站检测空链、死链工具(Xenu)

    网站常用检测空链.死链工具,Xenu是很小但是功能强大的检查网站404链接的软件,支持多线程,无需安装可直接打开使用.步骤如下: 网站的链接一般都成千上万,如果存在大量的空链接将大大的影响用户体验,怎 ...

  2. 移动App性能评测与优化-Android内存测试 ,DVM原理

    常见的测试方法包括Monkey/UIAutomator类的常规压力测试,大数据/操作的峰值压力测试,长时间运行的稳定性测试等. 前提: 测试准备:版本是纯净版本,不应该附加多余的log和调试用组件. ...

  3. nginx+keepalived高可用 (主从+双主)

    1.Nginx+keepalived 主从配置这种方案,使用一个vip地址,前端使用2台机器,一台做主,一台做备,但同时只有一台机器工作,另一台备份机器在主机器不出现故障的时候,永远处于浪费状态,对于 ...

  4. vue-router路由拦截基本设置,md5加密,js-cookie,vuex刷新页面store中的数据丢失等

    vuex持久化 vuex-persistedstate

  5. webpack打包后服务端__dirname失效问题

    在webpack.config.js中添加如下配置: target: 'node', node: { __dirname: false, __filename: false, } 详见:https:/ ...

  6. BeautifulSoup模板简单应用-提取html指定数据(api_name/api_method/api_path,请求body/请求header/pagam参数)

    from bs4 import BeautifulSoup import re import os.path import itertools name='newcrm' source_file_pa ...

  7. 【DP】 路面修整 usaco 2008 feb_gold

    题目描述: ``` FJ打算好好修一下农场中某条凹凸不平的土路.按奶牛们的要求,修好后的路面高度应当单调上升或单调下降,也就是说,高度上升与高度下降的路段不能同时出现在修好的路中. 整条路被分成了N段 ...

  8. python中的一切皆对象

    python中一切皆对象是这个语言灵活的根本.函数和类也是对象,属于python的一等公民.包括代码包和模块也都是对象.python的面向对象更加彻底. 可以赋值给一个变量可以添加到集合对象中可以作为 ...

  9. 002_基础电路_AD快捷键

    AD快捷键设置 陆小果哥哥制作 1.      F2----------------------------------------放置走线 a)        b)       需设置,点中走线按住 ...

  10. 计算 byte[] 转 int modebus 指定位数 获取值 使用

    计算 byte[] 转 int modebus 指定位数 获取值 使用 if (bytetores.Length > 6) { int total = 0; for (int i = 0; i ...