CSAPP ProxyLab实验报告

Proxylab

1. 实验概述

WedProxy是介于浏览器和远端服务器的桥梁,接收浏览器的请求并发送给服务器,接收服务器响应并返回给浏览器。代理具有以下三个功能:

  1. 建立和某个服务器、客户端的连接,接收客户端请求并发送给服务器,接收服务器响应并返回给浏览器。
  2. 允许多个连接并发执行。
  3. 缓存最近访问的内容。

和前一个实验malloclab相比这个实验不算难,不仅评分宽松、没有考察错误处理外,结构体的使用还可以避免像malloclab那样因为指针误用导致的莫名其妙的问题。虽然这个实验涉及第十章、十一章、十二章,但是可以参照Tiny修改,难度也相应减小。

2. 总体思路

主线程先做些初始化工作,主要是初始化描述符池、各个线程、cache、建立监听端口。然后进入一个循环,监听端口,得到连接请求,并把连接描述符放入描述符池。每个线程尝试从描述符池拿描述符,拿到后与客户端建立连接,接受一条请求,先检查cache中有无缓存对象,有的话直接返回,否则与服务端建立连接,接受服务端响应,把响应内容放入cache

3. 解析request

3.1 思路

从与一个客户端关联的连接端口逐行读取request,需要解析request line得到发给服务器的reques line并确定主机和端口,与某个服务器建立连接后修改请求行,并将要求的头部替换为自定义头部,保留其它头部,把新产生的请求逐行发给服务器。

3.2 问题

存储每个域(如methoduri)的缓冲区多大合适?防止客户端返回的内容过长导致缓存区溢出。

4. 传递文件内容

这一部分需要对课本第十章很熟悉,当连接建立后proxy会得到与客户端相连的描述符和与服务端相连的描述符,和两者交互直接抽象为对文件的读写,书中提出的RIO包可以健壮地用于套接字符的读写.

下面简单介绍这个包:

rio_readn:Unix系统调用read的限定大小版,一定会从指定描述符读取n个字节,阻塞直到读完。

rio_writen:Unix系统调用write的限定大小版,一定会向指定描述符写n个字节,阻塞直到写完。

rio_t:一个用于建立某个描述符与缓冲区联系的结构体,缓冲区的引入使得该结构体除了具有与一般描述符相同的行为(都可以读写,只是要调用定制的函数)外还可以借助缓冲区减少系统调用的次数,提高性能。

下面是与rio_t配套的函数:

rio_readinitb:建立缓冲区与指定描述符间的联系。

rio_read:这是后面几个读有关函数使用的基础函数,调用时若缓冲区为空就会调用read填满缓冲区,否则返回缓冲区中的内容。与read有相同的语义,出错时返回-1,遇到EOF返回0,字节数超过缓冲区内未读字节数量会返回不足值。

rio_readnbrio_read的限定大小版,一定会从指定rio_t读取n个字节,阻塞直到读完。

rio_readlineb:从指定rio_t读取一个文本行,或者读到EOF,或者读完maxlen字节。

不用担心代理使用上面的函数时会发生读取响应不完整的情况,因为rio_readlineb会反复调用rio_read,直到rio_read返回0,也就是遇到EOFEOF就表明响应已经完整发送了。

如何使用RIO包传递文件内容

这个很简单,代理从客户端读,再往服务端写,建立一个rio_t对象与读端关联,反复调用Rio_readlineb读取客户端发来的内容然后用Rio_writen写回内容就行。即使二进制文件没有文本行,但是一定有EOF,起始位置到EOF间的数据都会被正确送回。

5. 实现并发

基于预线程化实现并发。

5.1 线程逻辑

每个线程的逻辑流为:从描述符池中取出一个描述符,接受该描述符对应客户端发来的请求并解析出服务端后与服务端建立连接,再发送连接头部,之后把服务端响应返回给客户端,这一切可以通过一个函数doit完成。

5.2 描述符池

使用书中提供的SBUF包,可以完成对描述符池的抽象(一个有多个消费者一个生产者的缓冲区),但是subf_insert存在问题,如果服务的客户端数量过多缓冲区慢,主线程将一直阻塞,改进建议是先检查slots,如果满了就给客户端返回拒绝请求的消息。

