CS:APP-Proxy Lab(CS:APP系列实验完结篇)
前言
本实验要求我们实现一个代理服务器,充当一个在用户和目标服务器之间的桥梁角色,深入了解各个主机之间建立连接的流程。话不多说,打开电脑,带上键盘,开启实验!
Part Ⅰ
参照tiny.c的main函数以及教材663页的图11-20的程序,我们要在我们的代理程序中,使用Open_listenfd
和Accept
等待客户端发起连接,客户端连接我们的代理后,我们再使用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.c
和sbuf.c
,sbuf.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
当然我们也可以通过修改浏览器代理的方式,然后通过浏览器访问服务器,例如,在火狐浏览器中设置代理:
然后在浏览器中访问服务器:
后台的代理程序也看到了输出:
...
cache miss! uri = http://172.22.108.111:8899/godzilla.gif, total_size = 12247, put_cache status = 1
后续优化
请求行不带端口时,默认80端口,这个特殊情况要处理一下。
完整代码请参考【Github仓库】
本文由「黄阿信」创作,创作不易,请多支持。
如果您觉得本文写得不错,那就点一下「赞赏」请我喝杯咖啡~
商业转载请联系作者获得授权,非商业转载请附上原文出处及本链接。
关注公众号,获取最新动态!