计算机系统基础实训八—ProxyLab实验

news2025/2/8 23:03:11

实验目的与要求

1、让学生应用套接字接口实现网络编程;

2、让学生理解Web服务器开发的相关知识;

3、让学生应用并发编程技术进行并发服务器的开发;

实验原理与内容

Web代理是一种在Web浏览器和终端服务器之间充当中介角色的程序。在Web代理的帮助下,浏览器不是直接联系终端服务器以获取网页,而是浏览器会首先联系代理,代理会向终端服务器转发请求,当终端服务器响应代理时,代理会将响应发送到浏览器。

代理有多种用途,有时可以在防火墙中使用代理,使得防火墙只能通过代理联系防火墙以外的服务器。代理还可以使客户端匿名,通过剥离请求的所有标识信息,代理可以使浏览器对Web服务器匿名。代理甚至可以通过将来自服务器的对象存储到本地来实现缓存,后续的请求可以直接从缓存中获取Web对象而不需要再次与远程服务器通信。

3.1第一部分:实现顺序的Web代理程序

第一步是实现一个处理HTTP/1.0 GET请求的简单顺序代理程序,其它的请求类型(如POST等)不作要求。在代理程序启动时,程序将在命令行参数指定的端口上侦听连接请求。一旦建立了连接,您的代理程序应该读取整个HTTP请求并对请求进行解析。它需要判断客户端是否发送了有效的HTTP请求。如果HTTP请求有效,则建立自己到相应Web服务器的连接,然后向服务器请求客户端所指定的对象。最后代理程序读取服务器的响应并将其转发给客户端。

3.1.1 HTTP/1.0 GET requests

当终端用户在Web浏览器的地址栏中输入URL时,例如http://www.cmu.edu/hub/index.html,浏览器将向代理程序发送HTTP请求,该请求会以类似于以下的内容作为请求行:

GET http://www.cmu.edu/hub/index.html  HTTP/1.1

在这种情况下,代理程序应该将请求解析为至少以下字段:主机名(www.cmu.edu)和其后面的路径或查询的内容(/hub/index.html)。这样,代理程序知道自己需要打开到www.cmu.edu的连接并以以下的形式发送自己的HTTP请求:

GET /hub/index.html HTTP/1.0

请注意,HTTP请求中的所有行都要以回车符“\r\n”结尾。而且重要的是,每个HTTP请求都要以空行“\r\n”终止。

在上面的示例中,您应该注意到Web浏览器的请求行以HTTP/1.1结尾,而代理程序的请求行以HTTP/1.0结尾。现代Web浏览器会生成HTTP/1.1请求,但是您的代理程序应该处理它们并将它们以HTTP/1.0的方式进行转发。

需要认识到的是,HTTP请求,即使只是HTTP/1.0 GET请求,也可以复杂到难以置信。书本描述了HTTP事务的某些细节,但如果您想获取完整的HTTP/1.0规范则应该参考RFC 1945。在理想情况下,代理程序应该可以解析所有的HTTP请求,不过在本实验中您的代理程序不要求处理多个请求行的情况。不过您的代理程序绝不能因请求格式错误而终止。

3.1.2 请求头

在本实验中重要的请求头是:Host、User-Agent、Connection和Proxy-Connection。

1、始终发送Host请求头,虽然HTTP/1.0规范中在技术上不支持这种行为,但在某些Web服务器中有必要诱使其做出合理的响应,尤其是那些使用虚拟主机的服务器。

Host请求头描述终端服务器的主机名。例如,访问http://www.cmu.edu/hub/index.html,您的代理程序应该发送以下请求头:

Host: www.cmu.edu

Web浏览器可能会将自己的Host请求头附加到HTTP请求。如果是这样的话,代理程序应使用与浏览器相同的Host请求头。

2、您可以选择始终发送以下User-Agent请求头:

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3)Gecko/20120305 Firefox/10.0.3

代理程序应将请求头作为单行发送。User-Agent请求头用来标识客户端(什么操作系统、什么浏览器等),而Web服务器通常使用请求头所标识的信息来组织其返回的信息。发送这一User-Agent请求头,可以使得返回的字符串在内容和格式上有所改进,在telnet测试中非常有用。

始终发送以下的Connection请求头:Connection: close

永远发送以下Proxy-Connection请求头:Proxy-Connection: close

Connection和Proxy-Connection请求头用于指明连接在第一次请求/响应完成后是否保持活动状态。本实验强烈建议您的代理程序为每个请求打开一个新的连接。把这些请求头的值指定为“close”会提醒Web服务器您的代理程序会在每一次请求/响应后关闭连接。