6. cache

6.1 基本结构

每当代理收到服务端发来的数据需要在本地存一个副本,这样当客户端再次要求同样数据时能够直接返回。用一个链表实现cache。按照writeup,应当将对cache的访问视为读写者模型,取操作视为读,插入一个新数据对象视为写。

支持下列基本操作:

  • cache_insert(cache_t *cache, block_t *block)

    cache中插入一个块,需要给cache上写锁,块加到链表末尾。

  • cache_get(cache_t *cache, char *url)

    遍历链表找出对应url的块,并返回内容指针。为了保证该函数是线程安全的,会另外用一段内存区域存块内容并返回内容指针(注意,调用者需要释放内容)。

  • cache_init(cache_t *cache)

    初始化cache。

  • cache_evic(cache_t *cache, int size)

    驱逐块。

6.2 驱逐策略

如果严格使用LRU算法,将不存在读者,因为读数据块同时要把数据块放到链表头部,实际上也要写cache,类似于时钟算法,给每个block一个标识位表示最近是否被访问,如果被访问了或者被加入cache就置为1.

需要驱逐一个块时,从头遍历一遍链表,驱逐第一个标识为0的块,并将标识为1的块的表示置为0.如果遍历一遍没有驱逐一个块就从头开始再遍历一遍,这次一定能找到合适的驱逐块。

7. 问题

除了上面列出的问题外,正确编写错误处理函数也是本次实验的一个重要部分,需要改进的地方包括但不限于:

  • cache.h中函数没有按预期工作时如何提示错误信息且不影响继续运行
  • 客户端数量超过线程数时如何处理

8. 调试

  • 使用函数cache_printblock_print打印不变量。
  • 使用curl,通过proxy连接tiny。具体使用时只用先在合适端口启动proxytiny,然后按照writeup上使用命令curl -v --proxy http://localhost:15214 http://localhost:15213/home.html来传送tiny目录下的home.html

9. 代码

为了不修改Makefile(本人太菜,不会改),直接把函数声明和定义写到.h文件中。

proxy.c

#include <stdio.h>
#include "csapp.h"
#include "sbuf.h"
#include "cache.h"

/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
#define SBUFSIZE 16
#define NTHREADS 4
#define FOR(a, b) for (int i=a; i<b; i++)
// int main()
// {
   
//     printf("%s", user_agent_hdr);
//     return 0;
// }
void doit(int fd);
void read_requesthdrs(rio_t *rp, int fd);
int parse_uri(char *uri, char *filename, char *cgiargs);
void serve_static(int fd, char *filename, int filesize);
void get_filetype(char *filename, char *filetype);
void serve_dynamic(int fd, char *filename, char *cgiargs);
void clienterror(int fd, char *cause, char *errnum, 
		 char *shortmsg, char *longmsg);
int gen_request();
int gen_host(char *uri, char *host, char *port, char *request);
void return_content(rio_t *rp, int clientfd, char *url);
void *thread(void *vargp);

/* You won't lose style points for including this long line in your code */
static const char *user_agent_hdr = "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 Firefox/10.0.3\r\n";
sbuf_t sbuf; /* 共享描述符池 */
cache_t cache; 

int main(int argc, char **argv) 
{
   
    int listenfd, connfd;
    char hostname[MAXLINE], port[MAXLINE];
    socklen_t clientlen;
    struct sockaddr_storage clientaddr;
    pthread_t tid;

    /* Check command line args */
    if (argc != 2) {
   
	fprintf(stderr, "usage: %s <port>\n", argv[0]);
	exit(1);
    }
    listenfd = Open_listenfd(argv[1]);

    sbuf_init(&sbuf, SBUFSIZE);
    cache_init(&cache);
    FOR(0, NTHREADS) {
   
        Pthread_create(&tid, NULL, thread, NULL);
    }
    
    while (1) {
   
        clientlen = sizeof(clientaddr);
        connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); //line:netp:tiny:accept
        Getnameinfo((SA *) &clientaddr, clientlen, hostname, MAXLINE, 
                    port, MAXLINE, 0);
        printf("Accepted connection from (%s, %s)\n", hostname, port);
        sbuf_insert(&sbuf, connfd);
    }
    
}
/* $end tinymain */

