1.问题的提出
前段时间参加一个公司的面试,面试官提出一个问题:请从底层的角度分析HTTP协议中的POST和GET的区别。
当时我没能回答这个问题,后面问面试官这个问题,结果他也没有答出来。自己回来的路上一路思考并且搜索了相关的资料,网上的说法是:POST是分成两个TCP发出去,一个是Header头部分的TCP包,一个是Body的TCP包。自己心里面怀疑这个答案是否正确,因为如果按照网上的说法来进行系统设计的话,存在一定的性能问题,因为只包含header的TCP包的话肯定比较小,这个TCP包本来可以装更多的内容的,所以对网络的性能存在一定的浪费。
基于上述的怀疑,自己决定使用wireshark对POST方式的HTTP请求进行抓包,因此有了这一篇文章。
2.客户端和服务器端的代码
2.1 客户端的POST请求代码
写了一个简单的form页面,代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="http://52.198.228.221/post.php" method="post" enctype="multipart/form-data">
<input name="test" type="text">
<button type="submit">确定</button>
</form>
</body>
</html>
2.2 采用Postman作为客户端
分别将postman作为独立软件和chrome插件两者方式进行测试。
2.3 服务器端的代码
服务器端的代码很简单,直接把post提交的内容显示出来即可,代码如下:
<?php
var_dump($_POST);
3.测试结果
3.1 使用Chrome的原生form页面提交
3.1.1 大批量内容发送
我们使用大量的文字进行发送,使用的浏览器是chrome 63:
我们可以看到,这次TCP请求是分成4个tcp segment进行发送。我们再查看一下第一个TCP包:
从tcp的内容可以看到,该数据表只包含HTTP的Header部分,不包含Body的内容。
3.1.2 少量内容发送
我们接下来使用少量的文字进行发送,Chrome可以一次性的发送所有内容到服务器:
我们可以到Chrome没有进行TCP分包发送,一个TCP包就发送了所有的内容。
3.2 使用Firefox的原生form页面提交
3.2.1 大批量内容发送
我们使用大量的文字进行发送,使用的浏览器是Firefox 57:
我们可以看到,在发送同样的大批量内容的情况下,Firefox是分成了3个tcp segment发送,我们查看一下第一个TCP包:
我们看到,Firefox的第一个TCP已经不仅包含了HTTP中的Header的内容,而且包含了部分Body的内容。
3.2.2 少量内容发送
我们接下来使用少量的文字进行发送:
Firefox的结果和Chrome一样,都是一个TCP包把内容发完。
3.3 使用Postman(浏览器插件模式)进行POST提交
3.3.1 大批量内容发送
因为Postman是以浏览器插件的形式运行的,所以结果基本和上面Chrome的结果一样:
Postman发送的第一个TCP包:
和Chrome的结果一样,第一个TCP包只有HTTP中Header的内容。
3.3.2 少量内容发送
与Chrome的结果一样,一个TCP包把全部内容发完:
3.4 使用Postman(独立软件模式)进行POST提交
由于在Postman的官网上,出现了新版的Postman,它作为一个独立的软件出现,不再是一个Chrome的插件,我们同样对其进行了测试:
3.4.1 大批量内容发送
我们可以看到它和作为Chrome插件的Postman在TCP包的分割上有不同,发送同样的内容,这里是分割成为3个TCP Segments的,而插件版的Postman是分割成4个TCP Segments的。
我们在看看第一个TCP包的情况:
第一个TCP包已经包含有HTTP协议中Body的部分内容。
3.4.2 少量内容发送
和其他测试的结果一样,一个TCP包把全部内容发完:
3.5 使用PHPStorm自带的rest测试工具进行POST提交(底层是Apache HttpClient 4.5.2)
具体的设置如下:
3.5.1 大批量内容发送
我们可以看到是分成了3个TCP Segments进行发送的,我们再看一下第一个TCP包的内容:
可以看到,第一个TCP不仅有HTTP的Header的内容,还带有部分Body的内容。
3.5.2 少量内容发送
和其他的客户端一样,都是一个TCP包发完。
4.总结
上面进行了5个客户端的抓包测试,我们把结果总结成以下的表格:
客户端 | 大批量内容发送分成的TCP Segments数量 | 第一个TCP的内容 | 少量内容发送的TCP Segments数量 |
---|---|---|---|
Chrome63 | 4 | Header | 1 |
Firefox57 | 3 | Header+Body | 1 |
Postman(浏览器插件模式) | 4 | Header | 1 |
Postman(独立软件模式) | 3 | Header+Body | 1 |
PHPStorm(底层是Apache HttpClient) | 3 | Header+Body | 1 |
通过上述的测试,我们发现文章开头提到的“POST是分成两个TCP发出去,一个是Header头部分的TCP包,一个是Body的TCP包”是错误的,在一个TCP包能所有内容发完的情况下,只发一个TCP包,不会把HTTP的Header和Body的内容分成两个TCP包。