CSAPP2e: Proxy lab 解答

  这次的Proxy lab 是要求实现一个简单的web 代理。与此相关的章节是网络编程和并发编程,其实之前零零星星的看过一些讲HTTP协议的书,但是对于套接字这些都是一知半解,跟着课堂学完这两章突然柳暗花明,再看一些更详细更深入的书,像《HTTP权威指南》,《计算机网络-自顶向下的方法》就明白的多了。

  下面就说一下这次lab,共有3个部分,第一部分是实现一个单线程代理,接收客户端请求,连接服务器然后转发。第二部分是实现并发,为每一个请求新建一个进程。第三部分是最有趣的,为每个请求建立独立的进程之后,该怎么共享进程之间的数据,也就是缓存呢?这里用到了书上介绍的写者-读者模型。源代码如下(已经通过了测试)。

  1 /*
  2  * [email protected]
  3  * 2014.12.28 18:24
  4  */
  5
  6 #include "csapp.h"
  7
  8 #define MAX_CACHE_SIZE 1049000
  9 #define MAX_OBJECT_SIZE 102400
 10
 11 /* argument for each thread */
 12 struct arg {
 13     int connfd;            /* client socked */
 14     int turn;            /* the time of current request */
 15 };
 16 /* cache block */
 17 struct cache_block {
 18     char data[MAX_OBJECT_SIZE];        /* store the response head and body */
 19     sem_t mutex;                    /* 模仿书上P673,使用写者-读者模型解决并发冲突 */
 20     sem_t w;
 21     int readcnt;
 22
 23     int turn;                        /* the time of request */
 24     sem_t t_mutex;
 25     sem_t t_w;
 26     int t_readcnt;
 27
 28     char url[300];                    /* the url of request */
 29     sem_t url_mutex;
 30     sem_t url_w;
 31     int url_readcnt;
 32
 33 };
 34 /* total 10 cache block */
 35 struct cache_block cache[10];
 36
 37 /* init the block */
 38 void cache_erase(int index);
 39
 40 /* because of concurrency, all operation on cache use following 4 function */
 41 /* write data, url and turn on cache[index] */
 42 void cache_write(int index, char *url,char *data, int turn);
 43 /* read data on cache[index] */
 44 void cache_data_read(int index, char *dst, int turn);
 45 /* read url on cache[index] */
 46 void cache_url_read(int index,char *dst);
 47 /* read turn on cache[index] */
 48 int cache_turn_read(int index);
 49
 50 /* thread for each request */
 51 void *thread(void *connfdp);
 52
 53 /* parse the request line */
 54 void parse_url(char *url, char *hostname, char *query_path, int *port);
 55
 56 /* connect to server, if failed return -1 */
 57 int connect_server(char *hostname,int port,char *query_path);
 58
 59
 60 int main(int argc,char **argv)
 61 {
 62     Signal(SIGPIPE, SIG_IGN);
 63     struct sockaddr_in clientaddr;
 64     int port,listenfd,clientlen;
 65     int turn=1;
 66     pthread_t tid;
 67     struct arg *p;
 68
 69     /* check port */
 70     if(argc!=2) {
 71         fprintf(stderr, "usage: %s <port>\n", argv[0]);
 72         exit(0);
 73     }
 74
 75     // init 10 cache blocks
 76     int i;
 77     for(i=0;i<10;i++)
 78         cache_erase(i);
 79
 80     port=atoi(argv[1]);
 81     listenfd=Open_listenfd(port);
 82     clientlen=sizeof(clientaddr);
 83
 84     while(1) {
 85         p=(int*)Malloc(sizeof(struct arg));
 86         p->connfd=Accept(listenfd,(SA*)&clientaddr,&clientlen);
 87         p->turn =turn++;
 88         /* create thread */
 89         Pthread_create(&tid,NULL,thread,(void *)p);
 90     }
 91     return 0;
 92 }
 93
 94 /* parse request url */
 95 void parse_url(char *ur, char *hostname, char *query_path, int *port)
 96 {
 97     char url[100];
 98     url[0]=‘\0‘;
 99     strcat(url,ur);
100     hostname[0]=query_path[0]=‘\0‘;
101     char *p=strstr(url,"//");        /* skip "http://" and "https://" */
102     if(p!=NULL) {
103         p=p+2;
104     } else {
105         p=url;
106     }
107     char *q=strstr(p,":");            /* read ":<port>" and "/index.html" */
108     if(q!=NULL) {
109         *q=‘\0‘;
110         sscanf(p,"%s",hostname);
111         sscanf(q+1,"%d%s",port,query_path);
112     } else {
113         q=strstr(p,"/");
114         if(q!=NULL) {
115             *q=‘\0‘;
116             sscanf(p,"%s",hostname);
117             *q=‘/‘;
118             sscanf(q,"%s",query_path);
119         } else {
120             sscanf(p,"%s",hostname);
121         }
122         *port=80;
123     }
124     /* the default path */
125     if(strlen(query_path)<=1)
126         strcpy(query_path,"/index.html");
127
128     return;
129 }
130
131 /* connect to server and return socket fd */
132 int connect_server(char *hostname,int port,char *query_path)
133 {
134     static const char *user_agent = "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 Firefox/10.0.3\r\n";
135     static const char *accept_str= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n";
136     static const char *connection = "Connection: close\r\nProxy-Connection: close\r\n";
137
138     char buf[MAXLINE];
139     /* connect to server */
140     int proxy_clientfd;
141     proxy_clientfd=open_clientfd(hostname,port);
142
143     /* if failed return */
144     if(proxy_clientfd<0)
145         return proxy_clientfd;
146
147     /* write request to server */
148     sprintf(buf,"GET %s HTTP/1.0\r\n",query_path);
149     Rio_writen(proxy_clientfd,buf,strlen(buf));
150     sprintf(buf,"Host: %s\r\n",hostname);
151     Rio_writen(proxy_clientfd,buf,strlen(buf));
152     Rio_writen(proxy_clientfd,user_agent,strlen(user_agent));
153     Rio_writen(proxy_clientfd,accept_str,strlen(accept_str));
154     Rio_writen(proxy_clientfd,connection,strlen(connection));
155     Rio_writen(proxy_clientfd,"\r\n",strlen("\r\n"));
156     return proxy_clientfd;
157 }
158
159 /*
160  * if there is a finished cache, read and response.
161  * else connect to server
162  */
163 void *thread(void *p)
164 {
165     Pthread_detach(pthread_self());
166     int connfd=((struct arg*)p)->connfd,turn=((struct arg*)p)->turn;
167     free(p);
168
169     char buf[MAXLINE];
170     char method[MAXLINE],version[MAXLINE],url[MAXLINE];
171     char host[MAXLINE],query[MAXLINE];
172     char url_tmp[300],*data_tmp;
173     rio_t rio;
174     int index,port,content_length;
175     int serverfd;
176
177     /* read request line */
178     rio_readinitb(&rio,connfd);
179     rio_readlineb(&rio,buf,MAXLINE);
180     sscanf(buf,"%s %s %s",method,url,version);
181
182     if(strcasecmp(method,"GET")) {
183         /* ignore */
184         printf("Not GET\r\n");
185         Close(connfd);
186         return NULL;
187     }
188     /* ignore client request */
189     do {
190         rio_readlineb(&rio,buf,MAXLINE-1);
191     }while(strcmp(buf,"\r\n"));
192
193     /* find cache block */
194     for(index=0;index<10;index++) {
195         cache_url_read(index,url_tmp);
196         /* the block‘url is same as current url */
197         if(!strcmp(url,url_tmp))
198             break;
199     }
200
201     data_tmp=(char*)Malloc(MAX_OBJECT_SIZE);
202     data_tmp[0]=‘\0‘;
203
204     if(index <10) { /* if have cached */
205         cache_data_read(index,data_tmp,turn);
206         rio_writen(connfd,data_tmp,strlen(data_tmp));
207         Close(connfd);
208         free(data_tmp);
209         return NULL;
210     }
211
212     /* connect to server */
213     parse_url(url,host,query,&port);
214     if((serverfd=connect_server(host,port,query))<0) {
215         /* connect to server failed, return */
216         free(data_tmp);
217         Close(connfd);
218         return NULL;
219     }
220
221     rio_readinitb(&rio,serverfd);
222     content_length=0;
223     /* read response head line */
224     do {
225         int t=rio_readlineb(&rio,buf,MAXLINE-1);
226         if(t<=0)
227             break;
228         strncat(data_tmp,buf,t);
229         if(strstr(buf,"Content-length")!=NULL)
230             sscanf(buf,"Content-length: %d\r\n",&content_length);
231         rio_writen(connfd,buf,t);
232     }while(strcmp(buf,"\r\n"));
233
234     /* read response body */
235     /* response is small enough to cache */
236     if(content_length+strlen(data_tmp)<MAX_OBJECT_SIZE) {
237         while(content_length>0) {
238             int t= rio_readnb(&rio,buf,(content_length<MAXLINE-1)?content_length:MAXLINE-1);
239             if(t<=0)
240                 continue;
241             content_length-=t;
242             strncat(data_tmp,buf,t);
243             rio_writen(connfd,buf,t);
244         }
245         index=0;
246         int i;
247         /* least-recently-used */
248         for(i=1;i<10;i++) {
249             if(cache_turn_read(i)<cache_turn_read(index)) {
250                 index=i;
251             }
252         }
253         /* cache write */
254         cache_write(index,url,data_tmp,turn);
255     }
256     /* ignore store and write to client */
257     else {
258         while(content_length>0) {
259             int t= rio_readnb(&rio,buf,(content_length<MAXLINE-1)?content_length:MAXLINE-1);
260             if(t<=0)
261                 break;
262             content_length-=t;
263             rio_writen(connfd,buf,t);
264         }
265     }
266     Close(connfd);
267     Close(serverfd);
268     free(data_tmp);
269     return NULL;
270 }
271
272 void cache_erase(int index)
273 {
274     /* init all var */
275     cache[index].turn=0;
276     cache[index].url[0]=‘\0‘;
277     cache[index].data[0]=‘\0‘;
278     Sem_init(&cache[index].t_mutex,0,1);
279     Sem_init(&cache[index].t_w,0,1);
280     cache[index].t_readcnt=0;
281     Sem_init(&cache[index].url_mutex,0,1);
282     Sem_init(&cache[index].url_w,0,1);
283     cache[index].url_readcnt=0;
284     Sem_init(&cache[index].mutex,0,1);
285     Sem_init(&cache[index].w,0,1);
286     cache[index].readcnt=0;
287 }
288
289 void cache_write(int index,char *url, char *data, int turn)
290 {
291     /* semaphore */
292     P(&cache[index].url_w);
293     P(&cache[index].w);
294     P(&cache[index].t_w);
295     /* begin write operation */
296     cache[index].turn=turn;
297     strcpy(cache[index].data,data);
298     strcpy(cache[index].url,url);
299     /* end write operation */
300
301     /* semaphore */
302     V(&cache[index].t_w);
303     V(&cache[index].w);
304     V(&cache[index].url_w);
305     return ;
306 }
307
308 void cache_data_read(int index, char *dst, int turn)
309 {
310     /* semaphore */
311     P(&cache[index].mutex);
312     cache[index].readcnt++;
313     if(cache[index].readcnt==1)
314         P(&cache[index].w);
315     V(&cache[index].mutex);
316     P(&cache[index].t_w);
317
318     /* begin read operation */
319     cache[index].turn=turn;
320     strcpy(dst,cache[index].data);
321     /* end read operation */
322
323     /* semphore */
324     V(&cache[index].t_w);
325     P(&cache[index].mutex);
326     cache[index].readcnt--;
327     if(cache[index].readcnt==0)
328         V(&cache[index].w);
329     V(&cache[index].mutex);
330
331     return;
332 }
333
334 void cache_url_read(int index,char *dst)
335 {
336     /* semaphore */
337     P(&cache[index].url_mutex);
338     cache[index].url_readcnt++;
339     if(cache[index].url_readcnt==1)
340         P(&cache[index].url_w);
341     V(&cache[index].url_mutex);
342
343     /* begin read operation */
344     strcpy(dst,cache[index].url);
345     /* end read operation */
346
347     /* semphore */
348     P(&cache[index].url_mutex);
349     cache[index].url_readcnt--;
350     if(cache[index].url_readcnt==0)
351         V(&cache[index].url_w);
352     V(&cache[index].url_mutex);
353
354     return;
355 }
356
357 int cache_turn_read(int index)
358 {
359     int t;
360     /* semaphore */
361     P(&cache[index].t_mutex);
362     cache[index].t_readcnt++;
363     if(cache[index].t_readcnt==1)
364         P(&cache[index].t_w);
365     V(&cache[index].t_mutex);
366
367     /* begin read operation */
368     t=cache[index].turn;
369     /* end read operation */
370
371     /* semphore */
372     P(&cache[index].t_mutex);
373     cache[index].t_readcnt--;
374     if(cache[index].t_readcnt==0)
375         V(&cache[index].t_w);
376     V(&cache[index].t_mutex);
377
378     return t;
379 }
时间: 2024-12-11 05:32:11

