蓝色的思念
突然演变成了阳光的夏天
空气中的温暖不会很遥远
——被风吹过的夏天
完整代码见:CSAPP/proxylab-handout at main · SnowLegend-star/CSAPP (github.com)
Q:计算机网络中port的作用是什么?
A:在计算机网络中,"port"(端口)是用于标识网络通信中的特定应用程序或服务的数字标识符。端口是网络通信的重要组成部分,它允许多个应用程序在同一台计算机上共享网络连接,并确保正确地将数据传递给目标应用程序。
端口通常与 IP 地址结合使用,构成了网络通信中的目标地址。IP 地址用于标识网络中的设备,而端口则用于标识设备上的具体应用程序或服务。
端口的作用包括:
总之,端口在计算机网络中起着非常重要的作用,它们允许多个应用程序共享网络连接,并确保数据被正确地路由到目标应用程序或服务。
今天突然想把《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
第一部分要求我们实现一个简单的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的实现如下
- #include
- #include"csapp.h"
- #include"sbuf.h"
-
- /* Recommended max cache and object sizes */
- #define MAX_CACHE_SIZE 1049000
- #define MAX_OBJECT_SIZE 102400
- #define HTTP_PREFIX "http://"
-
- /* 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";
-
- void handleRequest(int fd);
- void parseRequest(char* buf, char* host, char* port, char* method, char* url, char* version, char* fileName);
- void clientError(int fd, char* cause, char* errnum, char* errmsg, char* errmsg_datail);
- int readAndFormatRequestHeader(rio_t* rio, char* clientRequest, char* Host, char* port,
- char* method, char* url, char* version, char* filename);
-
- //和tiny.c里面的那个差不多
- void clientError(int fd, char* cause, char* errnum, char* errmsg, char* errmsg_datail){
- char buf[MAXLINE];
-
- //打印HTTP的响应头
- sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, errmsg);
- Rio_writen(fd, buf, strlen(buf));
- sprintf(buf, "Content-type: text/html\r\n\r\n");
- Rio_writen(fd, buf, strlen(buf));
-
- //打印HTTP响应的主体
- sprintf(buf, "
Tiny Error "); - Rio_writen(fd,buf,strlen(buf));
- sprintf(buf, ""ffffff"">\r\n");
- Rio_writen(fd,buf,strlen(buf));
- sprintf(buf, "%s: %s\r\n", errmsg, errmsg);
- Rio_writen(fd, buf, strlen(buf));
- sprintf(buf, "
%s: %s\r\n"
, errmsg_datail, cause); - Rio_writen(fd, buf, strlen(buf));
- sprintf(buf, "
The Tiny Web Server\r\n"); - Rio_writen(fd, buf, strlen(buf));
- }
-
- void handleRequest(int fd){
- //处理各种定位的指针
- char* pos=NULL;
-
- //分割http请求的参数
- char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE],fileName[MAXLINE];
-
- //客户端的几个请求头的主体
- char clientRequest[MAXLINE];
- char hostName[MAXLINE],port[MAXLINE];
-
- //proxy-client和proxy-server的IO
- rio_t rio_Proxy2Client,rio_Proxy2Server;
-
- //Step1: proxy读入来自client的请求
- Rio_readinitb(&rio_Proxy2Client,fd);
- if(Rio_readlineb(&rio_Proxy2Client,buf,MAXLINE)==0){
- //这个请求是空的
- printf("Oops! empty request\n");
- return ;
- }
-
- //如果http的版本是1.1,处理成1.0
- if((pos=strstr(buf,"HTTP/1.1"))!=NULL){
- buf[pos-buf-1+strlen("HTTP/1.1")]='0';
- }
-
- //Step2: 分割请求
- parseRequest(buf, hostName, port, method, uri, version, fileName);
-
- //判断请求是否有效
- if(strcasecmp(method,"GET")!=0){
- clientError(fd, method, "501", "Not Implement", "Tiny Does not implement this method");
- return ;
- }
-
- int rv=readAndFormatRequestHeader(&rio_Proxy2Client, clientRequest, hostName, port, method, uri, version, fileName);
- if(rv==0){
- return ;
- }
-
- //Step3: tiny server和proxy建立连接
- int clientfd=Open_clientfd(hostName, port);
-
- Rio_readinitb(&rio_Proxy2Server, clientfd);
- Rio_writen(rio_Proxy2Server.rio_fd, clientRequest, strlen(clientRequest));
-
- //Step4: 从tiny server读入response,并且把它发送给client
- printf("The Proxy is ready to relay the response\n");
-
- char tinyResponse[MAXLINE];
- int n;
-
- while((n=Rio_readlineb(&rio_Proxy2Server, tinyResponse, MAXLINE))!=0){
- Rio_writen(fd, tinyResponse, n);
- }
- }
-
-
- //client request is like this
- //GET http://www.cmu.edu/hub/index.html HTTP/1.1
- void parseRequest(char* buf, char* host, char* port, char* method, char* url, char* version, char* fileName){
- sscanf(buf,"%s %s %s", method, url, version);
- //method = "GET", url = "http://localhost:15213/home.html", version = "HTTP1.0"
-
- char* host_pos =strstr(url,HTTP_PREFIX)+strlen(HTTP_PREFIX); //主机名开始的位置
- char* port_pos =strstr(host_pos,":"); //端口开始的位置
- char* slash_pos=strstr(host_pos,"/"); //suffix开始的位置
-
- //判断url有没有带端口号,如果没带就是默认端口80
- if(port_pos==NULL){ //没带端口号
- strcpy(port,"80");
- strncmp(host,host_pos,slash_pos-host_pos);
- }
- else{
- strncpy(host,host_pos,port_pos-host_pos);
- strncpy(port,port_pos+1,slash_pos-port_pos-1);
- }
-
- strcpy(fileName,slash_pos);
- printf("HostName: %s",host);
- printf("Port: %s",port);
- printf("fileName: %s",fileName);
- }
-
- int readAndFormatRequestHeader(rio_t* rio, char* clientRequest, char* Host, char* port,
- char* method, char* url, char* version, char* fileName){
-
- int UserAgent=0, Connection=0, proxyConnection=0, hostInfo=0;
- char buf[MAXLINE/2];
- int n;
- char* findpos;
-
- sprintf(clientRequest, "GET %s HTTP/1.0\r\n",fileName);
-
- n=Rio_readlineb(rio, buf, MAXLINE);
- printf("receive buf %s\n", buf);
- printf("n = %d", n);
-
- while(strcmp("\r\n",buf)!=0&&n!=0){
- strcat(clientRequest, buf);
- printf("receive buf %s\n", buf);
-
- //判断要求的四个请求头是否存在
- if((findpos=strstr(buf, "User-Agent:"))!=NULL){
- UserAgent=1;
- }
- if((findpos=strstr(buf,"Proxy-Connection:"))!=NULL){
- proxyConnection=1;
- }
- if((findpos=strstr(buf,"Connection"))!=NULL){
- Connection=1;
- }
- if((findpos=strstr(buf, "Host"))!=NULL){
- hostInfo=1;
- }
-
- n=Rio_readlineb(rio, buf ,MAXLINE);
- }
-
- if(n==0){
- return 0;
- }
-
-
- //如果缺失了这四个头部,则进行添加
- if(hostInfo==0){
- sprintf(buf, "Host: %s\r\n", Host);
- strcat(clientRequest, buf);
- }
-
- if(UserAgent==0){
- strcat(clientRequest, user_agent_hdr);
- }
-
- if(Connection==0){
- sprintf(buf, "Connection: close\r\n");
- strcat(clientRequest, buf);
- }
-
- if(proxyConnection==0){
- sprintf(buf, "Proxy-Connection: close\r\n");
- strcat(clientRequest, buf);
- }
-
- //添加最后的空行
- strcat(clientRequest,"\r\n");
- return 1;
- }
-
- int main(int argc,char** argv){
- if(argc!=2){
- unix_error("proxy usage: ./proxy
" ); - }
-
- int listenfd=Open_listenfd(argv[1]);
- struct sockaddr_storage clientaddr;
- char hostName[MAXLINE], port[MAXLINE];
-
- while(1){
- socklen_t clientlen=sizeof(struct sockaddr_storage);
- int connfd=Accept(listenfd,(SA*) &clientaddr,&clientlen);
-
- Getnameinfo((SA*) &clientaddr,clientlen,hostName,MAXLINE,port,MAXLINE,0);
-
- handleRequest(connfd);
- Close(connfd);
- }
- return 0;
- }
书上给出了三种并发编程的方式:
1、基于进程
2、基于I/O多路复用
3、基于线程
不得不说基于线程是最为通俗易懂的,而且书上还有一个基于线程进行并发编程的例子“echoservert-pre.c”。结合这份代码,在Part I的基础上稍作改动即可。
对了,书上还要求在main()函数里面加入Signal(SIGPIPE, SIG_IGN)来屏蔽SIGPIPE信号。我一开始并没有加这个顺利通过了lab,(:
part II实现如下
- #include
- #include"csapp.h"
- #include"sbuf.h"
-
- /* Recommended max cache and object sizes */
- #define MAX_CACHE_SIZE 1049000
- #define MAX_OBJECT_SIZE 102400
- #define HTTP_PREFIX "http://"
- #define NTHREADS 4
- #define SBUFSIZE 16
-
- sbuf_t sbuf; //shared buffer of connected descriptor
- 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";
-
- void handleRequest(int fd);
- void parseRequest(char* buf, char* host, char* port, char* method, char* url, char* version, char* fileName);
- void clientError(int fd, char* cause, char* errnum, char* errmsg, char* errmsg_datail);
- int readAndFormatRequestHeader(rio_t* rio, char* clientRequest, char* Host, char* port,
- char* method, char* url, char* version, char* filename);
-
- //和tiny.c里面的那个差不多
- void clientError(int fd, char* cause, char* errnum, char* errmsg, char* errmsg_datail){
- char buf[MAXLINE];
-
- //打印HTTP的响应头
- sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, errmsg);
- Rio_writen(fd, buf, strlen(buf));
- sprintf(buf, "Content-type: text/html\r\n\r\n");
- Rio_writen(fd, buf, strlen(buf));
-
- //打印HTTP响应的主体
- sprintf(buf, "
Tiny Error "); - Rio_writen(fd,buf,strlen(buf));
- sprintf(buf, ""ffffff"">\r\n");
- Rio_writen(fd,buf,strlen(buf));
- sprintf(buf, "%s: %s\r\n", errmsg, errmsg);
- Rio_writen(fd, buf, strlen(buf));
- sprintf(buf, "
%s: %s\r\n"
, errmsg_datail, cause); - Rio_writen(fd, buf, strlen(buf));
- sprintf(buf, "
The Tiny Web Server\r\n"); - Rio_writen(fd, buf, strlen(buf));
- }
-
- void handleRequest(int fd){
- //处理各种定位的指针
- char* pos=NULL;
-
- //分割http请求的参数
- char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE],fileName[MAXLINE];
-
- //客户端的几个请求头的主体
- char clientRequest[MAXLINE];
- char hostName[MAXLINE],port[MAXLINE];
-
- //proxy-client和proxy-server的IO
- rio_t rio_Proxy2Client,rio_Proxy2Server;
-
- //Step1: proxy读入来自client的请求
- Rio_readinitb(&rio_Proxy2Client,fd);
- if(Rio_readlineb(&rio_Proxy2Client,buf,MAXLINE)==0){
- //这个请求是空的
- printf("Oops! empty request\n");
- return ;
- }
-
- //如果http的版本是1.1,处理成1.0
- if((pos=strstr(buf,"HTTP/1.1"))!=NULL){
- buf[pos-buf-1+strlen("HTTP/1.1")]='0';
- }
-
- //Step2: 分割请求
- parseRequest(buf, hostName, port, method, uri, version, fileName);
-
- //判断请求是否有效
- if(strcasecmp(method,"GET")!=0){
- clientError(fd, method, "501", "Not Implement", "Tiny Does not implement this method");
- return ;
- }
-
- int rv=readAndFormatRequestHeader(&rio_Proxy2Client, clientRequest, hostName, port, method, uri, version, fileName);
- if(rv==0){
- return ;
- }
-
- //Step3: tiny server和proxy建立连接
- int clientfd=Open_clientfd(hostName, port);
-
- Rio_readinitb(&rio_Proxy2Server, clientfd);
- Rio_writen(rio_Proxy2Server.rio_fd, clientRequest, strlen(clientRequest));
-
- //Step4: 从tiny server读入response,并且把它发送给client
- printf("The Proxy is ready to relay the response\n");
-
- char tinyResponse[MAXLINE];
- int n;
-
- while((n=Rio_readlineb(&rio_Proxy2Server, tinyResponse, MAXLINE))!=0){
- Rio_writen(fd, tinyResponse, n);
- }
- }
-
-
- //client request is like this
- //GET http://www.cmu.edu/hub/index.html HTTP/1.1
- void parseRequest(char* buf, char* host, char* port, char* method, char* url, char* version, char* fileName){
- sscanf(buf,"%s %s %s", method, url, version);
- //method = "GET", url = "http://localhost:15213/home.html", version = "HTTP1.0"
-
- char* host_pos =strstr(url,HTTP_PREFIX)+strlen(HTTP_PREFIX); //主机名开始的位置
- char* port_pos =strstr(host_pos,":"); //端口开始的位置
- char* slash_pos=strstr(host_pos,"/"); //suffix开始的位置
-
- //判断url有没有带端口号,如果没带就是默认端口80
- if(port_pos==NULL){ //没带端口号
- strcpy(port,"80");
- strncmp(host,host_pos,slash_pos-host_pos);
- }
- else{
- strncpy(host,host_pos,port_pos-host_pos);
- strncpy(port,port_pos+1,slash_pos-port_pos-1);
- }
-
- strcpy(fileName,slash_pos);
- printf("HostName: %s",host);
- printf("Port: %s",port);
- printf("fileName: %s",fileName);
- }
-
- int readAndFormatRequestHeader(rio_t* rio, char* clientRequest, char* Host, char* port,
- char* method, char* url, char* version, char* fileName){
-
- int UserAgent=0, Connection=0, proxyConnection=0, hostInfo=0;
- char buf[MAXLINE/2];
- int n;
- char* findpos;
-
- sprintf(clientRequest, "GET %s HTTP/1.0\r\n",fileName);
-
- n=Rio_readlineb(rio, buf, MAXLINE);
- printf("receive buf %s\n", buf);
- printf("n = %d", n);
-
- while(strcmp("\r\n",buf)!=0&&n!=0){
- strcat(clientRequest, buf);
- printf("receive buf %s\n", buf);
-
- //判断要求的四个请求头是否存在
- if((findpos=strstr(buf, "User-Agent:"))!=NULL){
- UserAgent=1;
- }
- if((findpos=strstr(buf,"Proxy-Connection:"))!=NULL){
- proxyConnection=1;
- }
- if((findpos=strstr(buf,"Connection"))!=NULL){
- Connection=1;
- }
- if((findpos=strstr(buf, "Host"))!=NULL){
- hostInfo=1;
- }
-
- n=Rio_readlineb(rio, buf ,MAXLINE);
- }
-
- if(n==0){
- return 0;
- }
-
-
- //如果缺失了这四个头部,则进行添加
- if(hostInfo==0){
- sprintf(buf, "Host: %s\r\n", Host);
- strcat(clientRequest, buf);
- }
-
- if(UserAgent==0){
- strcat(clientRequest, user_agent_hdr);
- }
-
- if(Connection==0){
- sprintf(buf, "Connection: close\r\n");
- strcat(clientRequest, buf);
- }
-
- if(proxyConnection==0){
- sprintf(buf, "Proxy-Connection: close\r\n");
- strcat(clientRequest, buf);
- }
-
- //添加最后的空行
- strcat(clientRequest,"\r\n");
- return 1;
- }
-
- int main(int argc,char** argv){
- if(argc!=2){
- unix_error("proxy usage: ./proxy
" ); - }
-
- int listenfd=Open_listenfd(argv[1]), i;
- pthread_t tid;
- struct sockaddr_storage clientaddr;
- char hostName[MAXLINE], port[MAXLINE];
- sbuf_init(&sbuf, SBUFSIZE);
-
- for(i=0;i
//创建工作线程 - Pthread_create(&tid, NULL, thread, NULL);
- }
-
- while(1){
- socklen_t clientlen=sizeof(struct sockaddr_storage);
- int connfd=Accept(listenfd,(SA*) &clientaddr,&clientlen);
- //Getnameinfo((SA*)&clientaddr, clientlen, hostName, MAXLINE, port, MAXLINE, 0);
- sbuf_insert(&sbuf, connfd);
- }
- return 0;
- }
-
- void *thread(void *vargp){
- pthread_detach(pthread_self());
- while(1){
- int connfd=sbuf_remove(&sbuf);
- handleRequest(connfd);
- Close(connfd);
- }
- }
最后一部分写的我汗流浃背了。
这一部分要求我们给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问题
- void cache_init(cache_t *cache) {
- // cache=(cache_t*)Malloc(sizeof(cache_t));
- cache->cache_Item_Using = 0;
- }
在这里我开始准备模仿sbuf.c给cache分配个空间再说,结果测试的时候一直报错“Segmentation fault”,搞得我一度以为是malloc出了问题。最后把参数传递的问题解决后发现这里的malloc可用可不用,由于cache_t cache是全局变量,它的生命周期和进行同步,故没必要多此一举再在堆上给它分配空间了。
②url问题
- printf("=====The length of url is: %d====\n",(int)sizeof(url));
- // strncpy(item->url,url,sizeof(url)); 西八,这个bug害得我好苦啊
- 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函数,如下所示:
- #include
-
- #include
-
- int main() {
-
- char *url = "http://localhost:7710/";
-
- size_t length = strlen(url);
-
- printf("Length of the string: %zu\n", length); // 这里将输出字符串的长度,即22
-
- return 0;
-
- }
这段代码将输出字符串的长度,即22。
但是,由于strlen(str)不会包括结尾的‘\0’,所以得用strncpy(item->url,url,strlen(url)+1)
至此,真相大白。
csche.c如下
- #include "csapp.h"
- #include "cache.h"
-
- void initializeCache(cache_t* cache){
- cache->head = Malloc(sizeof(*(cache->head)));
- cache->head->flag = '@';
- cache->head->prev = NULL;
- cache->head->next = NULL;
-
- cache->tail = Malloc(sizeof(*(cache->tail)));
- cache->tail->flag = '@';
- cache->tail->prev = NULL;
- cache->tail->next = NULL;
-
- /* construct the doubly linked list */
- cache->head->next = cache->tail;
- cache->tail->prev = cache->head;
-
- cache->nitems = 0;
- }
cache.h实现如下
- #include "csapp.h"
- #define MAX_CACHE_SIZE 1049000
- #define MAX_OBJECT_SIZE 102400
-
- typedef struct _obj_t{
- char flag;
- char uri[100];
- char respHeader[1024];
- char respBody[MAX_OBJECT_SIZE];
- int respHeaderLen;
- int respBodyLen;
- struct _obj_t* prev;
- struct _obj_t* next;
- }obj_t;
-
- typedef struct _cache_t{
- obj_t* head;
- obj_t* tail;
- int nitems;
- }cache_t;
-
- //write to cache
- //read cache
- //search cache
-
- void initializeCache(cache_t* );
写入到cache的实现如下
- /*
- * This function is guarded by Write Lock, thus is thread safe
- * assume head is the newest part, we evict the last part
- * if possible
- */
- void writeToCache(obj_t* obj){
- /* step1: check current capacity, if full ,delete one */
- while(obj->respBodyLen + cacheSize > MAX_CACHE_SIZE && cache.head->next != cache.tail){
- obj_t* last = cache.tail->prev;
- last->next->prev = last->prev;
- last->prev->next = last->next;
-
- last->next = NULL;
- last->prev = NULL;
- Free(last);
- }
-
- /* step2: add into the cache */
- //mount the current obj into cache
- obj->next = cache.head->next;
- obj->prev = cache.head;
- cache.head->next->prev = obj;
- cache.head->next = obj;
- cacheSize += obj->respBodyLen;
- }
读取cahce条目的实现如下
- obj_t* readItem(char* targetURI, int clientfd){
- P(&mutex);
- readcnt++;
- if(readcnt == 1){
- P(&W);
- }
- V(&mutex);
-
- /***** reading section starts *****/
- obj_t* cur = cache.head->next;
- rio_t rio;
- Rio_readinitb(&rio, clientfd);
- while(cur->flag != '@'){
- if(strcmp(targetURI, cur->uri) == 0){
- return cur;
- }
-
- cur = cur->next;
- }
-
-
- /***** reading section ends *****/
- P(&mutex);
- readcnt--;
- if(readcnt == 0){
- V(&W);
- }
- V(&mutex);
-
- return NULL;
- }