CSAPP:Proxy lab实现


总的来说,这次实验不是很难,但也需要花费1~2天的时间。其中用到了生产者-消费者模型、读者-写者模型等等,写这篇博文的目的也算是自己对10~13章的一个总结和复习。但是我对HTTP协议这一块不是很懂,所以一开始遇到的一些问题。还好本次实验要求不是很高(需要学的东西还有很多)。。。废话不多说,下面进入主题。

题目要求

本次实验主要完成下列三个部分:

  • Part I: 完成迭代的代理服务器
  • Part II: 完成多线程代理服务器
  • Part III: 向代理中加入缓存机制

Part I & Part II

Part I 和Part II 两者的差距非常小,在完成了第一部分的基础上,只需简单加入生产者消费者模型既能够实现多线程的代理服务器。下面来介绍其基本原理,细节在代码中都有提及,最后将贴上完整代码。

代理服务器

代理服务器就像一个中介,它会帮助你访问Internet,将用户和Internet有机的连接在一起。代理服务器是用户的服务端,而又是最终服务器的客户端,其模式如下图所示。
代理服务器的基本流程
现在,整个程序的思路就很清晰了。代理服务器需要做的就是从客户端得到请求,转发到服务器,从服务器得到数据后再返回给客户端。

从客户端收到消息
解析请求
添加代理表头等信息
发送到服务器
将服务器端的消息转发给客户端

请求解析

简单的说,一般要从下面的请求中得到请求方式、主机名、端口号、文件路径以及HTTP的协议。

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

有时候请求是指定代码的,有时则没有(默认为80)。分类讨论即可。

添加代理表头信息

这一部分我不是特别理解,主要对HTTP协议概念不是很清晰,不过只要按照指导书中提及的,添加相应字符串即可。

发送信息

这一步就是简单的利用书中的rio库进行文件传输即可。

生产者-消费者模型

这个模型就像有一个固定大小的水箱,生产者不断像水箱中加水,而消费者不断从水箱另一端取水。很显然,如果水箱满了,生产者便无法加水,反之如果水箱空了,那消费者则无法取水。

而在实际的运用中(本题中),这一模型就称之为 预线程化的并发服务器。其实道理很简单,还是用水箱做例子,首先创建 n个线程,这就类似给出了一个固定大小的水箱,如果这时接收的到了客户的请求,那么“就向水箱中加水”,程序开始处理连接时,“就从水箱中把水取走”。这一过程会有个问题,当连接达到上限(水箱满了),此时就无法接收更多的连接;如果此时没有任何连接,那线程就会一直阻塞,直到“水箱中加入水”为止。

到这里,就可以写出多线程的代理服务器代码了。这里的sbuf.h与书中(中文版705页)的一模一样,也可以在官网中获得

#include "sbuf.h"
#include "csapp.h"


/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400

#define NTHREADS 4
#define SBUFSIZE 16

/* 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 doit(int fd);
void read_requesthdrs(rio_t *rp);
void build_header(char *header, char *hostname, char *path, rio_t *client_rio);
void parse_uri(char *uri, char *hostname, char *filepath, int *port);
int connect_endserver(char *hostname,int port,char *http_header);
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
void *thread(void *vargp);

sbuf_t sbuf;    /* Shared buffer of connected descriptors */

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

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

    printf("%s", user_agent_hdr);

    /* 监听套接字 */
    listenfd = Open_listenfd(argv[1]);

    sbuf_init(&sbuf, SBUFSIZE);
    for (int i = 0; i < NTHREADS; i++) {    /* Create worker threads */
        Pthread_create(&tid, NULL, thread, NULL);
    }

    while (1) {
        clientlen = sizeof(struct sockaddr_storage);
        connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
        sbuf_insert(&sbuf, connfd);
        Getnameinfo((SA *)&clientaddr, clientlen, hostname, MAXLINE, port, MAXLINE, 0);
        printf("Accepted connection from (%s, %s)\n", hostname, port);
        // Close(connfd);  /* 显式关闭连接 */
    }
}

