计算机网络实验1:HTTP 代理服务器的设计与实现
目录
一、修改并理解参考代码
1.修改错误
2.基本代理服务器原理
二、附加功能的实现
1.网站过滤:允许/不允许访问某些网站
2.用户过滤:支持/不支持某些用户访问外部网站
3.网站引导:将用户对某个网站的访问引导至一个模拟网站(钓鱼)
4.cache实现
原理
实现过程
三、实验演示结果
1.基础功能
2.Cache功能
3.屏蔽网站
4.屏蔽用户
5.钓鱼功能
一、修改并理解参考代码
1.修改错误
- 提示#include "stdafx.h"找不到并且产生大量错误 ,比如HttpHeader有问题之类
解决方法:
本人使用VS2019,直接将这个头文件从网上下载了一份,放入VS的头文件目录下,在项目上右键打开选项,我的目录是D:\Windows Kits\10\Include\10.0.19041.0\um
另一点需要注意的是在VS中把源文件的后缀改为cpp而不是c
- 提示 E0546 控制传输跳过的实例化:
goto语句的问题,不能在goto语句后面还增加定义,否则会认为没有初始化而报错
解决方法:
把定义语句 HttpHeader* httpHeader = new HttpHeader(); 放在goto之前的位置即可
- 运行报错'gethostbyname': Use getaddrinfo() or GetAddrInfoW) instead or define
_WINSOCK_DEPRECATED_NO_WARNINGS to disable deprecated APl warnings
解决方法:
打开项目属性,编辑预处理器定义,增加 _WINSOCK_DEPRECATED_NO_WARNINGS,如图:
- 运行时提示错误:LNK2019无法解析的外部符号WinMain,该符号在函数"int _cdeclinvoke_main(void)" (?invoke_main@@YAHXZ)中被引用
这个问题似乎是c语言运行时找不到适当的程序入口函数,也就是主函数和当前项目不匹配的问题。
解决方法:
将主函数int _tmain(int argc, char * argv[])
直接改为int main()
并且解决方案平台一定选择x64
- VS2019输出控制台中文乱码
记事本里改为ANSI格式,因为VS2019控制台的编码是GBK,而文件可能是UTF-8格式
这里折腾了很久,是因为原来安过的一个拓展,每次强行把文件变为UTF-8,把拓展禁用就好了。
2.基本代理服务器原理
①首先初始化一个套接字(代码中的BOOL InitSocket()函数)
WSAStartup(wVersionRequested, &wsaData);
ProxyServer = socket(AF_INET, SOCK_STREAM, 0); //Socket创建套接字
bind(ProxyServer, (SOCKADDR*)&ProxyServerAddr, sizeof(SOCKADDR)
listen(ProxyServer, SOMAXCONN)
利用 bind() 函数将该套接字与服务器 host 地址绑定,host 地址设为 “127.0.0.1”;
绑定端口号(例如:“10240”)
然后,利用 listen() 函数对该端口进行监听。
②通过accept() 函数,对每个报文的到来请求进行接收和相应,为了提供效率,对每个请求都创建一个新的线程来处理。代理服务器一直循环此段代码,达到不断监听的效果
acceptSocket = accept(ProxyServer, (SOCKADDR*)&acceptAddr, NULL);
lpProxyParam = new ProxyParam;
lpProxyParam->clientSocket = acceptSocket;
//创建新线程
hThread = (HANDLE)_beginthreadex(NULL, 0, &ProxyThread, (LPVOID)lpProxyParam, 0, 0);
CloseHandle(hThread);
Sleep(200);
④接下来进入线程执行函数unsigned int __stdcall ProxyThread(LPVOID lpParameter)
利用 recv() 和 send() 函数,接收来自客户端的 HTTP 请求,并通过代理服务器将该请求转发给服务器;同时,服务器也将获得的响应发给代理服务器,然后代理服务器再将该响应发送给客户端。在这里,代理服务器相当于一个中介,提供一个代理的服务,所有的请求和响应都经过它。
/*接收客户机传来的请求 参数一:指定接收端套接字描述符;参数二:指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;参数三:指明buf的长度;参数四 :一般置为0。*/recvSize = recv(((ProxyParam*)lpParameter)->clientSocket, Buffer, MAXSIZE, 0);ParseHttpHead(CacheBuffer, httpHeader); //HTTP报文分析函数,通过分割字符串获得url,host等信息//将客户端发送的 HTTP 数据报文直接转发给目标服务器ret = send(((ProxyParam*)lpParameter)->serverSocket, Buffer, strlen(Buffer) + 1, 0);printf("成功向目标服务器转发HTTP报文\n");//等待目标服务器返回数据recvSize = recv(((ProxyParam*)lpParameter)->serverSocket, Buffer, MAXSIZE, 0);//将目标服务器返回的数据直接转发给客户端ret = send(((ProxyParam*)lpParameter)->clientSocket, Buffer, sizeof(Buffer), 0);
④处理完成或发生错误时,进入error,代理服务器等待 200 ms 后,关闭该线程,并清理缓存,然后继续接收并处理下一个请求。
//错误处理
error:printf("关闭套接字\n");Sleep(200);closesocket(((ProxyParam*)lpParameter)->clientSocket);closesocket(((ProxyParam*)lpParameter)->serverSocket);delete lpParameter;_endthreadex(0);return 0;
}
⑤解析HTTP报文头部的函数void ParseHttpHead(char* buffer, HttpHeader* httpHeader)
函数的核心就是strtok_s()的字符串分割函数
strtok_s(buffer, delim, &ptr);
//线程安全的字符串分割函数,提取第一行,剩余的字符串在&ptr
/*首次调用时,s必须指向要分解的字符串,随后调用要把s设成NULL。
strtok在s中查找包含在delim中的字符并用NULL('\0')来替换,直到找遍整个字符串。返回指向下一个标记串。
当没有标记串时则返回空字符NULL。*/
二、附加功能的实现
1.网站过滤:允许/不允许访问某些网站
简单的用宏定义拦截4399网站(这样做到同时拦截多个网站比较麻烦,事实上采用char[]较为合理,后面的网站屏蔽钓鱼均用了改进的方法),如果httpHeader->url和INVALID_WEBSITE相同,说明当前访问的是被屏蔽的网站,接下来不连接目标服务器,直接goto error
#define INVALID_WEBSITE "http://www.4399.com/" //被屏蔽的网站
//************************************
//Method : ForbidInvalidWebsite
//FullName: ForbidInvalidWebsite
//Access: public
//Return : bool true为不是屏蔽网站,false为已屏蔽
//Qualifier:网站过滤,不允许访问某些网站
//Parameter: HttpHeader* httpHeader
//************************************
bool ForbidInvalidWebsite(HttpHeader* httpHeader)
{if (!strcmp(httpHeader->url, INVALID_WEBSITE)){printf("******************************************\n");printf("此网站%s 已被屏蔽!\n", INVALID_WEBSITE);printf("******************************************\n");return false;}return true;
}
2.用户过滤:支持/不支持某些用户访问外部网站
采用了const char* InvalidIP[10] = {" "}的结构存储被屏蔽IP,这样可以屏蔽多个用户
修改accept函数的第二个参数为 (SOCKADDR*)&acceptAddr,获取用户的IP。然后通过字符串匹配,判断IP是不是在禁用IP表内,如果在,就goto error
const char* InvalidIP[10] = {""}; //被屏蔽的用户IP,以127.0.0.1为例
const int IPnum = 0;
SOCKADDR_IN acceptAddr; //自定义变量,用来获得用户的IP
//监听连接请求,创建新套接字
//获取用户IP地址:inet_ntoa(acceptAddr.sin_addr));
acceptSocket = accept(ProxyServer, (SOCKADDR*)&acceptAddr, NULL);//用户过滤:支持/不支持某些用户访问外部网站
if (ForbidInvalidUser(inet_ntoa(acceptAddr.sin_addr)))
{closesocket(acceptSocket);
}//************************************
//Method : ForbidInvalidUser
//FullName: ForbidInvalidUser
//Access: public
//Return : bool true说明已屏蔽该用户
//Qualifier:用户过滤:支持/不支持某些用户访问外部网站 禁用客户IP表是全局变量
//Parameter: char* userIP 用户IP指针
//************************************
bool ForbidInvalidUser(char* userIP)
{for (int i = 0; i < IPnum; i++){if (strcmp(userIP,InvalidIP[i])){//用户IP在禁用IP表中printf("******************************************\n");printf("此用户ID = %s 已被屏蔽!\n", userIP);printf("******************************************\n");return true;}}return false;
}
3.网站引导:将用户对某个网站的访问引导至一个模拟网站(钓鱼)
匹配URL和前两个类似。重点是,修改 httpHeader->host 和 httpHeader->url 为钓鱼的目标网站。
//************************************
//Method : GoFishing
//FullName: GoFishing
//Access: public
//Return : void
//Qualifier:网站引导:将用户对某个网站的访问引导至一个模拟网站(钓鱼)
//Parameter: HttpHeader* httpHeader
//************************************
void GoFishing(HttpHeader* httpHeader)
{for (int i = 0; i < FishingSrcNum; i++){if (strstr(httpHeader->url, FishingSrcURL[i])){//访问的url在钓鱼源网址列表中printf("******************************************\n");printf("已从源网址:%s 转到 目的网址 :%s\n", httpHeader->url,FishingDestURL[i]);printf("******************************************\n");memcpy(httpHeader->host, FISHING_WEB_HOST, strlen(FISHING_WEB_HOST) + 1);memcpy(httpHeader->url, FishingDestURL[i], strlen(FishingDestURL[i]));}}
}
4.cache实现
要求能缓存原服务器响应的对象,并能够通过修改请求报文(添加 if-modified-since头行),向原服务器确认缓存对象是否是最新版本。
原理
浏览器向代理服务器发送所有的HTTP请求
代理服务器收到客户端的HTTP请求后,检查本地Cache是否有该请求的URL
如果缓存未命中,就直接向目标服务器发送HTTP请求,获取对象,将该请求返回的响应缓存下来,存到本地的Cache下
如果缓存命中,代理服务器则向目标服务器发送一个请求,该请求需要增加 “If-Modified-Since” 字段,通过此字段,告知目标服务器缓存资源最后修改的时间,服务器通过对比最后修改时间来判断缓存是否过期。如果没过期,服务器返回状态码304,代理服务器直接将本地缓存发送给客户端;如果缓存过期,服务器返回状态码200,同时返回一个更新过的响应,代理服务器接收后,将该响应发回给客户端,并更新本地缓存
实现过程
①int ParseHttpHead_AddCache(char* buffer, HttpHeader* httpHeader)
解析 TCP 报文中的 HTTP 头部,检验是否cache命中
相比于原来的解析报文,在更新完httpHeader后,增加了搜索url是否在cache的功能
分为缓存命中 / 缓存未命中,存入缓存 / 缓存未命中,缓存已满,覆盖开头缓存
②void ParseCache(char* buffer, char* status, char* last_modified)
解析 TCP 报文中的 HTTP 头部,在缓存命中的时候使用,获取status和last_modified信息
如果服务器返回状态码为304,说明数据没有被修改,将缓存的数据直接转发给客户端
如果服务器返回状态码为200,内容已经修改了,更新缓存中的内容
三、实验演示结果
1.基础功能
打开代理功能,将地址和端口设置为127.0.0.1和10240
此时未运行代理服务器程序时,无法正确加载网页
此时运行代理服务器,以本科生院为例,可以正确访问界面,并获得反馈
2.Cache功能
多刷新几次页面,会发现本科生院主页的内容不再来源于服务器,而是来自缓存
在将内容转发给客户端前设置断点,观察cache的内容,可以看到缓存了网页的内容
3.屏蔽网站
屏蔽4399
4.屏蔽用户
以本机127.0.0.1为例,加入到屏蔽用户列表后,无法访问任何网页
5.钓鱼功能
从今日哈工大钓鱼到教务处网站
计算机网络实验1:HTTP 代理服务器的设计与实现相关推荐
- 计算机网络课程设计之网络代理服务器的设计与实现
前言 本实验因为时间有限,写的比较草率... 白嫖容易,创作不易,本文原创,转载请注明!!! 源码和可运行程序: 链接:https://pan.baidu.com/s/1A9KctmpP2JJgyW2 ...
- 计算机网络实验仿真系统设计,计算机网络实验课程仿真系统平台的研究与设计...
摘要: 随着计算机技术和网络技术不断发展,<计算机网络>课程成为高校许多工科专业的骨干必修课程.课程涉及到的知识内容主要以理论教学为基础,以实验实践为辅助教学.由于受实验室建设资金.网络环 ...
- 计算机网络教学仿真平台,计算机网络实验课程仿真系统平台的研究与设计
摘要: 随着计算机技术和网络技术不断发展,<计算机网络>课程成为高校许多工科专业的骨干必修课程.课程涉及到的知识内容主要以理论教学为基础,以实验实践为辅助教学.由于受实验室建设资金,网络环 ...
- 计算机网络抓包设计,计算机网络实验利用wireshark抓包工具抓包
计算机网络实验利用wireshark抓包工具抓包 计算机网络实验[利用wireshark抓包工具抓包] 一.实验名称 使用网络协议分析仪 Wireshark 二.实验目的 1.掌握安装和配置网络协议分 ...
- 计算机网络实验-实验四 无线网络的设计
实验四 无线网络的设计 本次实验最应该注意的便是网关的作用 实验四 无线网络的设计 1.首先明确概念总共有两个路由器,一个是无线路由器,另一个是有线路由器.所以有线路由器这边直连交换机,交换机再连接A ...
- 江苏大学计算机网络设计,江苏大学计算机网络实验报告
计算机网络实验报告 班级:网络工程1102班姓名:董永 学号:3110610055 配置一个典型的网络系统 1 实验目的 在实验中,我们将学习如何配置一个典型的网络系统,其中硬件设施使用到路由器.三层 ...
- HTTP代理服务器的设计与实现
一.前言 这个实验挺麻烦的,本来就只有一周时间,等我开始着手准备的时候,也就剩两三天了,也没什么心情自己重头开打代码,于是就找了大佬的代码借鉴了一下,在验收之前处理完了所有的bug.不过,我觉得这个实 ...
- 计算机虚拟网络毕业论文,计算机毕业论文——基于WEB的虚拟计算机网络实验平台.doc...
PAGE Tianjin University of Technology and Education 毕 业 设 计 专 业: 计算机科学与技术 班级学号: 计0203班 – 11 学生姓名: 指导 ...
- 计算机网络技术教法改革方案,计算机网络实验论文,关于“计算机网络”教学改革相关参考文献资料-免费论文范文...
导读:本文是一篇计算机网络实验论文范文,可作为选题参考. (西南科技大学国防科技学院) 摘 要:"计算机网络"是一门理论与实践性都很强的课程,针对该课程存在教学模式与教学方法陈旧. ...
- 计算机网络平台实验,计算机网络实验
课程简介 "计算机网络实验"课程是计算机学院三年级本科生实践类必修课.本课程主要配合计算机网络课程,通过实验帮助学生学习和理解网络原理与协议,培养学生的动手能力.工程实践能力和综合 ...
最新文章
- MCMC笔记:蒙特卡罗方法
- linux提高nand速度,linux-2.6.31.1内核支持Nand Flash
- 【示例】Lucene查询索引库编程步骤
- php云点播源码,乐视云直播 点播服务端api
- 使用LINQ遇到的问题,请高手解答下原理
- 它决定支付30万美元的勒索金
- innoDB索引使用和优化汇总
- stm32F10x 看程序知识点记录
- gma 教程 | 气候气象 | 计算标准化降水指数(SPI)
- Springboot数据库配置文件明文密码加密解密
- windows之电脑开机出现 this product is covered by one or more of the following prtents
- iptables: No config file解决方法
- 大学数学建模大赛是用计算机,全国大学生数学建模大赛
- 泛型-IMPORTANT
- python能代替ps吗_Python中怎么像PS一样处理图像
- for循环打印九九乘法表
- Echarts折线图曲线图和三维图
- dnf手游服务器维护时效,DNF手游延期到2021年2月11日是真的吗 延期日期详细说明...
- [经济学原理|政治部分]剩余价值理论
- 第十三届蓝桥杯大赛软件赛省赛 Java 研究生组