为了方便起见,在proxy.c文件中以字符串常量的方式为您提供了所述的User-Agent请求头的值。

最后,如果浏览器发送的HTTP请求里面添加了其它的一些额外的请求头,那么您的代理程序应该不做任何更改地转发它们。

3.1.3 端口号

本实验有两类重要的端口号:HTTP请求的端口号和代理程序监听的端口号。

HTTP请求的端口号是HTTP请求中URL里的可选字段。也就是说,URL可能是这种形式:http://www.cmu.edu:8080/hub/index.html。在这种情况下,代理程序应该连接主机www.cmu.edu的8080端口,而不是默认的80端口。您的代理无论URL中是否包含端口号,都必须正常工作。

监听端口号是代理程序监听连接请求的端口号。监听端口号以命令行参数的方式传入代理程序。例如在命令行以以下的命令启动代理程序,代理程序会监听端口15213上的连接:

linux> ./proxy 15213

您可以使用任何未被其它进程使用的非特权监听端口号(大于1024且小于65536)。由于每个代理程序必须使用唯一的监听端口号,而且每台机器上会有许多进程同时在工作,因此我们提供了一个port-for-user.pl脚本来帮助您选择端口号。带上您的用户ID来执行该脚本可以帮您生成端口号:

linux> ./port-for-user.pl droh

droh: 45806

由脚本port-for-user.pl返回的端口号p始终是偶数的,所以如果您需要额外的端口号的话,例如给Tiny server使用,那么您可以安全地使用端口p和p+1。

请不要自己随机选一个端口号,如果这样做,有可能干扰到其他用户。

3.2 第二部分:处理多个并发请求

一旦有了一个正常工作的顺序代理程序,就可以对其进行修改,以同时处理多个请求。实现并发服务器的最简单方法是为每个新连接请求都生成一个新线程来处理,当然也可以使用书本上提到的其它方法来进行实现。

  1. 请注意,线程应该在分离模式下运行,以避免内存泄漏。
  2. 书本中描述的open_clientfd和open_listenfd函数是基于现代且与协议无关的getaddrinfo函数的,因此是线程安全的。

3.3 第三部分:缓存Web对象

在本实验的最后一部分中,您将向代理程序添加缓存功能,用于在内存中存储最近使用的Web对象。HTTP实际上定义了一个相当复杂的模型,通过该模型,Web服务器可以指示如何缓存它们所提供的对象,客户端可以指定如何使用这些缓存。但是,您的代理程序将采用简化的方法。

当代理程序从服务器接收到Web对象时,它应该在给客户端传输该对象时将其缓存在内存中。如果另一个客户端向同一服务器请求相同的对象,则代理程序不需要重新连接到服务器,它只要简单地将缓存的对象发回给客户端即可。

显然,如果您的代理程序要缓存所有被请求的对象,那么它将需要无限的内存。此外,由于某些Web对象比其它对象大,因此可能会出现以下情况:一个巨大的对象消耗了整个缓存,从而根本无法缓存其它对象。为了避免这些问题,代理程序应该同时规定最大缓存容量和最大缓存对象尺寸。

3.3.1 最大缓存容量

代理程序的整个缓存应具有以下最大容量:MAX_CACHE_SIZE = 1 MB

当累计缓存的大小时,代理程序应该仅计算用于存储实际Web对象的字节数,其它额外的字节,包括文件的元数据等,应该忽略。

3.3.2 最大对象尺寸

代理程序应仅缓存不超过以下最大尺寸的Web对象:MAX_OBJECT_SIZE = 100 KB

为了方便起见,这两个大小限制都已经在proxy.c文件中以宏的方式进行提供。

正确实现缓存的最简单方法是为每个活动连接都分配缓冲区,并对从服务器接收到的数据进行累计。如果缓冲区的大小超过最大对象尺寸,则丢弃该对象。如果Web服务器的响应没有超过最大对象尺寸则可以缓存该对象。使用此方案,代理程序用于Web对象的最大数据量为如下所示,其中T是最大活动连接数:

MAX_CACHE_SIZE + T * MAX_OBJECT_SIZE

3.3.3 驱逐策略

您的代理程序的缓存应采用最近最少使用(LRU)逐出策略,它不一定要是严格意义上的LRU,但应该要跟LRU相当接近。请注意,对一个对象的读和写都算作是使用该对象。

3.3.4 同步

