CSAPP proxy lab

csapp proxylab

目的: 实现一个客户到和服务器中间的代理。

  1. 用来转发请求和响应
  2. 同时处理多个请求
  3. 缓存对象(未实现)

转发请求和响应

作为服务端接收请求

HTTP请求格式

GET / HTTP/1.1
Host: www.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) 
Accept-Language: en-US,en;q=0.9
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

==注意:每一行以\r\n结尾,最后一行是\r\n==

请求行(GET / HTTP/1.1),三个部分 :

1. GET,表示方法
1. /,资源的路径,这里请求www.baidu.com 地址的根目录资源
1. HTTP/1.1,HTTP协议版本

请求头:请求行下面行都是请求头的内容k :v形式

请求体: 发送请求的自定义参数(本例中没有)

接收请求可以理解成,通过socket的文件描述符,一行一行读取,直到最后一行是\r\n

   //读到最后一行是\r\n结束
   while(strcmp(buf, "\r\n")) {    
        requestHeader *curRequestHeader =(requestHeader *) Malloc(sizeof(requestHeader));
        //读一行
       Rio_readlineb(rp, buf, MAXLINE);
 }

读取第一行例子

linestr

改造请求

http是一个字符串,根据需要封装成结构体,之后再把请求行和请求头改成需要转发的地址

请求头host,和port是我们要改造的,这代表转发目的地的地址和端口

作为客户端发请求

  1. 构造新的请求头字符串,拼接请求行,请求头,(请求体)

  2. 通过 host 和 port 获取目的服务器的文件描述符
  3. 通过文件描述符写出去(发送请求)
    //1.请求行 buffer
    //GET / HTTP/1.1\r\n 
    sprintf(buf, "%s %s %s\r\n", oldMethod, proxyPath, oldVersion);
    //2.请求头 buffer
    
    //strcat()
    for (int i = 0; i < myrequest->headerLen; ++i) {
        char *newHead=(char *)Malloc(sizeof(char));
        sprintf(newHead, "%s: %s\r\n", myrequest->header[i]->name, myrequest->header[i]->value);
        strcat(buf,newHead);
        Free(newHead);
        //Rio_writen(fd, buf, strlen(buf));
    }
    strcat(buf,"\r\n");
    printf("request=>\n %s",buf);
	//2. 文件描述符fd
    int clientfd = Open_clientfd(proxyHost, proxyPortStr);
    //3. 发送请求
    Rio_writen(clientfd,buf,strlen(buf));

流程如下

proxyprocess

同时处理多个请求

一个线程,发送请求如果阻塞了,proxy无法响应别的请求,可以有多种方法处理解决

  1. fork进程:子进程的方式消耗太大
  2. 多路复用: epoll可以,但是无法利用cpu多核心,效率低
  3. 线程: 轻量,又利用了cpu核心

实现线程池

1线程池

构造线程,执行run方法,无限循环从阻塞队列获取fd,再对fd进行读写

核心方法 void doit(int connFd) 实现请求转发

blockQueue myqueue;

//初始化线程,并运行
void init(){
    nthreads = INIT_THREAD_N;
    initConnFdArray(&myqueue, SBUFSIZE); 
    
    create_threads(nthreads);

}
void doit(int connFd);

void *run(void)
{
    //Pthread_detach(pthread_self());
    while (1) {
        usleep(1000*330);
        int connfd = poll(&myqueue);
        
        //实现请求转发的核心方法
        doit(connfd);
        
        Close(connfd);
        //pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);  // 可以取消
    }
}

void create_threads(int end)
{
    for (int i = 0; i < end; i++) {
        // create thread
        Pthread_create(&(threads[i].tid), NULL, run, NULL);
    }
    
}

2阻塞队列

构造线程数组(循环队列),利用生产者消费者模式,线程不断从阻塞队列获取文件描述符,操作文件描述符实现代理通信。

阻塞队列,存放客户端新来的文件描述符。

信号量mutex: 互斥存取

信号量empty: 用来线程同步消费

信号量full: 用来线程同步生产

typedef struct {
    
    int connFdArray[SBUFSIZE];
    int n; 
    //头
    int front;
    //尾
    int rear;
    //当前容量    
    sem_t mutex;
    sem_t full;
    sem_t empty;
    int size;


} blockQueue;

void initConnFdArray(blockQueue* queue,int n){
    sem_unlink("empty");
    sem_unlink("full");
    sem_unlink("mutexLock");

    // 初始化sem_t指针
    //queue->slots = *(sem_t *)malloc(sizeof(sem_t));
  // queue->items = *(sem_t *)malloc(sizeof(sem_t));
   // queue->mutex = *(sem_t *)malloc(sizeof(sem_t));
    queue->size=0;
    queue->n=n;
    queue->front=0;
    queue->rear=0;

    Sem_init(&queue->mutex, 0, 1); /* Binary semaphore for locking */
    Sem_init(&queue->empty, 0, n); /* Initially, buf has n empty slots */
    Sem_init(&queue->full, 0, 0); /* Initially, buf has zero data items */


    for (int i = 0; i < n; ++i) {
         queue->connFdArray[i] = -1;
    }
}


void offer(blockQueue* queue,int connFd){
    P(&queue->empty);                          /* Wait for available slot */
    P(&queue->mutex);   
    int tailIndex= queue->rear;                /* Lock the buffer */
    queue->connFdArray[tailIndex] = connFd;    
    queue->rear= (tailIndex+1) % (queue->n);   /* Insert the item */
    ++queue->size;
    V(&queue->mutex); /* Unlock the buffer */
    V(&queue->full); /* Announce available item */
}
int poll(blockQueue* queue){
    int item;
    P(&queue->full);                           /* Wait for available item */
    //pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);  // 不可取消
    P(&queue->mutex);                           /* Lock the buffer */
    int headIndex =(queue->front);
    item = queue->connFdArray[headIndex]; /* Remove the item */
    headIndex++;
    if (headIndex==queue->n)
    {
        headIndex=0;
    }
    queue->front=headIndex;
    --queue->size;
    V(&queue->mutex); /* Unlock the buffer */
    V(&queue->empty); /* Announce available slot */
    return item;
}

####

缓存对象

时间有限,这是cache lab内容就不再实现

结果

proxyprocess

代码地址

proxy_lab