HTTP传输测试#

---
创建日期: 2015-01-17
---
如题,测试HTTP传输。

这段时间想着怎么整LoadShedding。 如果使用服务端推送通知,可以,但是我且不说有没技术,也没钱弄服务器啊,感觉还是用户自己下载PDF比较好。 那怎么更新文件呢?总不能隔几天下载一次,浪费人家流量嘛这是。 我知道浏览器有个304状态的,所以只要检测服务端的修改日期就好了。 好吧,抛开这个,我要了解下HTTP传输过程中的信息,感觉在以后MIZip的一些数据操作中还是有用的。

我的意识中还是有Header这个概念的,至少200、404,还有Cookies。 使用PHP和JS,竟然产生了一种数据在各自端产生,然后像发送快递一样,或者说像搬砖一样从一处到另一处的感觉。 就会想比如下载东西,如果客户端关闭了,那服务端发送的消息怎么处理,它都已经产生了(我现在都很奇怪怎么会有这种想法:服务端已经生成了一份完全的拷贝,不断的往客户端送,服务器程序就不管了。而事实上这种描述好像又没有错),如果中途发送超时怎么办,客户端可就不要了? 或者客户端收到消息有误,需要重新发送一次。好些担忧……

我一直怨念的是,HTTP进行文件上传或者下载的时候,内容是一次性发送的吗?特别是较大的文件。 也一直想知道POST能发送多大的数据(因为认为它是一次性发送过去的),其实就是前面说的,担心对方都已经关闭了,本方还在一厢情愿的发送数据。 另外,之前知道Socket的事,所以有种HTTP、FTP和其它协议都是Socket,万法归宗的感觉。 当时还想做个WebSocket服务器的,可以确定,我现在是忘得差不多了。 所以Header加上Socket就是本次HTTP测试的主要线索。

在客户端或者服务端,我需要操作的并不是HTTP协议本身,所以很多细节都被接口隐藏了,这当然是更简单的方法,只是会忘了底层过程。 测试过程已经记不清了,基本上就剩一些结论。

第一个是发送粒度问题,我所误认为的信息一次性发送,就是认为发送粒度在Socket对象级别。 而事实上,一端可以不停的send(byte[] buff),另一端在不停的receive(byte[] buff),而且这两个操作默认是阻塞进程的。 所以至少,对于信息,一端读取一部分,发送,确认发送成功,再进行后续读取发送,而不是一次性把所有信息全部Send出去,只是HTML文件比较小,可能一次就Send了。(当然现在的网页已经不再小了)。

对于Receive,和Send次数有关吗?测试结果是没有,即使Send一次也可能Receive几次(相同的请求,返回的数据可能收到次数不同,而且单个大小也不同,不过返回中Header部分比较稳定地被单独Receive,可能是独立的一次Send); 而如果间隔段时间,几次Send也可以一次就Receive到。所以我也感觉使用while(receive(byte[] buff)>0)是不是有可能没接收完全,或者说底层使用TTL作为判据,这个我就不知道了。 Send次数不影响Receive结果,把一个Header分成几部分发送,中间加上Sleep,服务端(当然不是我做的服务端)也正常返回。我不确定服务器的处理方式,根据什么判断请求发送完成,\r\n\r\n?那如果有正文部分呢?加上Content Length判据?碰到一次Send中Header含一部分、正文含一部分的怎么处理?如果客户端Header始终没有发送完整呢?还是要多看看源代码,人家怎么考虑的。 其实应该猜到,即使一次Send也不可能把数据包放在一起,不是还有分组多线路发送包,到目的地再重组吗?估计得客户端接收完成,并反馈,一次的Send才算成功,才离开阻塞状态。

Header,首先是断点续传,我一直以为是一个比较高级的功能,其实就在Header中加了一个Range,多数服务器应该可以自动处理静态文件的断点。 但是例子中Range都是返回剩下部分的全部内容,既然剩下还有那么多,何必要断点,如果用户又中断了怎么办?还是因为误认为服务端一次发送剩下内容。

第二个是keep-alive,对于一个Socket对象,当一个文件传输完成,还可以继续请求下一个文件,而不需要重新创建Socket,我是不清楚建立新联接需要多少资源,反正肯定是省的。 一次请求完,keep-alive,本地如果不Send(或者是没有从上一个Receive的阻塞状态退出,因为while(receive(byte[] buff)>0)一直在等待服务端发送数据,除非Timeout,所以肯定还要其它判断,否则两端都在等待),对方自然也不会返回。 当connection: close,服务端关闭Socket,虽然客户端可以继续发送,服务端是不会理会的,早该干嘛干嘛去了,客户端的Receive不论多少次都是0(单独0字节是Send不出去的),而服务端再Send或者Receive就会出错。 前面说了,Header可以多次Send,所以也可以把多个Header一次Send出去,服务端会返回多个文件的,不过对于Receive是一样的,所以要自己判断。

第三个是Last-Modified,也就是对LoadShedding有用的,对于静态文件,服务器是会在Header中标记本文件最后修改时什么时候,如果请求Header加入If-Modified-Since,服务器就会判断是该200还是304。 经过简单分析加上查看浏览器中的做法,把If-Modified-Since设置为上一次请求返回的Last-Modified比较保险。

但是有个问题,如果文件已经修改了,那服务端会在发送完Header后把文件继续发送,不论客户端是否Receive,那些信息肯定已经缓存在客户端,而我要用户选择是否下载新文件就不真实了,已经预先下载好了,流量也花了。解决的方案是使用HEAD请求而不是GET,这样服务端只会返回Header而不含正文。那些下载肯定也是这样判断将要下载的内容会有多大。

再接着是压缩,我测试了MIZip的,部分会压缩部分不会,压缩确实体积小很多,只是我做测试开启压缩,就完全不知道返回了些什么东西。 顺便,想解析PDF文件的文本,了解了点压缩的内容:ZIP是文件格式,zlib提供压缩接口,但目标不是文件,真正的压缩算法是deflate(LZZ7+霍夫曼编码)。 解析PDF文件,对stream部分解压后,字符串还用了压缩,我也是跪了……

我也不知道哪里看到的,POST会发送两次,而GET只有一次,我想指的是Send次数,但个人感觉POST数据很多的时候会多次Send。 还有谁给GET请求来了个正文部分,我在PHP中有同时使用$_GET$_POST,如果GET有正文那怎么和POST分开?抄文章尽是瞎扯的。

再提醒自己,POST是提供了加密的可能,默认是没有加密的(你加密对方知道怎么解?),encode算个蛋的加密。 看了看MIZip/Hashare登陆时直接把账号密码显示在正文部分……