CSAPP2e: Proxy lab 解答的相关文章

CSAPP2e:Shell lab 解答

期中之后的第一个lab 就是实现一个简单的Shell 程序,程序的大部分已经写好,只需要实现 eval 函数和处理信号的sigchld_handle, sigint_handle, sigtstp_handle这三个函数. 这个lab 主要要求处理好各个信号,因为上课的时候一直听得很糊涂,就拖着没有写,直到这两天deadline逼近才动手.同样是时间紧迫,debug的时候出了很多问题,在网上搜了很多解答,但是因为题目版本不一样,并不完全适用,比如之前的不需要重定向.因此把自己写的代码也贴出来,最

ISP Lab Multicast

ISP Lab Multicast 1- IGMP 1-1 IGMP基本配置 R3(config)#int f0/0 R3(config-if)#ip ospf 110 a 0 R3(config)#ip multicast-routing R3(config)#int f0/0 R3(config-if)#ip pim sparse-mode \\使得路由器可以接受client的igmp信息 R3#sh ip igmp interface f 0/0 FastEthernet0/0 is up

嵌入式LAB 1:启动

嵌入式系统Lab 1 启动 1. 画出你所实际实施的连接示意图 2.给出实际拍摄的板卡连接照片 3.给出所用的器材的列表 pcduino(含电源).USB串口线.网线.SD卡.无线网卡 显示屏.鼠标.键盘.支持L2TP路由器.MacBook Air 4.给出得到的pcDuino启动时的输出文字,并逐行解释 U-Boot 2009.08 (Dec 25 2014 - 21:37:33) # U-Boot: Universal Boot Loader, 负责嵌入式 Linux 系统的引导 CPU:

