基于Socket的HTTP代理服务器的设计与实现

simpleLove

发布日期: 2019-12-17 08:45:56 浏览量: 497
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

一、实验目的

  • 熟悉并掌握 Socket 网络编程的过程与技术

  • 深入理解 HTTP 协议,掌握 HTTP 代理服务器的基本工作原理

  • 掌握 HTTP 代理服务器设计与编程实现的基本技能

二、实验内容

设计并实现一个基本 HTTP 代理服务器。要求在指定端口接收来自客户的 HTTP 请求并且根据其中的 URL 地址访问该地址所指向的 HTTP 服务器(原服务器),接收 HTTP 服务器的响应报文,并将响应报文转发给对应的客户进行浏览。

设计并实现一个支持 Cache 功能的 HTTP 代理服务器。要求能缓存原服务器响应的对象,并能够通过修改请求报文(添加 if-modified-since头行),向原服务器确认缓存对象是否是最新版本。

扩展 HTTP 代理服务器,支持如下功能:

  • 网站过滤:允许/不允许访问某些网站

  • 用户过滤:支持/不支持某些用户访问外部网站

  • 网站引导:将用户对某个网站的访问引导至一个模拟网站(钓鱼网站)

三、实验过程及结果

3.1 实验原理

3.1.1 Socket 编程的客户端和服务器端主要步骤

在TCP/IP网络应用中,通信的两个进程之间相互作用的主要模式是客户/服务器(C/S或B/S)模式,即客户向服务器发出请求,服务器接受到请求后,提供相应的服务。

两者的工作步骤可以通过下面流程图直观地看到:

服务器端

其过程是首先服务器方要先启动,并根据请求提供相应服务:

  • 打开一通信通道并告知本地主机,它愿意在某一公认地址上的某端口接收客户请求;对应的操作是申请一个socket,这时的socket称为“欢迎套接字”,然后绑定(bind)本地地址信息和欢迎套接字,然后开放监听(listen)

  • 等待客户请求到达该端口

  • 接收到客户端的服务请求时,处理该请求并发送应答信号。在TCP实现过程中进行了三次握手操作,但是实际编写过程中通过accept函数即可实现上述操作,并建立连接,注意这个时候才真正建立起了与客户机传输数据的套接字。接收到并发服务请求,要激活一新进程来处理这个客户请求。新进程处理此客户请求,并不需要对其它请求作出应答。服务完成后,关闭此新进程与客户的通信链路,并终止

  • 返回第二步,等待另一客户请求

  • 关闭服务器,对应的也就是关闭服务器的欢迎套接字

客户端

  • 打开一通信通道,即建立起要与服务器传输数据的套接字socket,通过connect连接到服务器所在主机的特定端口

  • 向服务器发服务请求报文,等待并接收应答;继续提出请求……

  • 请求结束后关闭通信通道并终止

从上面所描述过程可知:

  • 客户与服务器进程的作用是非对称的,因此代码不同

  • 服务器进程一般是先启动的。只要系统运行,该服务进程一直存在,直到正常或强迫终止

3.1.2 HTTP 代理服务器的基本原理与流程图

代理服务器,俗称“翻墙软件”,允许一个网络终端(一般为客户端),通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。

如图所示,为普通 Web 应用通信方式与采用代理服务器的通信方式的对比。

代理服务器可以认为是TCP/IP网络应用的客户端和服务器端的结合。一方面,它是浏览器客户端的服务器端,另一方面,它也是目标服务器的客户端。浏览器将请求报文发送给代理服务器,代理服务器经过一些处理或者不经过处理,将请求报文转发给目标服务器;目标服务器相应请求报文发出响应报文,代理服务器接受到响应报文之后直接将响应报文转发给浏览器客户端。

