HTTP 代理服务器的设计与实现
写在前面
花了好几天才把计算机网络的实验一搞定,在此记录一下这个实验的流程。
本实验的要求也是比较简单明了的:
(1) 设计并实现一个基本 HTTP 代理服务器。要求在指定端口(例如 8080)接收来自客户的 HTTP 请求并且根据其中的 URL 地址访问该地址 所指向的 HTTP 服务器(原服务器),接收 HTTP 服务器的响应报文,并 将响应报文转发给对应的客户进行浏览。
(2) 设计并实现一个支持 Cache 功能的 HTTP 代理服务器。要求能缓 存原服务器响应的对象,并能够通过修改请求报文(添加 if-modified-since 头行),向原服务器确认缓存对象是否是最新版本。(选作内容,加分项 目,可以当堂完成或课下完成)
(3) 扩展 HTTP 代理服务器,支持如下功能: (选作内容,加分项目, 可以当堂完成或课下完成)
a) 网站过滤:允许/不允许访问某些网站;
b) 用户过滤:支持/不支持某些用户访问外部网站;
c) 网站引导:将用户对某个网站的访问引导至一个模拟网站(钓 鱼)。
而且实验指导书上还给出了 200 来行的代码作为参考,可以说是很贴心了。但关键问题不是不知道原理,而是对 socket 编程是相当地陌生,还好代码大部分都能看懂,看不懂的查询一下也能搞定。本实验基本功能还是很好做的,主要就是 cache 的实现,我大部分时间就在搞这个,最后东拼西凑的,也算是搞出来了(虽然外部存储有时会乱码)。
实验代码在我 github 上,想参考的话,可以点击这里 ,欢迎来提各种建议,虽然我也不一定会去改,但还是希望这个代码会越来越好。
实验配置问题
首先要说的就是环境问题,由于我是使用的 CodeBlocks 进行编译的,因此,有时候会出现一些莫名的问题。这里简单介绍一下。
最大的问题就是静态链接问题,也就是这段代码
#pragma comment(lib,"Ws2_32.lib")
,在 VS 里可以很好地运行,但是在 CodeBlocks 中就失去作用了。这段代码也很简单,就是说要链接一个库,但是Codeblocks 使用的是 MingGW 来编译,MingGW不支持#pragma comment(lib,"Ws2_32.lib")
的写法。
解决方法也是很简单,由于该命令是静态链接 Ws2_32.lib 库,因此可以在设置里,加上 -lws2_32 或 -lwsock32,具体怎么加,这里就不讲了。第二个问题也是编译器的问题,由于版本问题,这里并不支持
int _tmain(int argc, _TCHAR* argv[])
的写法,需要改成int main(int argc, char* argv[])
或者直接写成int main()
,其实没有什么区别。具体原因,参考 main()和_tmain(int argc, _TCHAR* argv[]) 的详细区别 和 c/c++ int _tmain(int argc, _TCHAR* argv[])再就是 goto 语句的问题了,代码一直报 goto 语句的问题,不常用这个,我也是很懵啊,不过,还好前辈们有经验分享,具体原因可以参考这个: g++编译goto语句出现:[error:jump to label XXX],简单地说,就是你的 goto 语句之后不能再定义新的变量。
再就是关于strtok_s的问题了,可以参考这篇 stackoverflow :关于strtok_s的问题,就是说,只要改成 strtok() 这个函数就可以了。再去掉最后一个参数,因为这个函数只需要俩参数。虽然这个函数并不安全,但它可以用啊。
大点的问题就这些,还有一些小的问题,比如 VS 里专用的
#include "stdafx.h"
,要去掉,可以参考 为什么要加#include “stdafx.h” ,剩下的,大都没有详细说的必要了
好了,bug 就算是修复完了,现在就可以正常访问网站了:
运行程序 --> 打开浏览器 --> 设置代理 --> 设置 127.0.0.1 和端口号 1240
这样就实现了一个基本的代理服务器,其实现在就已经完成第一个要求了。但你还不知道它的原理是什么,所以,下面看一下它的原理。
实现一个基本的代理服务器
在继续往下看之前,你最好对这几个函数有一定的了解:
- bind() : 将一本地地址与一套接字捆绑,在 connect() 或 listen() 调用前使用
- listen() : 监听套接字的连接请求,将套接字设为监听模式
- connect() : 用于建立与指定 socket 的连接
- accept() : 在一个套接字处,接受一个连接
- send() : 发送数据(客户端向服务器发送请求,服务器端向客户端发送应答)
- recv() : 接收数据
更详细的可以自行去百度查找,这里就不多介绍了。先来看代理服务器的原理:
- 首先初始化一个套接字,利用 blind() 函数将该套接字与服务器 host 地址绑定,地址设为 “127.0.0.1”;同时,也要绑定端口号,这里就按照指导书上的要求设置为 “10240”。然后,利用 listen() 函数对该端口进行监听。
- 通过设置 accept() 函数,对每个到来的请求进行接收和相应,为了提供效率,对每个请求都创建一个新的线程来处理。
- 利用 recv() 和 send() 函数,接收来自客户端的 HTTP 请求,并通过这个代理服务器将该请求转发给服务器;同时,服务器也将获得的响应发给代理服务器,然后代理服务器再将该响应发送给客户端。在这里,代理服务器相当于一个中介,提供一个代理的服务,所有的请求和响应都经过它。
- 处理完成后,等待 200 ms 后,关闭该线程,并清理缓存,然后继续接收并处理下一个请求。对于客户端而言,它只要将正常发送的请求发给代理服务器,就可以接收到对应的响应。
用流程图可以表示为:
我个人觉得,这张流程图非常容易理解,基本上就是这段代码的逻辑,对于理解这段代码很有帮助。
扩展功能
对于这三个扩展功能,只要看懂了代码是如何解析并存储的 HTTP 头部信息,写这三个功能还是很简单的。不需要增加多少代码,只需进行 if 判断即可。
屏蔽网站
对请求过来的 HTTP 报文头部进行检测,提取出其中的访问地址 url ,检测其是否为要被屏蔽的网址,如果是,则直接跳转到代码中的 erro 部分,即关闭套接字,断开此次连接。代码片段如下:
if (strcmp (httpHeader->url, INVILID_WEBSITE) == 0) {printf("\n=====================================\n\n");printf("-------------Sorry!!!该网站已被屏蔽----------------\n");goto error;
}
屏蔽用户
更改套接字绑定的主机地址,这样的话,只要不是从该地址访问代理服务器的客户端,都会被该代理服务器屏蔽,部分代码如下:
//屏蔽用户
//ProxyServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;
ProxyServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//仅本机用户可访问服务器
//ProxyServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.2"); //屏蔽用户
其实,就是更改套接字绑定的代理服务器的 IP 地址,这样的话,就会屏蔽掉从其他接口进行访问客户端,从而实现用户屏蔽。
钓鱼
检测请求过来的 HTTP 报文头部,如果发现访问的网址是要被钓鱼的网址,则将该网址引导到其他网站(钓鱼网址),通过更改 HTTP 头部字段的 url (访问网址)和 host 主机名来实现,部分代码如下:
if (strstr(httpHeader->url, FISHING_WEB_SRC) != NULL) {printf("\n=====================================\n\n");printf("-------------已从源网址:%s 转到 目的网址 :%s ----------------\n", FISHING_WEB_SRC,FISHING_WEB_DEST);memcpy(httpHeader->host, FISHING_WEB_HOST, strlen(FISHING_WEB_HOST) + 1);memcpy(httpHeader->url, FISHING_WEB_DEST, strlen(FISHING_WEB_DEST));
}
cache 实现
cache 可以说是这个实验最精髓的地方了,原理很简单,比较容易理解,但代码写起来还是比较长的,起码比前几个实现起来要复杂。我也是参考了很多前辈们的代码才写出来的,这里就简单介绍一下原理吧,代码自己去看我的实现吧,前面已经给了链接,这里再补充一下:实验一
基本原理
- 客户端第一次请求服务器中的数据时,代理服务器将该请求返回的响应缓存下来,存到本地的文件下。
- 当客户端第二次访问该数据时,代理服务器检查本地是否有该请求的响应,如果没有,则继续缓存;如果有,则向服务器发送一个请求,该请求需要增加 “If-Modified-Since” 字段,通过此字段,告知服务器缓存资源最后修改的时间(可以将 “Date” 字段进行解析),服务器通过对比最后修改时间来判断缓存是否过期,如果没过期,服务器返回状态码304,代理服务器直接将本地缓存发送给客户端;如果缓存过期,服务器返回状态码200,同时返回一个更新过的响应,代理服务器接收后,将该响应发回给客户端,并更新本地缓存。
这一部分的代码虽然代码稍微多一些,但其实也没多少,而且原理很简单,不需要害怕,大胆去写就好了。
总结
这次实验对理解 HTTP 代理服务器还是很有帮助的,真正体会到了代理服务器的作用。虽然调试的时候会出来一堆莫名的 bug, 但是改好后的感觉还是相当不错的。
HTTP 代理服务器的设计与实现相关推荐
- 计算机网络课程设计之网络代理服务器的设计与实现
前言 本实验因为时间有限,写的比较草率... 白嫖容易,创作不易,本文原创,转载请注明!!! 源码和可运行程序: 链接:https://pan.baidu.com/s/1A9KctmpP2JJgyW2 ...
- 7、网络代理服务器的设计与实现
一.设计题目 7.网络代理服务器的设计与实现 二.设计内容 实现一个简易的 proxy 程序.proxy 程序的功能:能够做"二传手"的工作.它自身处在能同时连通外界目标服务器和我 ...
- HTTP代理服务器的设计与实现
一.前言 这个实验挺麻烦的,本来就只有一周时间,等我开始着手准备的时候,也就剩两三天了,也没什么心情自己重头开打代码,于是就找了大佬的代码借鉴了一下,在验收之前处理完了所有的bug.不过,我觉得这个实 ...
- 网络代理服务器的设计与实现
功能需求 实现一个简易的 proxy 程序.proxy 程序的功能:能够做"二传手"的工作.它自身处在能同时连通外界目标服务器和我的机器的位置上.我的机器把请求发送给它,它接受请求 ...
- 计算机网络实验1:HTTP 代理服务器的设计与实现
目录 一.修改并理解参考代码 1.修改错误 2.基本代理服务器原理 二.附加功能的实现 1.网站过滤:允许/不允许访问某些网站 2.用户过滤:支持/不支持某些用户访问外部网站 3.网站引导:将用户对某 ...
- 计算机网络--http代理服务器的设计与实现
一.Socket编程的客户端和服务端的主要步骤: Java Socket编程:对于http传输协议 客户端: 1.创建新的socket,绑定服务器host和端口号 2.Socket创建成功后获得相应的 ...
- (计网实验1)HTTP 代理服务器的设计与实现
目录 教训 参考知识 代理服务器定义 代理服务器原理 单用户代理服务器 多用户代理服务器 实验目的 实验内容 实验过程 设置浏览器代理 实现一个基本的HTTP代理服务器 Cache功能 实现扩展功能 ...
- 计算机网络课程设计之简单 Web Server 程序的设计与实现
前言 本实验是实现一个简易的webserver,我们一直在访问网站,甚至还做过Web课程设计,部署过Tomcat等等,所以说这次实验能更深入的了解其原理 白嫖容易,创作不易,本文原创,转载请注明!!! ...
- 计算机网络课程设计之TELNET 终端设计与实现
前言 Telnet设计是一个比较麻烦的东东,因为Telnet服务器需要部署,而且网络上的资料比较少,最后通过在云服务器CentOS上安装Telnet服务器然后自己的程序作为一个Telnet客户端测试成 ...
- 计算机网络课程设计之电子邮件客户端程序设计与实现
前言 本实验主要是用smtp协议发邮件,收邮件要用到POP3,时间有限,只实现了发邮件,实现过程放在总体设计中 白嫖容易,创作不易,本文原创,转载请注明!!! 源码和可运行程序: 链接:https:/ ...
最新文章
- whereis php,Linux命令教程之比较搜索命令whereis与which的区别
- Spring Security原理之springSecurityFilterChain
- 巴塞尔新资本协议_《巴塞尔公约》修订!进口再生颗粒或对中国产生巨大冲击...
- minio下载及安装:win linux平台
- 每天一道LeetCode----从数组中选择若干不连续元素使得总和最大
- mysql8.0.19.0_分享MySql8.0.19 安装采坑记录
- 【DICOMDIR专题】DICOMDIR基础知识及常见问题汇总
- Centos 7 安装 memcached
- java工程开发之图形化界面之(第六课)
- ActiveMQ的消息的(含附件)发送和接收使用
- 计算机基础远程教育答案,浙大远程教育2013年计算机作业答案-1-计算机基础知识题.docx...
- Servlet做Controller,实现一个类处理多个请求
- oracle 7天密码过期,oracle密码过期ORA-28002: 7天之后口令将过期的解决方法
- 车辆路径问题的基本操作
- 计算机视觉项目实战-驾驶员疲劳检测
- atv320说明书_施耐德变频器参数设置ATV320学习资料
- html+抽奖游戏,九宫格抽奖HTML+JS版
- 2013中国互联网公司、全球互联网公司最新市值排名(2013.04.20)
- JAVAEE工程师入门技术之第1课day01_Java基础语法HelloWorld
- iOS Masonry详解mas_makeConstraints() 添加约束 mas_remakeConstraints() 移除之前的约束,重新添加新的约束 mas_updateConst