CSAPP Bomb Lab记录

记录关于CSAPP 二进制炸弹实验过程 (CSAPP配套教学网站Bomb Lab自学版本,实验地址:http://csapp.cs.cmu.edu/2e/labs.html) (个人体验:对x86汇编寻址模式要有清晰的了解,如mov指令涉及的是计算出的地址所指向的存储单元的值,而lea指令保留的是计算出来的地址,数字是否加$表示常数的问题等: 实验中涉及的跳表的存储方式.链表的处理等是C语言的汇编语言实现方式,处理起来较为复杂,但可对这些方式的对象底层实现方式有一个较为清晰的了解: 涉及指针操作

【转载】JAVA常见面试题及解答(精华)

JAVA常见面试题及解答(精华) 1)transient和volatile是java关键字吗?(瞬联) 如果用transient声明一个实例变量,当对象存储时,它的值不需要维持.例如: class T { transient int a;  //不需要维持 int b;  //需要维持 } 这里,如果T类的一个对象写入一个持久的存储区域,a的内容不被保存,但b的将被保存. volatile修饰符告诉编译器被volatile修饰的变量可以被程序的其他部分改变.在多线程程序中,有时两个或更多的线程共

MIT 6.828 JOS学习笔记7. Lab 1 Part 2.2: The Boot Loader

Lab 1 Part 2 The Boot Loader Loading the Kernel 我们现在可以进一步的讨论一下boot loader中的C语言的部分,即boot/main.c.但是在我们分析之前,我们应该先回顾一些关于C语言的基础知识. Exercise 4: 阅读关于C语言的指针部分的知识.最好的参考书自然是"The C Programming Language". 阅读5.1到5.5节.然后下载pointers.c的代码,并且编译运行它,确保你理解在屏幕上打印出来的所

Learning WCF Chapter1 Generating a Service and Client Proxy

In the previous lab,you created a service and client from scratch without leveraging the tools available to WCF developers. Although this helps you to understand the raw requirements for sending messages between clients and services,in reality,develo

MIT 6.828 JOS学习笔记10. Lab 1 Part 3: The kernel

Lab 1 Part 3: The kernel 现在我们将开始具体讨论一下JOS内核了.就像boot loader一样,内核开始的时候也是一些汇编语句,用于设置一些东西,来保证C语言的程序能够正确的执行. 使用虚拟内存 在运行boot loader时,boot loader中的链接地址(虚拟地址)和加载地址(物理地址)是一样的.但是当进入到内核程序后,这两种地址就不再相同了. 操作系统内核程序在虚拟地址空间通常会被链接到一个非常高的虚拟地址空间处,比如0xf0100000,目的就是能够让处理器

深入理解计算机系统 (CS:APP) Lab2 - Bomb Lab 解析

原文地址:https://billc.io/2019/04/csapp-bomblab/ 写在前面 CS:APP是这学期的一门硬核课程,应该是目前接触到最底层的课程了.学校的教学也是尝试着尽量和CMU同步,课件和习题都直接照搬原版.包括现在着手的第二个实验室Bomb Lab.这个lab很有意思,没有提供全部c语言代码,需要手动根据反汇编语言推测在每一个阶段需要输入的内容,输入正确就可以进入下一个阶段. 理论上每个人获取到的lab都是不一样的,但对于自学学生而言在官网http://csapp.cs