对缓存的访问必须是线程安全的,并且确保缓存访问不受竞争条件的影响可能是该实验里更有挑战的一个地方。事实上,对缓存有一个特殊的要求,那就是多个线程必须能够同时从缓存中进行读取。当然,一次只能允许有一个线程能够对缓存进行写入,但读取缓存不能存在这种限制。

因此,使用一个大的排它锁来保护对缓存的访问不是一个可取的解决方案。你可能需要探索其它一些新方法,例如对缓存进行分区、使用Pthreads读者-写者锁或使用信号量实现自己的读写方案。无论使用哪种方法,您都不必严格实现LRU逐出策略,这使您在实现缓存时获得一定的灵活性。

在本实验中,您将编写一个可以缓存Web对象的简单HTTP代理程序。对于实验的第一部分,您将开发代理程序以接受连接、读取和分析请求、将请求转发到Web服务器、读取服务器的响应、并将这些响应转发给相应的客户端。在第一部分中您将学习基本的HTTP操作以及如何使用套接字接口编写网络通信程序。在第二部分中,您将对代理程序进行升级以处理多个并发连接。这将使您学习如何处理并发性,这是一个至关重要的系统概念。在最后一部分也就是第三部分,您将为您的代理程序添加缓存功能,使得代理程序可以在内存中简单地缓存最近访问的Web对象。

实验设备与软件环境

1.Linux操作系统—64位 Ubuntu 18.04

2. C编译环境(gcc)

3. 计算机

实验过程与结果(可贴图)

Part I:实现一个顺序的网络代理

先将tiny.c中的基本框架复制过来,移除不需要的函数,保留doit,parse_uri,clienterror即可,其他还用不到,接下来我们需要修改的是doit和parse_uri,doit应该做的事如下:读取客户端的请求行,判断其是否是GET请求,若不是,调用clienterror向客户端打印错误信息;parse_uri调用解析uri,提取出主机名,端口,路径信息。代理作为客户端,连接目标服务器;调用build_request函数构造新的请求报文new_request。

请求报头:

构造新的发送到终端服务器请求:

主函数代码:

Part II:处理多个并发请求

改变上面的程序,使其可以处理多个并发请求,这里使用多线程来实现并发服务器

Accept之后通过创建新的线程来完成doit函数。

注意:由于并发导致的竞争,所以需要注意connfd传入的形式,这里选择将每个已连接描述符分配到它自己的动态分配的内存块。

添加thread函数

Part III:缓存web对象

第三部分需要添加缓存web对象的功能。根据实验文档的要求我们需要实现对缓存实现读写者问题,且缓存的容量有限,当容量不足是,要按照类似LRU算法进行驱逐。我们先定义缓存的结构,这里使用的是双向链表,选择这个数据结构的原因在于LRU算法的需求,链尾即使最近最少使用的web对象。

最大缓存大小

代理的整个缓存应具有以下最大大小:

MAX_CACHE_SIZE = 1 MiB

在计算其缓存的大小时,代理必须只计算用于存储实际web对象的字节;应该忽略任何无关的字节,包括元数据。

首先设置好推荐最大缓存和对象大小,官方已经在文件中帮我们写好了

初始函数

实现读者写者问题,为此定义了如下几个相关变量

同时,定义了reader和writer函数作为读者和写者。

    int reader(int fd, char *url);其内调用get_cacheData检查是否缓存命中,若是,则将所缓存的数据通过fd发送给客户端,否则返回0表示缓存未命中。

void writer(char **url*, char **content*);缓存未命中后,与之前一样进行代理服务,从目标服务器接收数据后发送到客户端,如果web object的大小符号要求的话,再调用writer将接收的数据进行缓存。

最终结果

实验总结

本次实验不仅加深了我对网络编程、Web服务器开发与并发处理、缓存管理技术的深度理解,还充分锻炼了我的实践操作能力。通过亲自设计并实现一个实际的HTTP代理程序,我不仅掌握了协议的精髓,也对线程并发控制、数据结构设计有了更加深入的领悟。这次经历让我在实践中体会到了网络编程的魅力,更让我对HTTP协议的每一个细节有了更细腻的认识,同时并发控制与数据处理技巧的实践让我在实战中得到了锻炼。我将针对本次实验中发现的不足之处,如对特定协议细节的掌握不够熟练、并发策略的优化空间等问题,进行深入学习。我计划进一步深化对HTTP协议的理解,尤其是其进阶层次结构和高级特性,提升对并发处理的策略,如更高效的线程管理、负载均衡。同时,我也会关注数据处理中的安全性,确保在实际应用中既能高效又安全。

