要么改变世界,要么适应世界

CS:APP-Proxy Lab(CS:APP系列实验完结篇)

2023-12-10 17:20:46
171
目录

前言

本实验要求我们实现一个代理服务器,充当一个在用户和目标服务器之间的桥梁角色,深入了解各个主机之间建立连接的流程。话不多说,打开电脑,带上键盘,开启实验!

Part Ⅰ

参照tiny.c的main函数以及教材663页的图11-20的程序,我们要在我们的代理程序中,使用Open_listenfdAccept等待客户端发起连接,客户端连接我们的代理后,我们再使用Open_clientfd和目标服务器建立连接,然后将目标服务器返回的响应直接返回到客户端即可。

在这个过程中,有一些注意的点:

  • uri会被包装过,会被添加host:port前缀,我们可以从里边解析出原始的uri、目标host和目标port
  • 我们发送请求行时,要发送http/1.0,而不是http/1.1
  • 目标服务器返回的内容可能同时存在二进制和文本,因此我们要用Rio_readnb函数读取服务端返回的内容

代码如下:

#include <stdio.h>
#include "csapp.h"
/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400

void doit(int fd);
void clienterror(int fd, char *cause, char *errnum, 
		 char *shortmsg, char *longmsg);
void read_and_send_requesthdrs(rio_t* rp, int server_fd);
void parse_header(char* buf, char* key, char* value);
void get_target_server_info(char* uri, char* target_host, char* target_port);
void get_origin_uri(char* old_uri, char* new_uri);
/* 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";

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

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

    // argv[1] 是代理的工作端口
    listenfd = Open_listenfd(argv[1]);
    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);
        doit(connfd);                                             //line:netp:tiny:doit
        Close(connfd);                                            //line:netp:tiny:close
    }
}

void doit(int cliend_fd) {
    char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
    rio_t client_rio;

    /* Read request line and headers */
    Rio_readinitb(&client_rio, cliend_fd);
    if (!Rio_readlineb(&client_rio, buf, MAXLINE))  //line:netp:doit:readrequest
        return;
    printf("client buf = %s", buf);
    // 解析请求行,转为三个参数
    sscanf(buf, "%s %s %s", method, uri, version);       //line:netp:doit:parserequest
    if (strcasecmp(method, "GET")) {                     //line:netp:doit:beginrequesterr
        clienterror(cliend_fd, method, "501", "Not Implemented",
            "Tiny does not implement this method");
        return;
    }                                                    //line:netp:doit:endrequesterr
    char target_host[100], target_port[6];
    get_target_server_info(uri, target_host, target_port);

    // 和目标服务器建立连接
    int server_fd = Open_clientfd(target_host, target_port);
    printf("server_fd = %d\n", server_fd);

    // 先发送请求行 
    char new_uri[MAXLINE];
    get_origin_uri(uri, new_uri);
    printf("client's target_host : %s, target_port : %s target_uri : %s\n", target_host, target_port, new_uri);
    sprintf(buf, "%s %s %s\n", method, new_uri, "HTTP/1.0");
    Rio_writen(server_fd, buf, strlen(buf));

    rio_t server_rio;
    Rio_readinitb(&server_rio, server_fd);
    // 再发送请求报头 
    read_and_send_requesthdrs(&client_rio, server_fd);

    // 读取目标服务器返回的内容
    char rsp_buf[MAXLINE];
    // 由于响应包可能同时有文本和二进制,因此应该用 Rio_readnb
    while(Rio_readnb(&server_rio, rsp_buf, sizeof(rsp_buf))){
        printf("rsp_buf = %s\n", rsp_buf);
        // 发送目标服务返回的响应给客户端
        Rio_writen(cliend_fd, rsp_buf, sizeof(rsp_buf));
    } 
    
    Close(server_fd);
}

// 将包装过的请求uri解析出原始的uri
void get_origin_uri(char* old_uri, char* new_uri) {
    int old_uri_idx = 0;
    // 跳过协议头
    while (old_uri[old_uri_idx] != '/') {
        old_uri_idx++;
    }
    // 跳过两个斜杠
    old_uri_idx += 2;
    // 跳过域名
    while (old_uri[old_uri_idx] != ':') {
        old_uri_idx++;
    }
    // 跳过冒号
    old_uri_idx++;
    // 跳过端口
    while (old_uri[old_uri_idx] >= '0' && old_uri[old_uri_idx] <= '9') {
        old_uri_idx++;
    }
    strcpy(new_uri, old_uri + old_uri_idx);
}

