网络编程中如何得知一次请求(或响应)的数据已接收完
两年前用.net 2.0做了一个反向代理服务器,在这两年时间里,不断修改BUG以及优化性能,使得可用性大大提高。近来碰到一个功能需求,实在无法找出有效的解决办法,只好上来请教各位高人。
先说说反向代理的工作机理吧。
1、客户端通过浏览器访问反向代理的时候,会发出一个HTTP请求,反向代理收到这个TCP连接的时候,建立一个新的会话用于处理这个请求(BeginAccept、EndAccept);
2、会话对象建立一个从客户端接收数据的委托,开始异步读取数据(BeginRead);
3、取得数据时,进入异步读取的回调函数中,开始处理数据(EndRead);
4、检查反向代理与服务器的连接是否已建立,如果没有建立,那么需要先建立连接(ConnectServer),并建立服务器的异步读取委托(BeginRead);
5、把数据异步写入服务器(BeginWrite);
6、重新建立客户端异步读取委托(BeginRead),回到3;
7、收到服务器返回数据时,处理后,异步写入客户端(BeginWrite);
8、重新建立服务器异步读取委托(BeginRead),回到7;
所有的数据传输,都使用异步来完成,而只需要在3和7处为业务编写数据处理代码即可。
实际上,对于反向代理来说,只需要处理客户端发来的数据就可以了,需要把HTTP的HOST头替换为真实服务器,而对于服务器响应的数据,只需要原样发送给客户端就可以了。
在步骤3中,我们只知道当前收到了客户端发来的数据,而不知道这个数据是不是Http请求头,或者是完整的Http请求头。幸好,对于反向代理来说,不需要关心是否是完整的Http请求头,只需要检查是否是Http请求头,如果是,就修改Host即可。在这里,我假设Http请求的第一个数据包肯定是独立的数据包,不会“粘”在TCP连接中上一次数据的后面,这样就可以直接使用Http协议规定的格式来检查这个数据包是否Http请求头了。虽然这个假设没有什么依据,但它确实非常有效。
程序就这样工作了两年,没有什么问题。
但接下来,问题就出现了,有一个需求,要求能够把服务器返回的页面中的某个字符串替换为指定的字符串。比如我用反向代理指向博客园,我就需要把博客园页面中所有使用了绝对路径的连接修改为指向反向代理服务器的连接。这就要求在步骤7这里处理数据,把数据转为字符串,然后替换链接,然后才发往客户端。
但步骤7每次收到的数据只是一个片段,而不是整个页面的HTML。即使我们再次假设Http响应的第一个数据包是独立的数据包,也只能识别哪些是响应头,哪些是数据体而已。也想过每一段数据转为这一段的字符串进行处理,但是,如果刚好某个字符被网络层拆分到两个TCP数据包里怎么办?还有,想博客园这样使用了gzip的,如果不接受完整个页面的数据,是无法解压的;就算这两种情况都不存在,而网络层刚好在超链接的地方拆分数据包怎么办?
因此,最保守的做法就是拿到整个页面数据再开始处理。也想过Http响应头那里有个Content-Length指明内容长度的,但实际中,很多响应根本就不到这个段。
我查看过HttpListener类和HttpListenerRequest类,尝试从中发现它是如何接受完一次请求(响应)的,可惜这两个类调用了大量NativeAPI,就无法得知了。
还有浏览器,它又是如何得知某次响应是否已经完成的呢?
还请各位高人多多指教!
这个代理已经放到codeplex上,大家有兴趣可以看看:http://www.codeplex.com/XProxy/
还有,不要忘了给我的问题提个解决思路^_^
谢谢!
QQ:99363590
E-Mail:nnhy#vip.qq.com
QQ群:10193406
先说说反向代理的工作机理吧。
1、客户端通过浏览器访问反向代理的时候,会发出一个HTTP请求,反向代理收到这个TCP连接的时候,建立一个新的会话用于处理这个请求(BeginAccept、EndAccept);
2、会话对象建立一个从客户端接收数据的委托,开始异步读取数据(BeginRead);
3、取得数据时,进入异步读取的回调函数中,开始处理数据(EndRead);
4、检查反向代理与服务器的连接是否已建立,如果没有建立,那么需要先建立连接(ConnectServer),并建立服务器的异步读取委托(BeginRead);
5、把数据异步写入服务器(BeginWrite);
6、重新建立客户端异步读取委托(BeginRead),回到3;
7、收到服务器返回数据时,处理后,异步写入客户端(BeginWrite);
8、重新建立服务器异步读取委托(BeginRead),回到7;
所有的数据传输,都使用异步来完成,而只需要在3和7处为业务编写数据处理代码即可。
实际上,对于反向代理来说,只需要处理客户端发来的数据就可以了,需要把HTTP的HOST头替换为真实服务器,而对于服务器响应的数据,只需要原样发送给客户端就可以了。
在步骤3中,我们只知道当前收到了客户端发来的数据,而不知道这个数据是不是Http请求头,或者是完整的Http请求头。幸好,对于反向代理来说,不需要关心是否是完整的Http请求头,只需要检查是否是Http请求头,如果是,就修改Host即可。在这里,我假设Http请求的第一个数据包肯定是独立的数据包,不会“粘”在TCP连接中上一次数据的后面,这样就可以直接使用Http协议规定的格式来检查这个数据包是否Http请求头了。虽然这个假设没有什么依据,但它确实非常有效。
程序就这样工作了两年,没有什么问题。
但接下来,问题就出现了,有一个需求,要求能够把服务器返回的页面中的某个字符串替换为指定的字符串。比如我用反向代理指向博客园,我就需要把博客园页面中所有使用了绝对路径的连接修改为指向反向代理服务器的连接。这就要求在步骤7这里处理数据,把数据转为字符串,然后替换链接,然后才发往客户端。
但步骤7每次收到的数据只是一个片段,而不是整个页面的HTML。即使我们再次假设Http响应的第一个数据包是独立的数据包,也只能识别哪些是响应头,哪些是数据体而已。也想过每一段数据转为这一段的字符串进行处理,但是,如果刚好某个字符被网络层拆分到两个TCP数据包里怎么办?还有,想博客园这样使用了gzip的,如果不接受完整个页面的数据,是无法解压的;就算这两种情况都不存在,而网络层刚好在超链接的地方拆分数据包怎么办?
因此,最保守的做法就是拿到整个页面数据再开始处理。也想过Http响应头那里有个Content-Length指明内容长度的,但实际中,很多响应根本就不到这个段。
我查看过HttpListener类和HttpListenerRequest类,尝试从中发现它是如何接受完一次请求(响应)的,可惜这两个类调用了大量NativeAPI,就无法得知了。
还有浏览器,它又是如何得知某次响应是否已经完成的呢?
还请各位高人多多指教!
这个代理已经放到codeplex上,大家有兴趣可以看看:http://www.codeplex.com/XProxy/
还有,不要忘了给我的问题提个解决思路^_^
谢谢!
QQ:99363590
E-Mail:nnhy#vip.qq.com
QQ群:10193406
我不相信神话,我只相信汗水!我不相信命运,我只相信双手!