本次实验的成果不仅让我在技术上收获颇丰盈满载,也让我对未来的学习规划充满信心满满。我确信,凭借这次的实验经验,我将能以更稳健的脚步,扎实的技能,迈向更为复杂系统的开发挑战,构建出更高效、安全、可靠的应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1850629.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

可灵王炸更新,图生视频、视频续写,最长可达3分钟!Runway 不香了 ...

现在视频大模型有多卷? Runway 刚在6月17号 发布Gen3 ,坐上王座没几天; 可灵就在6月21日中午,重新夺回了王座!发布了图生视频功能,视频续写功能! 一张图概括: 二师兄和团队老师第一…

链表中环的入口节点

链表中环的入口节点 描述 链表中环的入口节点 给一个长度为n链表&#xff0c;若其中包含环&#xff0c;请找出该链表的环的入口结点&#xff0c;否则&#xff0c;返回null。 数据范围&#xff1a; n≤10000&#xff0c; 1<结点值<10000 要求&#xff1a;空间复杂度 O(1)…

剪画音频提取:周杰伦音乐自由听,谁还付费听歌呀!

​作为周杰伦的狂热粉丝&#xff0c;你是否常常为各大音乐软件的会员限制而感到烦恼&#xff1f;每当想听一首心仪的周杰伦歌曲&#xff0c;却被“会员专享”这几个字挡住去路&#xff0c;实在是令人扫兴。但是别担心&#xff0c;今天我将为你揭示一个神奇的方法——把视频提取…

嵌入式通信协议----Wi-Fi协议详解(二)(基于STM32+有人物联网WIFI模块)

四、有人WIFI模块 1.模块介绍 Wi-Fi 模块用于实现串口到 Wi-Fi 数据包的双向透明转发&#xff0c;模块内部完成协议转换&#xff0c;通 过该模块&#xff0c;客户可以将物理设备连接到 Wi-Fi 网络上&#xff0c;从而实现物联网的控制与管理。 2.模块参数 Wi-Fi 模块的…

网格布局之跨行越列

网格布局之跨行越列 欢迎关注&#xff1a;xssy5431 小拾岁月 参考链接&#xff1a;https://mp.weixin.qq.com/s/xStfSmewncTW49N0Y_Vhow 点击查看 使用场景 在常见的页面布局中&#xff0c;我们往往会遇到那种类似合并单元格的布局。比如&#xff1a;成绩排名、产品排名等等…

个体核定征收双免个体户0税率大额核定税率全行业筹划

工商银行的会计政策和程序规定 工商银行会计政策和程序规定 工商银行的会计政策和程序规定 https://www.9733.cn/shop/ssch 一、个体工商户核定征收 核定征收是一种简化税收管理方式&#xff0c;适用于簿记不健全、难以准确核算收入和成本的个体。以下是主要特点&#xff1a…

领先GPT-4o:Anthropic 推出新一代模型 Claude 3.5 Sonnet|TodayAI

Anthropic&#xff0c;全球领先的人工智能实验室之一&#xff0c;近日发布了其最新的人工智能模型——Claude 3.5 Sonnet。该模型不仅速度更快&#xff0c;成本更低&#xff0c;而且在多个关键任务上的表现超过了其前代模型 Claude 3 Opus。 更强的视觉功能与幽默感 Claude 3…

基于Java的农机电招平台系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果你对农机电招平台系统感兴趣或有相关开发需求&#xff0c;可以私信联系我。 开发语言 Java 数据库 MySQL 技术 B/S结构&#xff0c;SpringBoot框架 工具 Eclipse&#xff0c;Navicat&#xff0c;Tomcat8.0 系…

EasyRecovery数据恢复软件2024免费版下载

EasyRecovery数据恢复软件&#xff0c;是我在电脑使用过程中遇到的神器&#xff01;它不仅功能强大&#xff0c;操作简便&#xff0c;还帮我找回了丢失的重要文件。今天&#xff0c;我就来给大家分享一下我的使用体验和心得。 让我来介绍一下EasyRecovery的功能。这款软件可以恢…

CentOS编译安装OpenSSL 3.3.1

正文共&#xff1a;666 字 8 图&#xff0c;预估阅读时间&#xff1a;1 分钟 我们前面介绍了如何通过Windows Server生成证书&#xff08;Windows Server配置生成认证证书&#xff09;&#xff0c;也介绍了如何通过easy-RSA生成证书文件&#xff08;使用Easy-RSA配置生成SSL证书…

