该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新。http://xiaorui.cc/?p=3198

只要做过服务开发的人都会遇到粘包的问题,跟着标题也知道我在忙啥,对的,我在用python写一个简单粗暴的web服务器,中间就遇到了一个粘包的问题,我会在后面说明下这造成这问题的原因。 

理解TCP流的概念及粘包的现象

首先我们要明确一点,HTTP是基于TCP的上层应用协议,这是废话 !  tcp是数据流,这里有两个要点:

- 数据没有边界
你可以理解为水在一个水管里的流动,我们不知道哪段数据是一个我们需要的完整数据

- 收发有缓冲区
比如:当水从一端流到了另一端,我们在收数据的时候,不可能每来一滴水就处理一次,这个缓冲区就相当于有了一个水桶,再接了一定的水之后内核再给数据交到用户空间,这样可以大大提升性能。

那这里就有一个问题:由于这个特性,内核无法知道哪部分数据是你想要的,所以应用层拿到数据可能是完整数据,也可能是不完整或者一部分完整、一部分不完整(粘包),那么怎样能得到想要的数据呢?

数据协议可以理解为一种约定,按照这个约定来处理收到的数据进而得到想要的数据,这个过程就称为拆包,那问题又来了?

那么http会出现粘包的问题么? 

粘包是因为双方没有协商好协议导致的,如果有一个协议支撑整个数据的流通,那么不应该出现粘包的问题。我上次写过一篇golang tcp server粘包的问题,后来也是用了一个简单的协议解决粘包的问题。

http的请求和响应是以message为单位的。解析http当然和其他的协议一样,需要考虑不符合协议的情况,400 Bad Request响应就是因为发给server的数据包不合理。
http/1.1之前,每次请求响应都使用新的tcp连接,所以不需要考虑数据边界问题。
http/1.1支持keep-alive,多个message可以使用同一个tcp连接,所以要有确定边界的机制。

按照此类请求头的内容来解析就可以了,解析失败就返回400关连接,让客户端自己重试。
或者无视客户端的Connection: keep-alive头,一律返回Connection: close,就不用操心这个问题了。

那么http协议是如何定义边界,换个说法是怎么解决http粘包的问题。 

因为web的http可以理解为一个公共的数据协议,在实现一个web服务器的时候,就必需按照这个协议来进行数据处理,这样保证在应用层获取到数据是完整的数据,http是典型的包头+包体的这个一个约定格式.

- 通过\r\n\r\n来区分包头和包体

- 包头通过\r\n来区分不同的信息

看个示例:

对于大数据传输是怎么解决的。

通常如果 Content-Length 比实际长度短,会造成内容被截断;如果比实体内容长,会造成浏览器pending。持久链接必须要加Content-length,而且要精确算好大小,如果对于无法计算大小的respone,可以用Transfer-Encoding: chunked模式。 他适用于那种边取计算边发送的场景。

Transfer-Encoding chunked的格式:

最后以字节大小为0的块为结束,不用包含任何数据。当浏览器看到是0字节,就不再组装数据包.

另外我们要注意 multipart/form-data的Content-Type , multipart一般是用来上传文件的,大部分的http client已经实现了对该模式的支持。

实现也相对简单,首先我们估算一下文件的大小,然后传递一个boundary数据组分隔符,服务端会根据Content-Length和boundary来获取数据。


文章部分内容抄袭链接:

https://www.zhihu.com/question/24598268

END



对Python及运维开发感兴趣的朋友可以加QQ群 : 478476595 !!!

另外如果大家觉得文章对你有些作用!   帮忙点击广告. 一来能刺激我写博客的欲望,二来好维护云主机的费用.
如果想赏钱,可以用微信扫描下面的二维码. 另外再次标注博客原地址  xiaorui.cc  ……   感谢!
暂无相关产品