void doit(int fd) {
    int end_serverfd, port = 80;    /* HTTP default port is 80 */
    char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
    char filepath[MAXLINE], hostname[MAXLINE];
    char endserver_header[MAXLINE];
    rio_t client_rio, server_rio;

    /* Read request line and headers */
    Rio_readinitb(&client_rio, fd);
    Rio_readlineb(&client_rio, buf, MAXLINE);
    sscanf(buf, "%s %s %s", method, uri, version);

    if (strcasecmp(method, "GET")) {
        clienterror(fd, method, "501", "Not implemented", "Proxy does not implement this method");
        return;
    }

    /* Parse the uri to get hostname, file, path and port */
    parse_uri(uri, hostname, filepath, &port);

    /* build the http header which will send to the end server */
    build_header(endserver_header, hostname, filepath, &client_rio);

    end_serverfd =  connect_endserver(hostname, port, endserver_header);
    if (end_serverfd < 0) {
        printf("Connection failed\n");
        return;
    }

    Rio_readinitb(&server_rio, end_serverfd);

    /* write the http header to end server */
    Rio_writen(end_serverfd, endserver_header, strlen(endserver_header));

    /* Receive mgs from end server and send to client */
    size_t n;
    while ((n = rio_readlineb(&server_rio, buf, MAXLINE)) != 0) {
        printf("proxy received %ld bytes, then send to client %d\n", n, fd);
        Rio_writen(fd, buf, n);
    }

    Close(end_serverfd);
}

void read_requesthdrs(rio_t *rp) {
    char buf[MAXLINE];

    Rio_readlineb(rp, buf, MAXLINE);
    while (strcmp(buf, "\r\n")) {
        Rio_readlineb(rp, buf, MAXLINE);
        printf("%s", buf);
    }
    return;
}

void parse_uri(char *uri, char *hostname, char *filepath, int *port) {
    /*parse the uri to get hostname,file path ,port*/
    char* ptr = strstr(uri,"//");

    ptr = ptr != NULL? ptr+2 : uri;

    char *temp = strstr(ptr, ":");
    if(temp!=NULL) {
        *temp = '\0';
        sscanf(ptr, "%s", hostname);
        sscanf(temp+1, "%d%s", port, filepath);
    }
    else {
        temp = strstr(ptr,"/");
        if(temp != NULL) {
            *temp = '\0';
            sscanf(ptr, "%s", hostname);
            *temp = '/';
            sscanf(temp, "%s", filepath);
        }
        else {
            sscanf(ptr, "%s", hostname);
        }
    }
    return;
}

void build_header(char *header, char *hostname, char *path, rio_t *client_rio) {
    char buf[MAXLINE], request_hdr[MAXLINE], other_hdr[MAXLINE], host_hdr[MAXLINE];
    static const char *connection_key = "Connection";
    static const char *user_agent_key= "User-Agent";
    static const char *proxy_connection_key = "Proxy-Connection";

    /* request line */
    sprintf(request_hdr, "GET %s HTTP/1.0\r\n", path);
    /* get other request header for client rio and change it */
    while (Rio_readlineb(client_rio, buf, MAXLINE) > 0) {
        if (strcmp(buf, "\r\n") == 0) {
            break;
        }

        if (!strncasecmp(buf, "Host", strlen("Host"))) {
            strcpy(host_hdr, buf);
            continue;
        }

        if (!strncasecmp(buf, connection_key, strlen(connection_key))
                && !strncasecmp(buf, proxy_connection_key, strlen(proxy_connection_key))
                && !strncasecmp(buf, user_agent_key, strlen(user_agent_key))) {
            strcat(other_hdr, buf);
        }
    }

    if (strlen(host_hdr) == 0) {
        sprintf(host_hdr, "GET %s HTTP/1.0\r\n", hostname);
    }

    sprintf(header, "%s%s%s%s%s%s%s",
        request_hdr,
        host_hdr,
        "Connection: close\r\n",
        "Proxy-Connection: close\r\n",
        user_agent_hdr,
        other_hdr,
        "\r\n");
}

int connect_endserver(char *hostname,int port,char *http_header) {
    char portstr[100];
    sprintf(portstr, "%d", port);
    return Open_clientfd(hostname, portstr);
}

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

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

    /* Print the HTTP response */
    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");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));
    Rio_writen(fd, buf, strlen(buf));
    Rio_writen(fd, body, strlen(body));
}

void *thread(void *vargp) {
    Pthread_detach(pthread_self());
    while (1) {
        int connfd = sbuf_remove(&sbuf);
        doit(connfd);   /* Service client */
        Close(connfd);
    }
}

Part III