2024-6-18(沉默Spring,Springboot)

1.Spring小结 我们最后再来体会一下用 Spring 创建对象的过程&#xff1a; 通过 ApplicationContext 这个 IoC 容器的入口&#xff0c;用它的两个具体的实现子类&#xff0c;从 class path 或者 file path 中读取数据&#xff0c;用 getBean() 获取具体的 bean instance。 那…

【计算机网络仿真】b站湖科大教书匠思科Packet Tracer——实验7 虚拟局域网VLAN

一、实验目的 1.学习如何划分VLAN&#xff1b; 2.验证划分VLAN的作用&#xff1b; 3.学习如何用命令行创建VLAN&#xff0c;将端口划分到VLAN&#xff0c;设置端口类型。 二、实验要求 1.使用Cisco Packet Tracer仿真平台&#xff1b; 2.观看B站湖科大教书匠仿真实验视频&am…

每日优秀影视分享❗❗

一、热门电影推荐 《头脑特工队 2》&#xff1a;皮克斯再次为观众带来了这部经典动画的续集。 影片讲述了刚步入青春期的小女孩莱莉脑海中的复杂情绪进行的一场奇妙冒险。 这部电影不仅延续了前作的优秀品质&#xff0c;更在情感深度和视觉呈现上有了进一步的提升。 《艾尔登…

【球类识别系统】图像识别Python+卷积神经网络算法+人工智能+深度学习+TensorFlow

一、介绍 球类识别系统&#xff0c;本系统使用Python作为主要编程语言&#xff0c;基于TensorFlow搭建ResNet50卷积神经网络算法模型&#xff0c;通过收集 ‘美式足球’, ‘棒球’, ‘篮球’, ‘台球’, ‘保龄球’, ‘板球’, ‘足球’, ‘高尔夫球’, ‘曲棍球’, ‘冰球’,…

openEuler23.09安装Postgresql16.3

openEuler23.09安装Postgresql16.3&#xff0c;基于源代码编译安装PostgreSQL的基本步骤 一、PostgreSQL数据库服务环境搭建 操作系统版本 openEuler-23.09-x86_64-dvd.iso &#xff0c;安装步骤此处省略。。。 最常用且直接的方法来查看openEuler的版本号是查看/etc/os-rel…

[Redis]缓存常见问题解决(缓存穿透、击穿、雪崩一文解决!通俗易懂、代码实战!手把手教你解决缓存问题三兄弟!)

Redis常见问题解决 要求 只用一种缓存技术&#xff0c;从实验点中挑一些试验进行试验原理。 1.缓存原理 目标&#xff1a;理解缓存的基本原理和工作机制。 实验步骤&#xff1a; 阅读各缓存技术机制的文档和官方资料。实现一个简单的应用程序&#xff0c;模拟数据的读写和…

AD导出Gender、坐标文件、BOM

导出Gender 方式一 等输出完成后&#xff0c;将工程文件下的OutPut文件打包发给厂家即可 方式二 导出外观、层 导出孔 导出坐标文件 导出BOM 备注 外观尽量用机械层 参考 https://blog.csdn.net/lwb450921/article/details/123141335

Python发送HTML邮件有哪些步骤?怎么设置?

Python发送HTML邮件如何实现&#xff1f;Python发送邮件的策略&#xff1f; HTML邮件不仅可以包含丰富的文本格式&#xff0c;还可以插入图片、链接和其他多媒体内容&#xff0c;从而提升邮件的美观性和功能性。AokSend将详细介绍Python发送HTML邮件的主要步骤&#xff0c;帮助…

动态规划:基本概念

Dynamic Programming 动态规划&#xff08;Dynamic Programming, DP&#xff09; 是一种算法设计技巧&#xff0c;通常用来解决具有重叠子问题和最优子结构性质的问题。它通过将问题分解为更小的子问题&#xff0c;逐步解决这些子问题并将结果存储起来&#xff0c;以避免重复计…

C++拷贝构造函数、运算符重载函数、赋值运算符重载函数、前置++和后置++重载等的介绍

文章目录 前言一、拷贝构造函数1. 概念2. 特征3. 编译器生成默认拷贝构造函数4. 拷贝构造函数典型使用场景 二、运算符重载函数三、赋值运算符重载函数1. 赋值运算符重载格式2. 赋值运算符只能重载成类的成员函数不能重载成全局函数3.编译器生成一个默认赋值运算符重载4. 运算符…