/*
 * doit - handle one HTTP request/response transaction
 * 解析请求行得到服务端主机和端口
 * 解析请求header,改成自己的header
 */
/* $begin doit */
void doit
  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: CSAPP Proxy Lab是CMU计算机科学系的一项课程作业。该作业旨在通过设计和实现一个基于代理服务器的Web代理,让学生更好地理解计算机网络、操作系统和编程等方面的知识,提高他们的编程能力和问题解决能力。 在这个作业中,学生需要实现一个Web代理程序,该程序需要能够接受来自客户端的HTTP请求,并将请求转发到相应的Web服务器。代理程序需要能够处理HTTP请求的各种类型(GET、POST、PUT等),并且需要能够处理HTTP响应的各种错误码(404、500等)。代理程序还需要支持并发处理多个HTTP请求,以提高系统的效率。 在实现代理程序的过程中,学生需要掌握网络编程、多线程编程、Socket编程等技术,并且需要使用C语言实现代理程序。此外,还需要学生能够理解HTTP协议、代理服务器的工作原理以及Web应用的工作流程等相关知识。 总之,CSAPP Proxy Lab是一项非常有挑战性的作业,需要学生具备扎实的编程基础和网络知识。通过完成该作业,学生可以深入理解计算机网络、操作系统和编程等方面的知识,并提高自己的编程能力和问题解决能力。 ### 回答2: CSAPP Proxy Lab是Carnegie Mellon大学计算机科学的一项项目,旨在帮助学生深入了解计算机网络和代理服务器的工作原理以及如何开发高效的网络应用程序。 Proxy Server是一种应用程序,可以充当网络上的“中转站”,它可以通过代理服务器将客户端请求转发到目标服务器端,并将响应返回给客户端。Proxy Lab的任务是实现一个HTTP代理服务器,它需要能够从客户端接收请求,并将请求转发到目标服务器,然后将响应发送回客户端。 实现Proxy Lab需要掌握网络编程、多线程编程、缓存设计以及HTTP协议等知识。代理服务器需要支持并发处理多个客户端请求,以保证高效的网络传输。为了提高性能,代理服务器还需要对常见的网页、图片和视频进行缓存,避免重复请求。 除了上述技能外,实现Proxy Lab还需要良好的编程能力和团队合作精神。在实现Proxy Lab的过程中,学生需要与队友紧密协作,及时沟通、并发同步,以确保项目的顺利进行。 总之,CSAPP Proxy Lab是一项非常有挑战性的计算机网络应用项目,不仅要求学生充分理解TCP/IP协议、HTTP协议等基本概念,还需要具备优秀的编程和团队协作能力。完成该项目不仅可以提高学生的技能,也可以为日后工作和实际应用打下良好的基础。 ### 回答3: CSAPP Proxy Lab 是一个经典的计算机科学实验,它涵盖了计算机网络知识和系统编程技能。这个实验的主要目标是构建一个基本的 Web 代理服务器,该服务器能够处理 HTTP 请求,并在远程 Web 服务器上代表客户端处理这些请求。 在 Proxy Lab 中,学生需要实现一个基于事件驱动的 Web 代理服务器。该服务器使用 epoll 进行事件处理,可以同时处理多个连接和请求。代理服务器需要支持从客户端接收 HTTP 请求,并解析请求头,将请求发送到远程服务器,接收响应,并将响应发送回客户端。在此过程中,代理服务器需要支持请求过滤和转发,以及缓存功能。 重要的是,学生需要处理一些常见的 Web 代理挑战,例如连接重用、响应缓存、虚拟主机支持和负载均衡。通过完成 Proxy Lab 实验,学生将获得有关计算机系统编程和网络协议的深入知识,并获得实际构建 Web 代理服务器的经验。 总之,CSAPP Proxy Lab 是一个非常重要的实验,它可以帮助学生领会计算机网络和系统编程的核心概念。通过完成这个实验,学生将获得深入的理解和实践经验,从而更好地了解计算机系统和网络技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值