// 将包装过的请求uri解析出host和port
void get_target_server_info(char* uri, char* target_host, char* target_port) {
    int idx = 0;
    while (uri[idx] != ':') {
        idx++;
    }
    // 跳过第一个冒号和协议后面的两个斜杠
    idx += 3;
    int host_idx = 0;
    while (uri[idx] != ':') {
        target_host[host_idx++] = uri[idx++];
    }
    target_host[host_idx] = '\0';
    // 跳过第二个冒号
    idx++;
    int port_idx = 0;
    while (uri[idx] >= '0' && uri[idx] <= '9') {
        target_port[port_idx++] = uri[idx++];
    }
    target_port[port_idx] = '\0';
}

// 解析header中的key和对应的value
void parse_header(char* buf, char* key, char* value) {
    int buf_idx = 0;
    while (buf[buf_idx] != ':') {
        key[buf_idx] = buf[buf_idx];
        buf_idx++;
    }
    // 跳过冒号和空格
    buf_idx++;
    while (buf[buf_idx] == ' ')buf_idx++;
    strcpy(value, buf + buf_idx);
}
void read_and_send_requesthdrs(rio_t* rp, int server_fd) {

    char buf[MAXLINE];
    // char key[MAXLINE], value[MAXLINE];
    // 按行读取,直到遇到 \r\n
    while (strcmp(buf, "\r\n")) {          //line:netp:readhdrs:checkterm
        Rio_readlineb(rp, buf, MAXLINE);
        // 发送内容到目标服务器
        Rio_writen(server_fd, buf, strlen(buf));
        // parse_header(buf, key, value);
        printf("%s", buf);
    }
    Rio_writen(server_fd, "\r\n", strlen("\r\n"));
    return;
}

void clienterror(int fd, char* cause, char* errnum,
    char* shortmsg, char* longmsg) {
    char buf[MAXLINE];

    /* Print the HTTP response headers */
    sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg);
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Content-type: text/html\r\n\r\n");
    Rio_writen(fd, buf, strlen(buf));

    /* Print the HTTP response body */
    sprintf(buf, "<html><title>Tiny Error</title>");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "<body bgcolor=""ffffff"">\r\n");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "%s: %s\r\n", errnum, shortmsg);
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "<p>%s: %s\r\n", longmsg, cause);
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "<hr><em>The Tiny Web server</em>\r\n");
    Rio_writen(fd, buf, strlen(buf));
}

测试前别忘了给nop-server.py执行权限(chmod +x nop-server.py ):

make
./driver.sh
*** Basic ***
Starting tiny on 33356
Starting proxy on 29771
1: home.html
   Fetching ./tiny/home.html into ./.proxy using the proxy
   Fetching ./tiny/home.html into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
2: csapp.c
   Fetching ./tiny/csapp.c into ./.proxy using the proxy
   Fetching ./tiny/csapp.c into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
3: tiny.c
   Fetching ./tiny/tiny.c into ./.proxy using the proxy
   Fetching ./tiny/tiny.c into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
4: godzilla.jpg
   Fetching ./tiny/godzilla.jpg into ./.proxy using the proxy
   Fetching ./tiny/godzilla.jpg into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
5: tiny
   Fetching ./tiny/tiny into ./.proxy using the proxy
   Fetching ./tiny/tiny into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
Killing tiny and proxy
basicScore: 40/40

*** Concurrency ***
Starting tiny on port 12570
Starting proxy on port 10147
Starting the blocking NOP server on port 29573
Timeout waiting for the server to grab the port reserved for it
Terminated

我们可以看到,第一部分我们已经通过样例。

Part Ⅱ

在这一部分中,主要实现并发功能,主要函数方面没有多大的变化,这里使用预线程化的思想,参考了教材12.5.5的代码

先添加sbuf.csbuf.csbuf.c程序实现方式来自教材12.5.4,sbuf.h内容如下:

typedef struct{
    int *buf;
    int n;
    int front;
    int rear;
    sem_t mutex;
    sem_t slots;
    sem_t items;
} sbuf_t;