代理服务器在指定端口(例如 10240)监听浏览器的访问请求(需要在客户端浏览器进行相应的设置),接收到浏览器对远程网站的浏览请求时,首先查看浏览器来源的ip地址,如果属于被限制的用户,则认为没有接受到访问请求。否则,查看其请求的host主机,如果属于不允许访问的主机,则默认不向目标服务器发送请求;如果属于被引导的网站,则对该网站的请求报文中的host主机地址和url进行更改,代理服务器开始在代理服务器的缓存中检索 URL 对应的对象(网页、图像等对象),找到对象文件后,提取该对象文件的最新被修改时间;代理服务器程序在客户的请求报文首部插入<If-Modified-Since: 对象文件的最新被修改时间>,并向原 Web 服务器转发修改后的请求报文。如果代理服务器没有该对象的缓存,则会直接向原服务器转发请求报文,并将原服务器返回的响应直接转发给客户端,同时将对象缓存到代理服务器中。代理服务器程序会根据缓存的时间、大小和提取记录等对缓存进行清理。

流程图

3.1.3 HTTP 代理服务器实验验证过程以及实验结果

1.设置IE浏览器的代理服务器

2.运行程序

在IE浏览器输入 www.sougou.com ,在程序运行窗口发现了请求报文,并在浏览器端接收到了网页的数据,说明代理服务器基本功能实现。

3.拓展功能1-支持Cache功能

我们在加入缓冲区的代码中设置一个断点,如图:

图中309行的Buffer2是在缓冲区找到原网页,并且在原请求报文上加上了If-Modified-Since: 段的请求报文,刷新网页,查看Buffer2:

单步运行,查看原服务器返回的响应报文:

发现响应报文中有HTTP/1.1 304 Not Modified,表示没有更新,直接将缓存中的数据发送给浏览器,此时查看浏览器:

4.拓展功能2-a-网站过滤

在程序中设置了一个数组。以下四个网站主机被禁,接下来我们在浏览器输入被禁网站之一 http://today.hit.edu.cn/ ,看其反馈:

我们发现网站受限。接着我们查看源代码中,在代码设置断点查看:

Find表示在禁用网站中发现被禁用了,直接进入error。

5.拓展功能2-b-用户过滤

首先我限制只能用户来访问-即本机。

在之前我们看到成功了,现在我进行限制本机访问:

6.拓展功能2-c-网站引导

我们将网站 http://djangobook.py3k.cn/2.0/ 引导到 http://pt.hit.edu.cn/ ,在浏览器中输入网站 http://djangobook.py3k.cn/2.0/

3.1.4 实现 HTTP 代理服务器的关键技术及解决方案

简单代理服务器通过以下几个函数实现:

  1. //************************************
  2. // Method: InitSocket
  3. // FullName: InitSocket
  4. // Access: public
  5. // Returns: BOOL
  6. // Qualifier: 初始化套接字
  7. //************************************
  8. BOOL InitSocket()

该函数首先加载套接字库,这一步是必须的;然后分别使用C的库函数里的

  1. socket(AF_INET, SOCK_STREAM, 0);
  2. bind(ProxyServer, (SOCKADDR*)&ProxyServerAddr, sizeof(SOCKADDR));
  3. listen(ProxyServer, SOMAXCONN);

实现了服务器流程中的socket和bind和listen;

  1. //************************************
  2. // Method: ParseHttpHead
  3. // FullName: ParseHttpHead
  4. // Access: public
  5. // Returns: void
  6. // Qualifier: 解析 TCP 报文中的 HTTP 头部
  7. // Parameter: char * buffer
  8. // Parameter: HttpHeader * httpHeader
  9. //************************************
  10. BOOL ParseHttpHead(char *buffer, HttpHeader * httpHeader)

该函数对请求报文的头部文件进行解析,得到请求报文中的method,url,host和cookie存到一个对应的结构体中,该结构体用于ConnectToServer函数与原服务器建立链接。

  1. //************************************
  2. // Method: ConnectToServer
  3. // FullName: ConnectToServer
  4. // Access: public
  5. // Returns: BOOL
  6. // Qualifier: 根据主机创建目标服务器套接字,并连接
  7. // Parameter: SOCKET * serverSocket
  8. // Parameter: char * host
  9. //************************************
  10. BOOL ConnectToServer(SOCKET *serverSocket, char *host)
  1. //************************************
  2. // Method: ProxyThread
  3. // FullName: ProxyThread
  4. // Access: public
  5. // Returns: unsigned int __stdcall
  6. // Qualifier: 线程执行函数
  7. // Parameter: LPVOID lpParameter
  8. //************************************
  9. unsigned int __stdcall ProxyThread(LPVOID lpParameter)

