• CSAPP Lab08——Proxy Lab完成思路


    蓝色的思念

    突然演变成了阳光的夏天

    空气中的温暖不会很遥远

    ——被风吹过的夏天

    完整代码见:CSAPP/proxylab-handout at main · SnowLegend-star/CSAPP (github.com)

    Q:计算机网络中port的作用是什么?

    A:在计算机网络中,"port"(端口)是用于标识网络通信中的特定应用程序或服务的数字标识符。端口是网络通信的重要组成部分,它允许多个应用程序在同一台计算机上共享网络连接,并确保正确地将数据传递给目标应用程序。

    端口通常与 IP 地址结合使用,构成了网络通信中的目标地址。IP 地址用于标识网络中的设备,而端口则用于标识设备上的具体应用程序或服务

    端口的作用包括:

    1. 标识应用程序或服务:每个应用程序或服务都可以绑定到一个或多个端口上,通过端口号来唯一标识。
    2. 实现多路复用:通过使用端口,多个应用程序可以在同一台计算机上共享网络连接。这意味着同一时间内,可以同时进行多个不同应用程序的通信,每个应用程序使用不同的端口。
    3. 提供通信路由:在网络通信中,端口号帮助路由数据到正确的目标应用程序。当数据包到达目标设备后,根据目标端口号将数据包分发给相应的应用程序或服务。

    总之,端口在计算机网络中起着非常重要的作用,它们允许多个应用程序共享网络连接,并确保数据被正确地路由到目标应用程序或服务。


    运行课本样例code的方法 

    今天突然想把《CSAPP》书上的例子跑一下,结果还是经典的报错。本来以前运行过书上的示例代码,当时就是报错。折腾半天无果索性放弃了。现在想到书都快看完了还没跑成功过示例代码,我心血来潮决定好好研究下到底怎么执行。

    想不到还不好搜,找半天才看到一个可行的方案,原来编译书上的代码时都得和csapp.c进行联合编译。因为好多函数都在csapp.c里面重写了,如果不带上这个文件就会有各种报错,看似是没引入头文件导致库函数无法使用,实则是因为当前文件用到的函数其实是在csapp.c内部重新实现了的,只不过csapp.c内部编写的函数命名方式和库函数神似,有点以假乱真。

    至此,终于是成功运行了书上的示例代码。


     

    一不小心又在proxy lab上磨了好久,主要是一开始对怎么测试proxy感到很疑惑,就去b站上搜了下,结果发现了个讲得很好的up。遂跟着这个up把10~12章重新温习了一番,收获颇多。由于up主的github上只有通过最后一次实验的记录,我又研究半天怎么获得github的历史提交记录,最后也是成功掌握了这个技巧。

    言归正传,下面讲讲我完成这个lab的历程。

    首先,总体思路图大致如下

     

    1、Proxy先与Client进行通信,建立连接并接收并分析来自Client的http-request

    2、Proxy把处理好的http-request发送给Server

    3、Server发送response给Proxy

    4、Proxy将response转发给Client

    Part I: Implementing a sequential web proxy

    第一部分要求我们实现一个简单的sequential proxy,类似的代码在书上随处可见,tiny.c就是一个很好的模仿对象。它的大致流程如下:

    1、在main()里面创建一个监听描述符listenfd,然后在while的循环体里不断尝试与客户端进行连接connfd=Accept(listenfd)。连接成功后将connfd传入请求处理函数handleRequest()中。

    2、进入handRequest()后,分别创建好两个I/O缓冲区rio_Proxy2Client和rio_Proxy2Server,Proxy可以利用这两个不同的缓冲区分别和Client与Server进行读写操作。

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

    Host: www.cmu.edu

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

    Connection: close

    Proxy-Connection: close

    3、利用rio_Proxy2Client读入http-request,并把这个请求分割成writeup要求的几部分,即method、url、version;其中url又可以分割为hostName、port和fielName。

    4、处理完request的第一行后,开始处理后序的四个request header,少哪个header就自己补上哪个header。

    5、 Proxy利用刚才得到的hostName和port,调用clientfd=Open_clientfd()冒充一个Client与Server建立连接。

    6、Proxy利用clientfd和rio_Proxy2Server,先把从Client处得到的http-request发送给Server,然后不断读入Server回复的response。Proxy在读入response的同时,又利用connfd把它们发送给Client。

    7、还得加上一个如果method不是“GET”的错误处理函数,直接照着书上的敲一遍或者把tiny.c的那个clientError()复制过来就行。

    第一部分的代码难度并不高,核心操作就是要对字符串进行各种处理。

    part I的实现如下
     

    1. #include
    2. #include"csapp.h"
    3. #include"sbuf.h"
    4. /* Recommended max cache and object sizes */
    5. #define MAX_CACHE_SIZE 1049000
    6. #define MAX_OBJECT_SIZE 102400
    7. #define HTTP_PREFIX "http://"
    8. /* You won't lose style points for including this long line in your code */
    9. 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";
    10. void handleRequest(int fd);
    11. void parseRequest(char* buf, char* host, char* port, char* method, char* url, char* version, char* fileName);
    12. void clientError(int fd, char* cause, char* errnum, char* errmsg, char* errmsg_datail);
    13. int readAndFormatRequestHeader(rio_t* rio, char* clientRequest, char* Host, char* port,
    14. char* method, char* url, char* version, char* filename);
    15. //和tiny.c里面的那个差不多
    16. void clientError(int fd, char* cause, char* errnum, char* errmsg, char* errmsg_datail){
    17. char buf[MAXLINE];
    18. //打印HTTP的响应头
    19. sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, errmsg);
    20. Rio_writen(fd, buf, strlen(buf));
    21. sprintf(buf, "Content-type: text/html\r\n\r\n");
    22. Rio_writen(fd, buf, strlen(buf));
    23. //打印HTTP响应的主体
    24. sprintf(buf, "Tiny Error");
    25. Rio_writen(fd,buf,strlen(buf));
    26. sprintf(buf, ""ffffff"">\r\n");
    27. Rio_writen(fd,buf,strlen(buf));
    28. sprintf(buf, "%s: %s\r\n", errmsg, errmsg);
    29. Rio_writen(fd, buf, strlen(buf));
    30. sprintf(buf, "

      %s: %s\r\n", errmsg_datail, cause);

    31. Rio_writen(fd, buf, strlen(buf));
    32. sprintf(buf, "
      The Tiny Web Server\r\n"
      );
    33. Rio_writen(fd, buf, strlen(buf));
    34. }
    35. void handleRequest(int fd){
    36. //处理各种定位的指针
    37. char* pos=NULL;
    38. //分割http请求的参数
    39. char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE],fileName[MAXLINE];
    40. //客户端的几个请求头的主体
    41. char clientRequest[MAXLINE];
    42. char hostName[MAXLINE],port[MAXLINE];
    43. //proxy-client和proxy-server的IO
    44. rio_t rio_Proxy2Client,rio_Proxy2Server;
    45. //Step1: proxy读入来自client的请求
    46. Rio_readinitb(&rio_Proxy2Client,fd);
    47. if(Rio_readlineb(&rio_Proxy2Client,buf,MAXLINE)==0){
    48. //这个请求是空的
    49. printf("Oops! empty request\n");
    50. return ;
    51. }
    52. //如果http的版本是1.1,处理成1.0
    53. if((pos=strstr(buf,"HTTP/1.1"))!=NULL){
    54. buf[pos-buf-1+strlen("HTTP/1.1")]='0';
    55. }
    56. //Step2: 分割请求
    57. parseRequest(buf, hostName, port, method, uri, version, fileName);
    58. //判断请求是否有效
    59. if(strcasecmp(method,"GET")!=0){
    60. clientError(fd, method, "501", "Not Implement", "Tiny Does not implement this method");
    61. return ;
    62. }
    63. int rv=readAndFormatRequestHeader(&rio_Proxy2Client, clientRequest, hostName, port, method, uri, version, fileName);
    64. if(rv==0){
    65. return ;
    66. }
    67. //Step3: tiny server和proxy建立连接
    68. int clientfd=Open_clientfd(hostName, port);
    69. Rio_readinitb(&rio_Proxy2Server, clientfd);
    70. Rio_writen(rio_Proxy2Server.rio_fd, clientRequest, strlen(clientRequest));
    71. //Step4: 从tiny server读入response,并且把它发送给client
    72. printf("The Proxy is ready to relay the response\n");
    73. char tinyResponse[MAXLINE];
    74. int n;
    75. while((n=Rio_readlineb(&rio_Proxy2Server, tinyResponse, MAXLINE))!=0){
    76. Rio_writen(fd, tinyResponse, n);
    77. }
    78. }
    79. //client request is like this
    80. //GET http://www.cmu.edu/hub/index.html HTTP/1.1
    81. void parseRequest(char* buf, char* host, char* port, char* method, char* url, char* version, char* fileName){
    82. sscanf(buf,"%s %s %s", method, url, version);
    83. //method = "GET", url = "http://localhost:15213/home.html", version = "HTTP1.0"
    84. char* host_pos =strstr(url,HTTP_PREFIX)+strlen(HTTP_PREFIX); //主机名开始的位置
    85. char* port_pos =strstr(host_pos,":"); //端口开始的位置
    86. char* slash_pos=strstr(host_pos,"/"); //suffix开始的位置
    87. //判断url有没有带端口号,如果没带就是默认端口80
    88. if(port_pos==NULL){ //没带端口号
    89. strcpy(port,"80");
    90. strncmp(host,host_pos,slash_pos-host_pos);
    91. }
    92. else{
    93. strncpy(host,host_pos,port_pos-host_pos);
    94. strncpy(port,port_pos+1,slash_pos-port_pos-1);
    95. }
    96. strcpy(fileName,slash_pos);
    97. printf("HostName: %s",host);
    98. printf("Port: %s",port);
    99. printf("fileName: %s",fileName);
    100. }
    101. int readAndFormatRequestHeader(rio_t* rio, char* clientRequest, char* Host, char* port,
    102. char* method, char* url, char* version, char* fileName){
    103. int UserAgent=0, Connection=0, proxyConnection=0, hostInfo=0;
    104. char buf[MAXLINE/2];
    105. int n;
    106. char* findpos;
    107. sprintf(clientRequest, "GET %s HTTP/1.0\r\n",fileName);
    108. n=Rio_readlineb(rio, buf, MAXLINE);
    109. printf("receive buf %s\n", buf);
    110. printf("n = %d", n);
    111. while(strcmp("\r\n",buf)!=0&&n!=0){
    112. strcat(clientRequest, buf);
    113. printf("receive buf %s\n", buf);
    114. //判断要求的四个请求头是否存在
    115. if((findpos=strstr(buf, "User-Agent:"))!=NULL){
    116. UserAgent=1;
    117. }
    118. if((findpos=strstr(buf,"Proxy-Connection:"))!=NULL){
    119. proxyConnection=1;
    120. }
    121. if((findpos=strstr(buf,"Connection"))!=NULL){
    122. Connection=1;
    123. }
    124. if((findpos=strstr(buf, "Host"))!=NULL){
    125. hostInfo=1;
    126. }
    127. n=Rio_readlineb(rio, buf ,MAXLINE);
    128. }
    129. if(n==0){
    130. return 0;
    131. }
    132. //如果缺失了这四个头部,则进行添加
    133. if(hostInfo==0){
    134. sprintf(buf, "Host: %s\r\n", Host);
    135. strcat(clientRequest, buf);
    136. }
    137. if(UserAgent==0){
    138. strcat(clientRequest, user_agent_hdr);
    139. }
    140. if(Connection==0){
    141. sprintf(buf, "Connection: close\r\n");
    142. strcat(clientRequest, buf);
    143. }
    144. if(proxyConnection==0){
    145. sprintf(buf, "Proxy-Connection: close\r\n");
    146. strcat(clientRequest, buf);
    147. }
    148. //添加最后的空行
    149. strcat(clientRequest,"\r\n");
    150. return 1;
    151. }
    152. int main(int argc,char** argv){
    153. if(argc!=2){
    154. unix_error("proxy usage: ./proxy ");
    155. }
    156. int listenfd=Open_listenfd(argv[1]);
    157. struct sockaddr_storage clientaddr;
    158. char hostName[MAXLINE], port[MAXLINE];
    159. while(1){
    160. socklen_t clientlen=sizeof(struct sockaddr_storage);
    161. int connfd=Accept(listenfd,(SA*) &clientaddr,&clientlen);
    162. Getnameinfo((SA*) &clientaddr,clientlen,hostName,MAXLINE,port,MAXLINE,0);
    163. handleRequest(connfd);
    164. Close(connfd);
    165. }
    166. return 0;
    167. }

    Part II

    书上给出了三种并发编程的方式:

    1、基于进程

    2、基于I/O多路复用

    3、基于线程

    不得不说基于线程是最为通俗易懂的,而且书上还有一个基于线程进行并发编程的例子“echoservert-pre.c”。结合这份代码,在Part I的基础上稍作改动即可。

    对了,书上还要求在main()函数里面加入Signal(SIGPIPE, SIG_IGN)来屏蔽SIGPIPE信号。我一开始并没有加这个顺利通过了lab,(:

    part II实现如下
     

    1. #include
    2. #include"csapp.h"
    3. #include"sbuf.h"
    4. /* Recommended max cache and object sizes */
    5. #define MAX_CACHE_SIZE 1049000
    6. #define MAX_OBJECT_SIZE 102400
    7. #define HTTP_PREFIX "http://"
    8. #define NTHREADS 4
    9. #define SBUFSIZE 16
    10. sbuf_t sbuf; //shared buffer of connected descriptor
    11. void *thread(void *vargp);
    12. /* You won't lose style points for including this long line in your code */
    13. 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";
    14. void handleRequest(int fd);
    15. void parseRequest(char* buf, char* host, char* port, char* method, char* url, char* version, char* fileName);
    16. void clientError(int fd, char* cause, char* errnum, char* errmsg, char* errmsg_datail);
    17. int readAndFormatRequestHeader(rio_t* rio, char* clientRequest, char* Host, char* port,
    18. char* method, char* url, char* version, char* filename);
    19. //和tiny.c里面的那个差不多
    20. void clientError(int fd, char* cause, char* errnum, char* errmsg, char* errmsg_datail){
    21. char buf[MAXLINE];
    22. //打印HTTP的响应头
    23. sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, errmsg);
    24. Rio_writen(fd, buf, strlen(buf));
    25. sprintf(buf, "Content-type: text/html\r\n\r\n");
    26. Rio_writen(fd, buf, strlen(buf));
    27. //打印HTTP响应的主体
    28. sprintf(buf, "Tiny Error");
    29. Rio_writen(fd,buf,strlen(buf));
    30. sprintf(buf, ""ffffff"">\r\n");
    31. Rio_writen(fd,buf,strlen(buf));
    32. sprintf(buf, "%s: %s\r\n", errmsg, errmsg);
    33. Rio_writen(fd, buf, strlen(buf));
    34. sprintf(buf, "

      %s: %s\r\n", errmsg_datail, cause);

    35. Rio_writen(fd, buf, strlen(buf));
    36. sprintf(buf, "
      The Tiny Web Server\r\n"
      );
    37. Rio_writen(fd, buf, strlen(buf));
    38. }
    39. void handleRequest(int fd){
    40. //处理各种定位的指针
    41. char* pos=NULL;
    42. //分割http请求的参数
    43. char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE],fileName[MAXLINE];
    44. //客户端的几个请求头的主体
    45. char clientRequest[MAXLINE];
    46. char hostName[MAXLINE],port[MAXLINE];
    47. //proxy-client和proxy-server的IO
    48. rio_t rio_Proxy2Client,rio_Proxy2Server;
    49. //Step1: proxy读入来自client的请求
    50. Rio_readinitb(&rio_Proxy2Client,fd);
    51. if(Rio_readlineb(&rio_Proxy2Client,buf,MAXLINE)==0){
    52. //这个请求是空的
    53. printf("Oops! empty request\n");
    54. return ;
    55. }
    56. //如果http的版本是1.1,处理成1.0
    57. if((pos=strstr(buf,"HTTP/1.1"))!=NULL){
    58. buf[pos-buf-1+strlen("HTTP/1.1")]='0';
    59. }
    60. //Step2: 分割请求
    61. parseRequest(buf, hostName, port, method, uri, version, fileName);
    62. //判断请求是否有效
    63. if(strcasecmp(method,"GET")!=0){
    64. clientError(fd, method, "501", "Not Implement", "Tiny Does not implement this method");
    65. return ;
    66. }
    67. int rv=readAndFormatRequestHeader(&rio_Proxy2Client, clientRequest, hostName, port, method, uri, version, fileName);
    68. if(rv==0){
    69. return ;
    70. }
    71. //Step3: tiny server和proxy建立连接
    72. int clientfd=Open_clientfd(hostName, port);
    73. Rio_readinitb(&rio_Proxy2Server, clientfd);
    74. Rio_writen(rio_Proxy2Server.rio_fd, clientRequest, strlen(clientRequest));
    75. //Step4: 从tiny server读入response,并且把它发送给client
    76. printf("The Proxy is ready to relay the response\n");
    77. char tinyResponse[MAXLINE];
    78. int n;
    79. while((n=Rio_readlineb(&rio_Proxy2Server, tinyResponse, MAXLINE))!=0){
    80. Rio_writen(fd, tinyResponse, n);
    81. }
    82. }
    83. //client request is like this
    84. //GET http://www.cmu.edu/hub/index.html HTTP/1.1
    85. void parseRequest(char* buf, char* host, char* port, char* method, char* url, char* version, char* fileName){
    86. sscanf(buf,"%s %s %s", method, url, version);
    87. //method = "GET", url = "http://localhost:15213/home.html", version = "HTTP1.0"
    88. char* host_pos =strstr(url,HTTP_PREFIX)+strlen(HTTP_PREFIX); //主机名开始的位置
    89. char* port_pos =strstr(host_pos,":"); //端口开始的位置
    90. char* slash_pos=strstr(host_pos,"/"); //suffix开始的位置
    91. //判断url有没有带端口号,如果没带就是默认端口80
    92. if(port_pos==NULL){ //没带端口号
    93. strcpy(port,"80");
    94. strncmp(host,host_pos,slash_pos-host_pos);
    95. }
    96. else{
    97. strncpy(host,host_pos,port_pos-host_pos);
    98. strncpy(port,port_pos+1,slash_pos-port_pos-1);
    99. }
    100. strcpy(fileName,slash_pos);
    101. printf("HostName: %s",host);
    102. printf("Port: %s",port);
    103. printf("fileName: %s",fileName);
    104. }
    105. int readAndFormatRequestHeader(rio_t* rio, char* clientRequest, char* Host, char* port,
    106. char* method, char* url, char* version, char* fileName){
    107. int UserAgent=0, Connection=0, proxyConnection=0, hostInfo=0;
    108. char buf[MAXLINE/2];
    109. int n;
    110. char* findpos;
    111. sprintf(clientRequest, "GET %s HTTP/1.0\r\n",fileName);
    112. n=Rio_readlineb(rio, buf, MAXLINE);
    113. printf("receive buf %s\n", buf);
    114. printf("n = %d", n);
    115. while(strcmp("\r\n",buf)!=0&&n!=0){
    116. strcat(clientRequest, buf);
    117. printf("receive buf %s\n", buf);
    118. //判断要求的四个请求头是否存在
    119. if((findpos=strstr(buf, "User-Agent:"))!=NULL){
    120. UserAgent=1;
    121. }
    122. if((findpos=strstr(buf,"Proxy-Connection:"))!=NULL){
    123. proxyConnection=1;
    124. }
    125. if((findpos=strstr(buf,"Connection"))!=NULL){
    126. Connection=1;
    127. }
    128. if((findpos=strstr(buf, "Host"))!=NULL){
    129. hostInfo=1;
    130. }
    131. n=Rio_readlineb(rio, buf ,MAXLINE);
    132. }
    133. if(n==0){
    134. return 0;
    135. }
    136. //如果缺失了这四个头部,则进行添加
    137. if(hostInfo==0){
    138. sprintf(buf, "Host: %s\r\n", Host);
    139. strcat(clientRequest, buf);
    140. }
    141. if(UserAgent==0){
    142. strcat(clientRequest, user_agent_hdr);
    143. }
    144. if(Connection==0){
    145. sprintf(buf, "Connection: close\r\n");
    146. strcat(clientRequest, buf);
    147. }
    148. if(proxyConnection==0){
    149. sprintf(buf, "Proxy-Connection: close\r\n");
    150. strcat(clientRequest, buf);
    151. }
    152. //添加最后的空行
    153. strcat(clientRequest,"\r\n");
    154. return 1;
    155. }
    156. int main(int argc,char** argv){
    157. if(argc!=2){
    158. unix_error("proxy usage: ./proxy ");
    159. }
    160. int listenfd=Open_listenfd(argv[1]), i;
    161. pthread_t tid;
    162. struct sockaddr_storage clientaddr;
    163. char hostName[MAXLINE], port[MAXLINE];
    164. sbuf_init(&sbuf, SBUFSIZE);
    165. for(i=0;i//创建工作线程
    166. Pthread_create(&tid, NULL, thread, NULL);
    167. }
    168. while(1){
    169. socklen_t clientlen=sizeof(struct sockaddr_storage);
    170. int connfd=Accept(listenfd,(SA*) &clientaddr,&clientlen);
    171. //Getnameinfo((SA*)&clientaddr, clientlen, hostName, MAXLINE, port, MAXLINE, 0);
    172. sbuf_insert(&sbuf, connfd);
    173. }
    174. return 0;
    175. }
    176. void *thread(void *vargp){
    177. pthread_detach(pthread_self());
    178. while(1){
    179. int connfd=sbuf_remove(&sbuf);
    180. handleRequest(connfd);
    181. Close(connfd);
    182. }
    183. }

     

    Part III

    最后一部分写的我汗流浃背了。

    这一部分要求我们给Proxy添加cache。对于cache的读取方式,可以参照第一类“读者-写者”问题。我一开始也并没有反应过来可以在此引入“读者-写者”模式,还是在网上参考了其他人的解决方案才隐约发现得这么做的哈哈哈。

    其实可以直接把和cache相关的代码写在proxy.c里面,就是会让proxy.c更为臃肿一点。但我一想书上都给出sbuf.c和sbuf.h了,我也模仿着写一个cache.c和cache.h岂不美哉?正式这个决定让我后序改bug改得一头大包。我先在proxy.c中定义了缓存cache_t cache,然后进行参数传递在cache.c中修改cache。由于我对全局变量跨文件的处理方式一知半解,但还是硬着头皮写了下去。

    后面仔细思考了下,关于“全局变量跨文件的处理”其实有两种方案。

           方案一:我直接cache_t cache写入cache.h当中,而不是写在proxy.c。这样只需要在cache.c和proxy.c中直接引入“cache.h”就可以直接用cache了,但是这样有个弊端———有且仅有一个cache供proxy.c使用,如果还有个proxy2.c也要用到cache,那就没得用了。还是治标不治本。

           方案二:用指针传递来在cahce.c中修改proxy.c定义的cache_t cache。这方法其实不难,我最后也是采用的这个方法。坏就坏在昨晚写的代码简直就是一坨,好像用了指针传递又好像没用,我就在这一坨东西上反复修改,然后越陷越深。理清了思路之后问题就迎刃而解了。

    还有几个让我困惑的小问题:

    ①malloc问题

    1. void cache_init(cache_t *cache) {
    2. // cache=(cache_t*)Malloc(sizeof(cache_t));
    3. cache->cache_Item_Using = 0;
    4. }

    在这里我开始准备模仿sbuf.c给cache分配个空间再说,结果测试的时候一直报错“Segmentation fault”,搞得我一度以为是malloc出了问题。最后把参数传递的问题解决后发现这里的malloc可用可不用,由于cache_t cache是全局变量,它的生命周期和进行同步,故没必要多此一举再在堆上给它分配空间了。

    ②url问题

    1. printf("=====The length of url is: %d====\n",(int)sizeof(url));
    2. // strncpy(item->url,url,sizeof(url)); 西八,这个bug害得我好苦啊
    3. strncpy(item->url,url,MAXLINE);

    在解决缓存的问题后,我发现cache部分的得分依然是0昏。初步判断是url的匹配有问题,测试了一番后发现一个十分怪异的情况——存入的url长度并不能用sizeof(url),实际长度好像比sizeof(url)还要长。

     

     

    把每次存进cache的item->url打印出来一看果然如此,于是最后直接假设url的长度是MAXLINE,这样就可以通过测试了

     

    Q:例如char* url=“http://localhost:7710/”

    那sizeof(url)是输出8还是字符串本身的长度22呢?

    A:    在这种情况下,char* url = "http://localhost:7710/"中的url是一个指向字符的指针,指向字符串常量"http://localhost:7710/"的首地址。因此,使用sizeof(url)将返回指针的大小,而不是字符串的长度。

    在大多数平台上,指针的大小通常是机器字长的大小。在64位系统上,指针通常是8个字节,因此sizeof(url)将返回8。

    要获取字符串本身的长度,您可以使用strlen函数,如下所示:

    1. #include
    2. #include
    3. int main() {
    4. char *url = "http://localhost:7710/";
    5. size_t length = strlen(url);
    6. printf("Length of the string: %zu\n", length); // 这里将输出字符串的长度,即22
    7. return 0;
    8. }

    这段代码将输出字符串的长度,即22。

    但是,由于strlen(str)不会包括结尾的‘\0’,所以得用strncpy(item->url,url,strlen(url)+1)

    至此,真相大白。 

    csche.c如下
     

    1. #include "csapp.h"
    2. #include "cache.h"
    3. void initializeCache(cache_t* cache){
    4. cache->head = Malloc(sizeof(*(cache->head)));
    5. cache->head->flag = '@';
    6. cache->head->prev = NULL;
    7. cache->head->next = NULL;
    8. cache->tail = Malloc(sizeof(*(cache->tail)));
    9. cache->tail->flag = '@';
    10. cache->tail->prev = NULL;
    11. cache->tail->next = NULL;
    12. /* construct the doubly linked list */
    13. cache->head->next = cache->tail;
    14. cache->tail->prev = cache->head;
    15. cache->nitems = 0;
    16. }

    cache.h实现如下

    1. #include "csapp.h"
    2. #define MAX_CACHE_SIZE 1049000
    3. #define MAX_OBJECT_SIZE 102400
    4. typedef struct _obj_t{
    5. char flag;
    6. char uri[100];
    7. char respHeader[1024];
    8. char respBody[MAX_OBJECT_SIZE];
    9. int respHeaderLen;
    10. int respBodyLen;
    11. struct _obj_t* prev;
    12. struct _obj_t* next;
    13. }obj_t;
    14. typedef struct _cache_t{
    15. obj_t* head;
    16. obj_t* tail;
    17. int nitems;
    18. }cache_t;
    19. //write to cache
    20. //read cache
    21. //search cache
    22. void initializeCache(cache_t* );

    写入到cache的实现如下

    1. /*
    2. * This function is guarded by Write Lock, thus is thread safe
    3. * assume head is the newest part, we evict the last part
    4. * if possible
    5. */
    6. void writeToCache(obj_t* obj){
    7. /* step1: check current capacity, if full ,delete one */
    8. while(obj->respBodyLen + cacheSize > MAX_CACHE_SIZE && cache.head->next != cache.tail){
    9. obj_t* last = cache.tail->prev;
    10. last->next->prev = last->prev;
    11. last->prev->next = last->next;
    12. last->next = NULL;
    13. last->prev = NULL;
    14. Free(last);
    15. }
    16. /* step2: add into the cache */
    17. //mount the current obj into cache
    18. obj->next = cache.head->next;
    19. obj->prev = cache.head;
    20. cache.head->next->prev = obj;
    21. cache.head->next = obj;
    22. cacheSize += obj->respBodyLen;
    23. }

     

     读取cahce条目的实现如下

    1. obj_t* readItem(char* targetURI, int clientfd){
    2. P(&mutex);
    3. readcnt++;
    4. if(readcnt == 1){
    5. P(&W);
    6. }
    7. V(&mutex);
    8. /***** reading section starts *****/
    9. obj_t* cur = cache.head->next;
    10. rio_t rio;
    11. Rio_readinitb(&rio, clientfd);
    12. while(cur->flag != '@'){
    13. if(strcmp(targetURI, cur->uri) == 0){
    14. return cur;
    15. }
    16. cur = cur->next;
    17. }
    18. /***** reading section ends *****/
    19. P(&mutex);
    20. readcnt--;
    21. if(readcnt == 0){
    22. V(&W);
    23. }
    24. V(&mutex);
    25. return NULL;
    26. }

     

     

  • 相关阅读:
    白嫖阿里云服务器教程来了,薅秃阿里云!
    优化算法 - 梯度下降
    算法——双指针
    Spring的RestTemplate学习
    ModuleNotFoundError: No module named ‘sklearn.cross_validation‘
    华为HCDA论证教程
    数据治理容易走进哪些误区?
    第五章 神经网络(下)
    JavaScript系列从入门到精通系列第十七篇:JavaScript中的全局作用域
    数字逻辑·数制转换、编码方法
  • 原文地址:https://blog.csdn.net/John_Snowww/article/details/139370089