cache的作用,顾名思义,就是当用户多次访问同一个资源时,如果每次都从服务器获取数据,这是一件很麻烦的事情。如果代理服务器本地把频繁访问的数据都存储下来,那么就能很好的解决这个问题。

读者-写者模型

一组并发线程访问共享变量对象时,有些线程是只读对象(从cache中读取数据),有些是修改对象(修改cache中的内容)。本次试验采用读者优先,要求不让读者等待,除非此时一个写者正在访问共享对象。

举个不是很恰当的例子。你写了份作业,大家都要传阅抄你的,那么别人抄你作业的时候你不能把本子要回来,但你在修改错误的时这时别人没法把你作业拿走。不过会有个情况,当有个人抄完作业后,你发现了一处错误想要修改,而此时另一个人也在等你的作业,这时候你就不能修改而是等到他抄完后才能修改,这就是读者优先。写者优先同理。

Cache的功能

这里的cache,需要完成一系列基本功能才能使之正常运行。

  • cache初始化
  • 寻找请求内容是否在cache中
  • 寻找可用的cache位置
  • 向cache中存储内容
  • 更新LRU表

cache.h

#ifndef __CACHE_H__
#define __CACHE_H__

/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
#define LRU_MAGIC_NUMBER 9999
#define CACHE_OBJS_COUNT 10

#include "csapp.h"

/* $begin cache */
typedef struct {
    int readcnt;                /* Initially = 0 */
    int LRU;
    int is_empty;
    sem_t mutex;                /* protects accesses to readcnt */
    sem_t w;                    /* protects accesses to cache */
    char uri[MAXLINE];         /* store uri */
    char obj[MAX_OBJECT_SIZE]; /* store object from server */

} cache_block;
/* $end cache */

typedef struct {
    cache_block cacheobjs[CACHE_OBJS_COUNT];    /* 10 cache blocks */
} cache_t;

void cache_init(cache_t *cache);
int  cache_find(cache_t *cache, char *uri);
int  cache_eviction(cache_t *cache);
void cache_store(cache_t *cache, char *uri, char *buf);
void cache_lru(cache_t *cache, int i);
void read_pre(cache_t *cache, int i);
void read_after(cache_t *cache, int i);
void write_pre(cache_t *cache, int i);
void write_after(cache_t *cache, int i);


#endif  /* __CACHE_H__*/

cache.c

#include "csapp.h"
#include "cache.h"

/* Create an empty cache */
void cache_init(cache_t *cache) {
    for (int i = 0; i < CACHE_OBJS_COUNT; i++) {
        cache->cacheobjs[i].readcnt = 0;
        cache->cacheobjs[i].LRU = 0;
        cache->cacheobjs[i].is_empty = 1;
        sem_init(&(cache->cacheobjs[i].mutex), 0, 1);
        sem_init(&(cache->cacheobjs[i].w), 0, 1);
    }
}

/* find uri is in the cache or not */
int cache_find(cache_t *cache, char *uri) {
    int i;
    for (i = 0; i < CACHE_OBJS_COUNT; i++) {
        read_pre(cache, i);
        if ((cache->cacheobjs[i].is_empty==0) && (strcmp(uri, cache->cacheobjs[i].uri)==0)) {
            break;
        }
        read_after(cache, i);
    }

    if (i >= CACHE_OBJS_COUNT) return -1;    /* can not find url in the cache */
    return i;
}

/* find an available cache */
int  cache_eviction(cache_t *cache) {
    int min = LRU_MAGIC_NUMBER;
    int minindex = 0;
    int i;
    for (i = 0; i < CACHE_OBJS_COUNT; i++) {
        read_pre(cache, i);
        if (cache->cacheobjs[i].is_empty == 1) {
            minindex = i;
            read_after(cache, i);
            break;
        }

        if (cache->cacheobjs[i].LRU < min) {
            minindex = i;
            read_after(cache, i);
            continue;
        }
        read_after(cache, i);
    }
    return minindex;
}

void cache_store(cache_t *cache, char *uri, char *buf) {
    int i = cache_eviction(cache);

    write_pre(cache, i);

    strcpy(cache->cacheobjs[i].uri, uri);
    strcpy(cache->cacheobjs[i].obj, buf);
    cache->cacheobjs[i].is_empty = 0;
    cache->cacheobjs[i].LRU = LRU_MAGIC_NUMBER;
    cache_lru(cache, i);

    write_after(cache, i);
}