该函数是核心函数,其实现了从浏览器接收请求报文,向服务器发送请求报文,从服务器接收响应报文,向浏览器发送响应报文。

基本的代理服务器中没有缓冲,处理方式中仅对请求报文头部进行解析,通过ParseHttpHead函数,然后将得到的头部文件作为ConnectToServer函数与原服务器建立链接。连接成功后,便将请求报文发送过去,接收收到响应报文,然后发送响应报文给浏览器即可。

3.1.5 代理服务器设置cahce实现方式

首先设置一个结构体,为存储在cache中的数据格式:

  1. struct __CACHE{
  2. cache_HttpHeader htphed;
  3. char buffer[MAXSIZE];
  4. char date[DATELENGTH];//存储的更新时间
  5. };

htphed用于在缓冲区中找存储的请求报文的头部,buffer是该请求报文在服务器端返回的响应报文,date指该响应报文最后一次更新的时间,即该响应报文中的last-modifited。

在该程序中,我在内存中申请了100个该结构体的内存,即一个大小为100的数组,用以作为CACHE。

然后在ProxyThread函数中,当收到请求报文时,在对报文头部处理之后,首先在cache中寻找该请求,如果找到了,在请求报文之中-第三行加入if-modified-since: date,发送给服务器,接收到服务器返回的响应报文,对响应报文进行处理,看其头部是否为304 not modified,如果是,直接将cache中的响应报文返回给浏览器,如果不是,首先将该响应报文存入cache中,即对cache进行更新—仍存储在之前的那个位置上,然后将响应报文返回给浏览器。如果在cache中没有找到该请求,将处理后的请求报文头部存入Cache,得到响应报文之后,对响应报文进行解析,得到date,然后将响应报文和date存入cache。

如果cache的100个区域满了,会再次从0开始覆盖原来的cache中的数据。

3.1.6 网站过滤

设置了一个全局数组,里面存放的是被禁止访问的网站的主机。

在ProxyThread函数中解析出请求报文头部之后,将请求报文头部中的host与全局数组中的数据进行比较,如果出现相同的表示访问的网站被禁止访问,直接跳转到结束位置。实现代码如下:

3.1.7 用户过滤

在主函数中,当建立起浏览器和代理服务器的链接时,得到浏览器的地址信息,也就得到浏览器端的ip地址,与被禁用户ip比较,如果相同,认为链接没有建立,代理服务器等待下一次访问请求。

3.1.8 网站引导

类似于网站过滤,在ProxyThread函数中解析出请求报文头部之后,将请求报文头部中的url与被引导的网站比较,如果相同,将请求报文中的url改为引导向的网站的url,host也变为引导向的网站的host即可。

四、实验心得

经过此次实验,在实践过程中很清晰地学到了TCP协议在传输数据的流程和方式;熟悉了Socket 网络编程的过程与技术;同时也更清晰地掌握了HTTP 代理服务器的基本工作原理;掌握了 HTTP 代理服务器设计与编程实现的基本技能。并且也在这基础上了解了浏览器在进行搜索网页过程中,网络所起的作用以及具体的工作原理,数据的传输方式等等,受益匪浅。

同时,熟悉了通过C语言实现网络编程的简单流程和技巧,对比之前在python中实现网络编程的步骤,很明显的可以发现,C语言代码量更大一些,而python实现socket编程简单几行代码就能实现C中的很长一段代码。就轻便性而言,我认为python更好一些。

在设计Cache和网站过滤、网站引导和用户过滤的过程中,也了解到了很多C语言scoket编程中函数的一些用法,一些技巧,是学习和掌握网络中应用层数据传输方式的很有效的方式。

上传的附件 cloud_download 基于Socket的HTTP代理服务器的设计与实现.7z ( 1.40mb, 4次下载 )
error_outline 下载需要13点积分

发送私信

成功一定有方法,失败一定有原因

12
文章数
9
评论数
eject