HTTP 代理服务器的设计与实现(C++)

打印 上一主题 下一主题

主题 892|帖子 892|积分 2676

实验内容


  • 设计并实现一个基本 HTTP 代理服务器。要求在指定端口(例如 8080)接收来自客户的 HTTP  请求并且根据其中的 URL 地址访问该地址 所指向的 HTTP 服务器(原服务器),接收 HTTP 服 务器的响应报文,并 将响应报文转发给对应的客户进行浏览。
  • 设计并实现一个支持 Cache 功能的 HTTP 代理服务器。要求能缓 存原服务器响应的对象,并 能够通过修改请求报文(添加 if-modified-since 头行),向原服务器确认缓存对象是否是最新版本。 (选作内容)
  • 扩展 HTTP 代理服务器,支持如下功能: (选作内容)

    • 网站过滤:允许/不允许访问某些网站;
    • 用户过滤:支持/不支持某些用户访问外部网站;
    • 网站引导:将用户对某个网站的访问引导至一个模拟网站(钓鱼)。

代理服务器的概念

代理服务器,允许一个网络终端(一般为客户端)通过这个服务与另一 个网络终端(一般为服务器)进行非直接的连接。普通 Web 应用通信方式与采用代理服务器的 通信方式的对比如下图所示:

代理服务器在指定端口(本实验中所指定的是666端口)监听浏览器的访问请求(需要在客户端浏览器进行相应的设置),接收到浏览器对远程网站的浏览请求时,代理服务器开始在代理服务器的缓存中检索 URL 对应的对象(网页、 图像等对象),找到对象文件后,提取该对象文件的最新被修改时间;代理服务器程序在客户的请求报文首部插入,并向原 Web 服务器转发修改后的请求报文。如果代理服务器没有该对象的缓存,则会直接向原服务器转发请求报文,并将原服务器返回的响应直接转发给客户端,同时将对象缓存到代理服务器中。代理服务器程序会根据缓存的时间、大小和提取记录等对缓存进行清理。

代码结构

代码中共实现 3个类,分别为WebsiteDetector类、Cache类和HttpProxyServer类。
WebsiteDetector类:

该类实现了网站过滤和网站引导功能。通过构造函数直接静态设置了钓鱼网站和屏蔽的网站:
  1. WebsiteDetector::WebsiteDetector()
  2. {
  3.         AddValidURL("http://jwc.hit.edu.cn/","http://jwts.hit.edu.cn/");
  4.         AddBlockedURL("http://xltj.hit.edu.cn/");
  5. }
复制代码
可知,屏蔽了心理网站。将教务处网站引导到本科教学管理与服务平台。
Cache

该类在当前目录下创建文件夹.cache/,在其中存储浏览缓存对象。同时该类中,保存着对象与文件名的映射关系,对象和LastModified字段的映射关系。
  1. class Cache {
  2. public:
  3. std::string GetDate(const std::string& url); // 获取url对应保存的LastModified字段
  4. bool Get(const std::string& url, char* response, size_t& start, size_t& responseSize); // 读取缓存
  5. bool Put(const std::string& url, const char* response, size_t responseSize, size_t& start); // 保存缓存
  6. private:
  7. ​      std::string cacheDirectory_; // 存放缓存的文件目录
  8. ​      std::map<std::string, std::string> cacheMap_; // 对象和LastModified字段的映射关系
  9. ​      std::map<std::string, std::string> fileMap_; / 对象与文件名的映射关系
  10. ​      std::mutex mutex_; // 多线程同时读写文件的互斥锁
  11. };
复制代码
HttpProxyServer

该类是代理服务器的实现类。是一个多用户代理服务器。首先该类创建HTTP代理服务的TCP主套接字,该套接字监听等待客户端的连接请求。当客户端连接之后,创建一个子线程,由子线程行上述一对一的代理过程,服务结束之后子线程终止。
  1. class HttpProxyServer
  2. {
  3. public:
  4.         HttpProxyServer(int port); // 构造函数,参数为端口号
  5.         void Start(); // 监听客户端连接请求
  6. private:
  7.         int serverSocket_; // 代理服务Socket
  8.         int port_; // 端口号
  9.         struct sockaddr_in serverAddr_; // 代理服务地址
  10.         Cache cache_; // Cache类
  11.         WebsiteDetector websiteDetector_; // websiteDetector类
  12.         void HandleClient(int clientSocket); // 子线程调用函数
  13.         std::string ExtractUrl(const std::string &httpRequest); // 解析URL
  14.         int CreateServerSocket(const std::string &host); // 创建与原服务器连接的Socket
  15.         bool ServerToClient(const std::string &url, int clientSocket); // 转发数据
  16.         void ParseUrl(const std::string &url, std::string &host, std::string &path); // 解析主机名与路径名
  17. };
复制代码
程序基本流程

(1)  初始化服务器Socket,监听等待客户端的连接请求。
(2)  当客户端连接后,创建子线程处理请求。
(3)  子线程接收请求,解析HTTP请求的首部行和请求头。然后提取Url,Url作为参数通过websiteDetector类判断是否屏蔽或者引导。
(4)  然后进入转发的过程,首先进行域名解析,然后创建Socket先原服务器发送请求,接收响应,将数据转发到客户端。
(5)  在转发的过程中,涉及保存缓存和读取缓存。
网站引导功

利用首部行中的location字段,实现引导。
  1. std::string locationResponse = std::string("HTTP/1.1 302 Found") + MY_CRLF + "Location: " + newUrl + MY_CRLF + MY_CRLF;
  2. send(clientSocket, locationResponse.c_str(), locationResponse.size(), 0);
复制代码
用户过滤功能

设置服务器地址信息时实现。
  1. serverAddr_.sin_addr.s_addr = INADDR_ANY;
  2. // serverAddr_.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //只允许本机用户访问服务器
复制代码
Cache功能

1.   代理服务器处理客户端请求时,对于第一次出现的对象,会保存下。当客户端再次请求时,代理服务器就会在请求中添加If-Modified-Since首部行。
  1. date = cache_.GetDate(url);
  2. std::string cacheRequest = httpRequest + "If-Modified-Since: " + date + MY_CRLF + MY_CRLF;
复制代码

  • 发送该请求后,等待原服务器响应,并判断是否回应304状态码。
[code]if (IsResponseNotModified(responseNotModified) ) {​      // std::cout

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

半亩花草

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表