void sbuf_init(sbuf_t *sp, int n);
void sbuf_deinit(sbuf_t *sp);
void sbuf_insert(sbuf_t *sp, int item);
int sbuf_remove(sbuf_t *sp);

然后修改Makefile文件如下:

# Makefile for Proxy Lab 
#
# You may modify this file any way you like (except for the handin
# rule). You instructor will type "make" on your specific Makefile to
# build your proxy from sources.

CC = gcc
CFLAGS = -g -Wall
LDFLAGS = -lpthread

all: proxy

csapp.o: csapp.c csapp.h
	$(CC) $(CFLAGS) -c csapp.c

proxy.o: proxy.c csapp.h
	$(CC) $(CFLAGS) -c proxy.c

sbuf.o: csapp.o sbuf.c
	$(CC) $(CFLAGS) -c sbuf.c

proxy: proxy.o csapp.o sbuf.o
	$(CC) $(CFLAGS) proxy.o csapp.o sbuf.o -o proxy $(LDFLAGS)

# Creates a tarball in ../proxylab-handin.tar that you can then
# hand in. DO NOT MODIFY THIS!
handin:
	(make clean; cd ..; tar cvf $(USER)-proxylab-handin.tar proxylab-handout --exclude tiny --exclude nop-server.py --exclude proxy --exclude driver.sh --exclude port-for-user.pl --exclude free-port.sh --exclude ".*")

clean:
	rm -f *~ *.o proxy core *.tar *.zip *.gzip *.bzip *.gz

proxy.c的改动如下:

--- a/Proxy-Lab/proxy.c
+++ b/Proxy-Lab/proxy.c
@@ -1,19 +1,26 @@
 #include <stdio.h>
 #include "csapp.h"
+#include "sbuf.h"
 /* Recommended max cache and object sizes */
 #define MAX_CACHE_SIZE 1049000
 #define MAX_OBJECT_SIZE 102400

+#define N_THREADS 4
+#define SBUF_SIZE 16
+
 void doit(int fd);
 void clienterror(int fd, char *cause, char *errnum,
-                char *shortmsg, char *longmsg);
+         char *shortmsg, char *longmsg);
 void read_and_send_requesthdrs(rio_t* rp, int server_fd);
 void parse_header(char* buf, char* key, char* value);
 void get_target_server_info(char* uri, char* target_host, char* target_port);
 void get_origin_uri(char* old_uri, char* new_uri);