/* update the LRU number except the new cache one */
void cache_lru(cache_t *cache, int index) {
    int i;
    for(i=0; i<index; i++)    {
        write_pre(cache, i);
        if(cache->cacheobjs[i].is_empty==0 && i!=index){
            cache->cacheobjs[i].LRU--;
        }
        write_after(cache, i);
    }
    i++;
    for(; i<CACHE_OBJS_COUNT; i++) {
        write_pre(cache, i);
        if(cache->cacheobjs[i].is_empty==0 && i!=index){
            cache->cacheobjs[i].LRU--;
        }
        write_after(cache, i);
    }
}

void read_pre(cache_t *cache, int i) {
    P(&cache->cacheobjs[i].mutex);
    cache->cacheobjs[i].readcnt++;
    if (cache->cacheobjs[i].readcnt == 1) {   /* first in */
        P(&cache->cacheobjs[i].w);
    }
    V(&cache->cacheobjs[i].mutex);
}

void read_after(cache_t *cache, int i) {
    P(&cache->cacheobjs[i].mutex);
    cache->cacheobjs[i].readcnt--;
    if (cache->cacheobjs[i].readcnt == 0) {   /* Last out */
        V(&cache->cacheobjs[i].w);
    }
    V(&cache->cacheobjs[i].mutex);
}

void write_pre(cache_t *cache, int i) {
    P(&cache->cacheobjs[i].w);
}

void write_after(cache_t *cache, int i) {
    V(&cache->cacheobjs[i].w);
}

proxy.c

#include "cache.h"
#include "sbuf.h"
#include "csapp.h"

/* Recommended max cache and object sizes */

#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
#define LRU_MAGIC_NUMBER 9999
#define CACHE_OBJS_COUNT 10

#define SBUFSIZE 16
#define NTHREADS 4


/* 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 doit(int fd);
void read_requesthdrs(rio_t *rp);
void build_header(char *header, char *hostname, char *path, rio_t *client_rio);
void parse_uri(char *uri, char *hostname, char *filepath, int *port);
int connect_endserver(char *hostname,int port,char *http_header);
void clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
void *thread(void *vargp);

sbuf_t sbuf;    /* Shared buffer of connected descriptors */
cache_t cache;  /* Shared cache */

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

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

    cache_init(&cache);

    /* 监听套接字 */
    listenfd = Open_listenfd(argv[1]);

    sbuf_init(&sbuf, SBUFSIZE);
    

    for (int i = 0; i < NTHREADS; i++) {    /* Create worker threads */
        Pthread_create(&tid, NULL, thread, NULL);
    }

    while (1) {
        clientlen = sizeof(struct sockaddr_storage);
        connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen);
        sbuf_insert(&sbuf, connfd);
        Getnameinfo((SA *)&clientaddr, clientlen, hostname, MAXLINE, port, MAXLINE, 0);
        printf("Accepted connection from (%s, %s)\n", hostname, port);
        // Close(connfd);  /* 显式关闭连接 */
    }
}

void doit(int connfd) {
    int end_serverfd, port = 80;    /* HTTP default port is 80 */
    char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE];
    char filepath[MAXLINE], hostname[MAXLINE];
    char endserver_header[MAXLINE];
    rio_t client_rio, server_rio;

    /* Read request line and headers */
    Rio_readinitb(&client_rio, connfd);
    Rio_readlineb(&client_rio, buf, MAXLINE);
    sscanf(buf, "%s %s %s", method, uri, version);

    char url_store[100];
    strcpy(url_store,uri);  /*store the original url */

    if (strcasecmp(method, "GET")) {
        clienterror(connfd, method, "501", "Not implemented", "Proxy does not implement this method");
        return;
    }

    /* find cache */
    int index;
    if ((index = cache_find(&cache, url_store)) != -1) {  /* cache hits */
        read_pre(&cache, index);
        Rio_writen(connfd, cache.cacheobjs[index].obj, strlen(cache.cacheobjs[index].obj));
        read_after(&cache, index);
        return;
    }

    /* Parse the uri to get hostname, file, path and port */
    parse_uri(uri, hostname, filepath, &port);

    /* build the http header which will send to the end server */
    build_header(endserver_header, hostname, filepath, &client_rio);

    end_serverfd =  connect_endserver(hostname, port, endserver_header);
    if (end_serverfd < 0) {
        printf("Connection failed\n");
        return;
    }

    Rio_readinitb(&server_rio, end_serverfd);

    /* write the http header to end server */
    Rio_writen(end_serverfd, endserver_header, strlen(endserver_header));

    /* Receive mgs from end server and send to client */
    char cache_buf[MAX_OBJECT_SIZE];
    int buf_size = 0, n;
    while ((n = rio_readlineb(&server_rio, buf, MAXLINE)) != 0) {
        printf("proxy received %d bytes, then send to client %d\n", n, connfd);

        buf_size += n;
        if (buf_size < MAX_OBJECT_SIZE) {
            strcat(cache_buf, buf);
        }
        Rio_writen(connfd, buf, n);
    }

    Close(end_serverfd);

    /* store it in cache */
    if (buf_size < MAX_OBJECT_SIZE) {
        cache_store(&cache, url_store, cache_buf);
    }
}

void read_requesthdrs(rio_t *rp) {
    char buf[MAXLINE];

    Rio_readlineb(rp, buf, MAXLINE);
    while (strcmp(buf, "\r\n")) {
        Rio_readlineb(rp, buf, MAXLINE);
        printf("%s", buf);
    }
    return;
}

void parse_uri(char *uri, char *hostname, char *filepath, int *port) {
    /*parse the uri to get hostname,file path ,port*/
    char* ptr = strstr(uri,"//");

    ptr = ptr != NULL? ptr+2 : uri;

    char *temp = strstr(ptr, ":");
    if(temp!=NULL) {
        *temp = '\0';
        sscanf(ptr, "%s", hostname);
        sscanf(temp+1, "%d%s", port, filepath);
    }
    else {
        temp = strstr(ptr,"/");
        if(temp != NULL) {
            *temp = '\0';
            sscanf(ptr, "%s", hostname);
            *temp = '/';
            sscanf(temp, "%s", filepath);
        }
        else {
            sscanf(ptr, "%s", hostname);
        }
    }
    return;
}

void build_header(char *header, char *hostname, char *path, rio_t *client_rio) {
    char buf[MAXLINE], request_hdr[MAXLINE], other_hdr[MAXLINE], host_hdr[MAXLINE];
    static const char *connection_key = "Connection";
    static const char *user_agent_key= "User-Agent";
    static const char *proxy_connection_key = "Proxy-Connection";

    /* request line */
    sprintf(request_hdr, "GET %s HTTP/1.0\r\n", path);
    /* get other request header for client rio and change it */
    while (Rio_readlineb(client_rio, buf, MAXLINE) > 0) {
        if (strcmp(buf, "\r\n") == 0) {
            break;
        }

        if (!strncasecmp(buf, "Host", strlen("Host"))) {
            strcpy(host_hdr, buf);
            continue;
        }

        if (!strncasecmp(buf, connection_key, strlen(connection_key))
                && !strncasecmp(buf, proxy_connection_key, strlen(proxy_connection_key))
                && !strncasecmp(buf, user_agent_key, strlen(user_agent_key))) {
            strcat(other_hdr, buf);
        }
    }

    if (strlen(host_hdr) == 0) {
        sprintf(host_hdr, "GET %s HTTP/1.0\r\n", hostname);
    }

    sprintf(header, "%s%s%s%s%s%s%s",
        request_hdr,
        host_hdr,
        "Connection: close\r\n",
        "Proxy-Connection: close\r\n",
        user_agent_hdr,
        other_hdr,
        "\r\n");
}

int connect_endserver(char *hostname,int port,char *http_header) {
    char portstr[100];
    sprintf(portstr, "%d", port);
    return Open_clientfd(hostname, portstr);
}

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

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

    /* Print the HTTP response */
    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");
    Rio_writen(fd, buf, strlen(buf));
    sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body));
    Rio_writen(fd, buf, strlen(buf));
    Rio_writen(fd, body, strlen(body));
}

void *thread(void *vargp) {
    Pthread_detach(pthread_self());
    while (1) {
        int connfd = sbuf_remove(&sbuf);
        doit(connfd);   /* Service client */
        Close(connfd);
    }
}

Makefile

在添加了cachesbuf后,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: sbuf.c csapp.h
	$(CC) $(CFLAGS) -c sbuf.c

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

proxy: proxy.o sbuf.o csapp.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


最后

提问

指导书中有这么一句话描述cache的性质:

If the entirety of the web server’s response is read before the maximum object size is exceeded, then the object can be cached. Using this scheme, the maximum amount of data your proxy will ever use for web objects is the following, where T is the maximum number of active connections:

MAX_CACHE_SIZE + T * MAX_OBJECT_SIZE

这里我百思不得其写,求解答!!!!

后记

花了好几月时间终于看完了《CSAPP》,今天也算是圆满结束了。可以说受益匪浅,不过还有很多的不足需要去弥补,去学习。文中的一些例子和理解可能有很多不正确的地方,还请各位大佬纠正。

  • 4
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: CSAPP Proxy Lab是CMU计算机科学系的一项课程作业。该作业旨在通过设计和实现一个基于代理服务器的Web代理,让学生更好地理解计算机网络、操作系统和编程等方面的知识,提高他们的编程能力和问题解决能力。 在这个作业中,学生需要实现一个Web代理程序,该程序需要能够接受来自客户端的HTTP请求,并将请求转发到相应的Web服务器。代理程序需要能够处理HTTP请求的各种类型(GET、POST、PUT等),并且需要能够处理HTTP响应的各种错误码(404、500等)。代理程序还需要支持并发处理多个HTTP请求,以提高系统的效率。 在实现代理程序的过程中,学生需要掌握网络编程、多线程编程、Socket编程等技术,并且需要使用C语言实现代理程序。此外,还需要学生能够理解HTTP协议、代理服务器的工作原理以及Web应用的工作流程等相关知识。 总之,CSAPP Proxy Lab是一项非常有挑战性的作业,需要学生具备扎实的编程基础和网络知识。通过完成该作业,学生可以深入理解计算机网络、操作系统和编程等方面的知识,并提高自己的编程能力和问题解决能力。 ### 回答2: CSAPP Proxy Lab是Carnegie Mellon大学计算机科学的一项项目,旨在帮助学生深入了解计算机网络和代理服务器的工作原理以及如何开发高效的网络应用程序。 Proxy Server是一种应用程序,可以充当网络上的“中转站”,它可以通过代理服务器将客户端请求转发到目标服务器端,并将响应返回给客户端。Proxy Lab的任务是实现一个HTTP代理服务器,它需要能够从客户端接收请求,并将请求转发到目标服务器,然后将响应发送回客户端。 实现Proxy Lab需要掌握网络编程、多线程编程、缓存设计以及HTTP协议等知识。代理服务器需要支持并发处理多个客户端请求,以保证高效的网络传输。为了提高性能,代理服务器还需要对常见的网页、图片和视频进行缓存,避免重复请求。 除了上述技能外,实现Proxy Lab还需要良好的编程能力和团队合作精神。在实现Proxy Lab的过程中,学生需要与队友紧密协作,及时沟通、并发同步,以确保项目的顺利进行。 总之,CSAPP Proxy Lab是一项非常有挑战性的计算机网络应用项目,不仅要求学生充分理解TCP/IP协议、HTTP协议等基本概念,还需要具备优秀的编程和团队协作能力。完成该项目不仅可以提高学生的技能,也可以为日后工作和实际应用打下良好的基础。 ### 回答3: CSAPP Proxy Lab 是一个经典的计算机科学实验,它涵盖了计算机网络知识和系统编程技能。这个实验的主要目标是构建一个基本的 Web 代理服务器,该服务器能够处理 HTTP 请求,并在远程 Web 服务器上代表客户端处理这些请求。 在 Proxy Lab 中,学生需要实现一个基于事件驱动的 Web 代理服务器。该服务器使用 epoll 进行事件处理,可以同时处理多个连接和请求。代理服务器需要支持从客户端接收 HTTP 请求,并解析请求头,将请求发送到远程服务器,接收响应,并将响应发送回客户端。在此过程中,代理服务器需要支持请求过滤和转发,以及缓存功能。 重要的是,学生需要处理一些常见的 Web 代理挑战,例如连接重用、响应缓存、虚拟主机支持和负载均衡。通过完成 Proxy Lab 实验,学生将获得有关计算机系统编程和网络协议的深入知识,并获得实际构建 Web 代理服务器的经验。 总之,CSAPP Proxy Lab 是一个非常重要的实验,它可以帮助学生领会计算机网络和系统编程的核心概念。通过完成这个实验,学生将获得深入的理解和实践经验,从而更好地了解计算机系统和网络技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值