+void *thread_handle(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;
+
 int main(int argc, char** argv) {
     int listenfd, connfd;
     char hostname[MAXLINE], port[MAXLINE];
@@ -28,6 +35,15 @@ int main(int argc, char** argv) {

     // argv[1] 是代理的工作端口
     listenfd = Open_listenfd(argv[1]);
+
+    // 初始化缓冲区
+    sbuf_init(&sbuf, SBUF_SIZE);
+    // 创建工作线程组
+    pthread_t tid;
+    for(int i = 0; i < N_THREADS; i++){
+        Pthread_create(&tid, NULL, thread_handle, NULL);
+    }
+
     while (1) {
         clientlen = sizeof(clientaddr);
         // 等待客户端发起连接
@@ -35,8 +51,17 @@ int main(int argc, char** argv) {
         Getnameinfo((SA*)&clientaddr, clientlen, hostname, MAXLINE,
             port, MAXLINE, 0);
         printf("Accepted connection from (%s, %s)\n", hostname, port);
-        doit(connfd);                                             //line:netp:tiny:doit
-        Close(connfd);                                            //line:netp:tiny:close
+        // doit(connfd);
+        sbuf_insert(&sbuf, connfd);
+    }
+}
+
+void *thread_handle(void *vargp){
+    Pthread_detach(pthread_self());
+    while(1){
+        int connfd = sbuf_remove(&sbuf);
+        doit(connfd);
+        Close(connfd);
     }
 }

在测试之前,我们要把nop-server.py的第一行改为#!/usr/bin/python3,否则可能会使用默认的python2

测试

make
./driver.sh
*** Basic ***
Starting tiny on 23143
Starting proxy on 27741
1: home.html
   Fetching ./tiny/home.html into ./.proxy using the proxy
   Fetching ./tiny/home.html into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
2: csapp.c
   Fetching ./tiny/csapp.c into ./.proxy using the proxy
   Fetching ./tiny/csapp.c into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
3: tiny.c
   Fetching ./tiny/tiny.c into ./.proxy using the proxy
   Fetching ./tiny/tiny.c into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
4: godzilla.jpg
   Fetching ./tiny/godzilla.jpg into ./.proxy using the proxy
   Fetching ./tiny/godzilla.jpg into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
5: tiny
   Fetching ./tiny/tiny into ./.proxy using the proxy
   Fetching ./tiny/tiny into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
Killing tiny and proxy
basicScore: 40/40

*** Concurrency ***
Starting tiny on port 23053
Starting proxy on port 3541
Starting the blocking NOP server on port 18756
Trying to fetch a file from the blocking nop-server
Fetching ./tiny/home.html into ./.noproxy directly from Tiny
Fetching ./tiny/home.html into ./.proxy using the proxy
Checking whether the proxy fetch succeeded
Success: Was able to fetch tiny/home.html from the proxy.
Killing tiny, proxy, and nop-server
concurrencyScore: 15/15

*** Cache ***
Starting tiny on port 27423
Starting proxy on port 28166
Fetching ./tiny/tiny.c into ./.proxy using the proxy
Fetching ./tiny/home.html into ./.proxy using the proxy
Fetching ./tiny/csapp.c into ./.proxy using the proxy
Killing tiny
Fetching a cached copy of ./tiny/home.html into ./.noproxy
Failure: Was not able to fetch tiny/home.html from the proxy cache.
Killing proxy
cacheScore: 0/15

totalScore: 55/70

Part Ⅲ

在这个部分中,我们需要添加缓存功能。

我们可以以uri作为索引,每次根据这个来寻找缓存中是否有,有的话,直接从缓存中返回,没有则请求对应服务器,然后将响应保存到缓存中。

缓存的实现参考之前的Cache Lab即可,主要是实现一个cache.c

#include <stddef.h>
#include "cache.h"
#include "csapp.h"
// 读优先的方式修改全局cache
sem_t all_cache_mutex, all_cache_w;
int readcnt;
void cache_init(caches *mycaches){
    readcnt = 0;
    mycaches->cnt = 0;
    mycaches->global_clock = 0;
    Sem_init(&all_cache_mutex, 0, 1);
    Sem_init(&all_cache_w, 0, 1);
}
void get_cache(caches *mycaches, char *uri, unsigned char *content, int *status){
    *status = 0;
    P(&all_cache_mutex);
    readcnt++;
    if (readcnt == 1){
        P(&all_cache_w);
    }
    V(&all_cache_mutex);


    for(int i = 0; i < mycaches->cnt; i++){
        if (!strcmp(uri, mycaches->cache_lines[i].uri)){
            memcpy((void*)content, (void*)mycaches->cache_lines[i].object_content, sizeof(mycaches->cache_lines[i].object_content));
            *status = 1;
            break;
        }
    }

    P(&all_cache_mutex);
    readcnt--;
    if (readcnt == 0){
        V(&all_cache_w);
    }
    V(&all_cache_mutex);
}
void put_cache(caches *mycaches, char *uri, unsigned char *content, size_t content_size, int *status){
    *status = 0;
    if(content_size > MAX_OBJECT_SIZE){
        return;
    }
    P(&all_cache_w);
    // 如果已经满了
    if(mycaches->cnt == CACHE_NUM){
        // 寻找时间戳最小的
        int min_clock = mycaches->cache_lines[0].clock, min_idx = 0;
        for(int i = 1; i < CACHE_NUM; i++){
            if(min_clock > mycaches->cache_lines[i].clock){
                min_idx = i;
                min_clock = mycaches->cache_lines[i].clock;
            }
        }
        // printf("cache full, so replace %s!\n", mycaches->cache_lines[min_idx].uri);
        // 替换
        strcpy(mycaches->cache_lines[min_idx].uri, uri);
        memcpy((void*)mycaches->cache_lines[min_idx].object_content, (void*)content, content_size);
        mycaches->cache_lines[min_idx].clock = mycaches->global_clock;
        *status = 1;
    } else{
        strcpy(mycaches->cache_lines[mycaches->cnt].uri, uri);
        memcpy((void*)mycaches->cache_lines[mycaches->cnt].object_content, (void*)content, content_size);
        mycaches->cache_lines[mycaches->cnt].clock = mycaches->global_clock;
        *status = 1;
        mycaches->cnt++;
    }
    mycaches->global_clock++;
    V(&all_cache_w);
}

proxy.c的改动如下:

--- a/Proxy-Lab/proxy.c
+++ b/Proxy-Lab/proxy.c
@@ -1,5 +1,6 @@
 #include <stdio.h>
 #include "csapp.h"
+#include "cache.h"
 #include "sbuf.h"
 /* Recommended max cache and object sizes */
 #define MAX_CACHE_SIZE 1049000
@@ -20,6 +21,7 @@ void *thread_handle(void *vargp);
 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;
+caches my_caches;

 int main(int argc, char** argv) {
     int listenfd, connfd;
@@ -36,6 +38,9 @@ int main(int argc, char** argv) {
     // argv[1] 是代理的工作端口
     listenfd = Open_listenfd(argv[1]);

+    // 初始化 cache
+    cache_init(&my_caches);
+
     // 初始化缓冲区
     sbuf_init(&sbuf, SBUF_SIZE);
     // 创建工作线程组
@@ -83,34 +88,52 @@ void doit(int cliend_fd) {
     }                                                    //line:netp:doit:endrequesterr
     char target_host[100], target_port[6];
     get_target_server_info(uri, target_host, target_port);
-
-    // 和目标服务器建立连接
-    int server_fd = Open_clientfd(target_host, target_port);
-    printf("server_fd = %d\n", server_fd);
-
-    // 先发送请求行
+
     char new_uri[MAXLINE];
     get_origin_uri(uri, new_uri);
-    printf("client's target_host : %s, target_port : %s target_uri : %s\n", target_host, target_port, new_uri);
-    sprintf(buf, "%s %s %s\n", method, new_uri, "HTTP/1.0");
-    Rio_writen(server_fd, buf, strlen(buf));
-
-    rio_t server_rio;
-    Rio_readinitb(&server_rio, server_fd);
-    // 再发送请求报头
-    read_and_send_requesthdrs(&client_rio, server_fd);
-

-    // 读取目标服务器返回的内容
-    char rsp_buf[MAXLINE];
-    // 由于响应包可能同时有文本和二进制,因此应该用 Rio_readnb
-    while(Rio_readnb(&server_rio, rsp_buf, sizeof(rsp_buf))){
-        printf("rsp_buf = %s\n", rsp_buf);
-        // 发送目标服务返回的响应给客户端
-        Rio_writen(cliend_fd, rsp_buf, sizeof(rsp_buf));
-    }
+    int get_cache_status;
+    unsigned char content[MAX_OBJECT_SIZE];
+    // 先根据 uri 查询缓存是否命中
+    get_cache(&my_caches, new_uri, content, &get_cache_status);
+    // 缓存命中
+    if(get_cache_status){
+        printf("cache fit! uri = %s\n", new_uri);
+        Rio_writen(cliend_fd, content, sizeof(content));
+    } else{
+        // 和目标服务器建立连接
+        int server_fd = Open_clientfd(target_host, target_port);
+        printf("server_fd = %d\n", server_fd);
+
+        // 先发送请求行
+        printf("client's target_host : %s, target_port : %s target_uri : %s\n", target_host, target_port, new_uri);
+        sprintf(buf, "%s %s %s\n", method, new_uri, "HTTP/1.0");
+        Rio_writen(server_fd, buf, strlen(buf));
+
+        rio_t server_rio;
+        Rio_readinitb(&server_rio, server_fd);
+        // 再发送请求报头
+        read_and_send_requesthdrs(&client_rio, server_fd);
+
+        // 读取目标服务器返回的内容
+        char rsp_buf[MAXLINE];
+        // 由于响应包可能同时有文本和二进制,因此应该用 Rio_readnb
+        size_t buf_size = 0, total_size = 0;
+        while((buf_size = Rio_readnb(&server_rio, rsp_buf, sizeof(rsp_buf)))){
+            printf("rsp_buf = %s\n", rsp_buf);
+            // 发送目标服务返回的响应给客户端
+            Rio_writen(cliend_fd, rsp_buf, sizeof(rsp_buf));
+            // 接着上次写入的地方,继续将服务器返回的内容复制到content中
+            memcpy((void*)(content + total_size), (void*)rsp_buf, buf_size);
+            total_size += buf_size;
+        }
+        int put_cache_status;
+        // 将本次请求响应放到缓存中
+        put_cache(&my_caches, new_uri, content, total_size, &put_cache_status);
+        printf("cache miss! uri = %s, total_size = %ld, put_cache status = %d\n", new_uri, total_size, put_cache_status);
+        Close(server_fd);
+    }

-    Close(server_fd);
 }

 // 将包装过的请求uri解析出原始的uri

修改Makefile

# Makefile for Proxy Lab 
#
# You may modify this file any way you like (except for the handin
# rule). You instructor will type "make" on your specific Makefile to
# build your proxy from sources.

CC = gcc
CFLAGS = -g -Wall
LDFLAGS = -lpthread

all: proxy

csapp.o: csapp.c csapp.h
	$(CC) $(CFLAGS) -c csapp.c

proxy.o: proxy.c csapp.h
	$(CC) $(CFLAGS) -c proxy.c

sbuf.o: csapp.o sbuf.c
	$(CC) $(CFLAGS) -c sbuf.c

cache.o: csapp.o cache.c
	$(CC) $(CFLAGS) -c cache.c
	
proxy: proxy.o csapp.o sbuf.o cache.o
	$(CC) $(CFLAGS) proxy.o csapp.o sbuf.o cache.o -o proxy $(LDFLAGS)

# Creates a tarball in ../proxylab-handin.tar that you can then
# hand in. DO NOT MODIFY THIS!
handin:
	(make clean; cd ..; tar cvf $(USER)-proxylab-handin.tar proxylab-handout --exclude tiny --exclude nop-server.py --exclude proxy --exclude driver.sh --exclude port-for-user.pl --exclude free-port.sh --exclude ".*")

clean:
	rm -f *~ *.o proxy core *.tar *.zip *.gzip *.bzip *.gz


测试:

make
./driver.sh
*** Basic ***
Starting tiny on 12323
Starting proxy on 3404
1: home.html
   Fetching ./tiny/home.html into ./.proxy using the proxy
   Fetching ./tiny/home.html into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
2: csapp.c
   Fetching ./tiny/csapp.c into ./.proxy using the proxy
   Fetching ./tiny/csapp.c into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
3: tiny.c
   Fetching ./tiny/tiny.c into ./.proxy using the proxy
   Fetching ./tiny/tiny.c into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
4: godzilla.jpg
   Fetching ./tiny/godzilla.jpg into ./.proxy using the proxy
   Fetching ./tiny/godzilla.jpg into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
5: tiny
   Fetching ./tiny/tiny into ./.proxy using the proxy
   Fetching ./tiny/tiny into ./.noproxy directly from Tiny
   Comparing the two files
   Success: Files are identical.
Killing tiny and proxy
basicScore: 40/40

*** Concurrency ***
Starting tiny on port 12727
Starting proxy on port 20884
Starting the blocking NOP server on port 26172
Trying to fetch a file from the blocking nop-server
Fetching ./tiny/home.html into ./.noproxy directly from Tiny
Fetching ./tiny/home.html into ./.proxy using the proxy
Checking whether the proxy fetch succeeded
Success: Was able to fetch tiny/home.html from the proxy.
Killing tiny, proxy, and nop-server
concurrencyScore: 15/15

*** Cache ***
Starting tiny on port 30629
Starting proxy on port 23341
Fetching ./tiny/tiny.c into ./.proxy using the proxy
Fetching ./tiny/home.html into ./.proxy using the proxy
Fetching ./tiny/csapp.c into ./.proxy using the proxy
Killing tiny
Fetching a cached copy of ./tiny/home.html into ./.noproxy
Success: Was able to fetch tiny/home.html from the cache.
Killing proxy
cacheScore: 15/15

totalScore: 70/70

当然我们也可以通过修改浏览器代理的方式,然后通过浏览器访问服务器,例如,在火狐浏览器中设置代理:

image-20231210092855103

然后在浏览器中访问服务器:

image-20231210092921735

后台的代理程序也看到了输出:

...
cache miss! uri = http://172.22.108.111:8899/godzilla.gif, total_size = 12247, put_cache status = 1

后续优化

请求行不带端口时,默认80端口,这个特殊情况要处理一下。

完整代码请参考【Github仓库】

历史评论
开始评论