【CSAPP】proxy Lab代理实验

这个实验较为简单,但是要写出来还是得花一天半天的时间。主要实现到是一个代理的功能,接受客户端到请求,再代替客户端请求服务器相应的内容后,再返回给客户端。

多线程程序gdb调试:

1.info thread 显示当前有几个线程

2.thread num  转换到标号为num的线程,当前线程结束后,通过Ctrl+C返回前一个线程

下面直接贴上代码了,并且在文章的最后说明了对该代理程序如何进行调试

#include "csapp.h"

/*
 * Function prototypes
 */
void parse_url(char *ur, char *hostname, char *query_path, int *port);
void format_log_entry(char *logstring, struct sockaddr_in *sockaddr, char *uri, int size);
void *thread(void *arg);
void client_error(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
int connect_server(char *hostname, int port, char *path );
/*
 * varibles
 */
struct args{
    int fd;
    struct sockaddr_in sockaddr;
};
pthread_mutex_t mutex;
FILE* logfile;
/* 
 * main - Main routine for the proxy program 
 */
int main(int argc, char **argv)
{
    FILE* logfile;
    pthread_t tid;
    /* Check arguments */
    if (argc != 2) {
	fprintf(stderr, "Usage: %s <port number>\n", argv[0]);
	exit(0);
    }

    Signal(SIGPIPE, SIG_IGN);
   
    logfile = Fopen("./logfile", "a");
    int listenfd = Open_listenfd( atoi(argv[1]) );
    while(1){
        socklen_t len = sizeof(int);
        struct args* p = (struct args*)Malloc(sizeof(struct args));
        p->fd = Accept(listenfd, (SA*)(&p->sockaddr),&len);  
        Pthread_create(&tid, NULL, thread, p);
    }
    exit(1);
}

void *thread(void *arg)
{
     rio_t rp;
     char buf[MAXLINE];
     char method[MAXLINE];
     char url[MAXLINE];
     char version[MAXLINE];
    
     char hostname[MAXLINE];
     char path[MAXLINE]; 
     int port;
     int serverfd;
     int clength;

     Pthread_detach(Pthread_self());
     struct args* tmp = (struct args*)arg;
     int fd =  tmp->fd;
     
     struct sockaddr_in sockaddr = tmp->sockaddr;
     Free(tmp);
     
     Rio_readinitb(&rp, fd); 
     Rio_readlineb(&rp, buf, MAXLINE);

    if(sscanf(buf, "%s %s %s", method, url, version) < 3){
        fprintf(stderr, "sscanf error");
        client_error(fd, method, "404","Not Found", "Not Found");  
        Close(fd);
        return NULL;
    }
   
    if(strcmp(method,"GET")){
        fprintf(stderr, "error request");
        client_error(fd, method, "500","Not Implement", "Not Implement");  
        Close(fd);
        return NULL;
    }
    /*忽略首部和实体*/
    do{
        Rio_readlineb(&rp, buf, MAXLINE);    
    }while(strcmp(buf, "\r\n"));
    
    /*解析URL,请求服务器*/
    parse_url(url,hostname, path, &port); 
    if( (serverfd = connect_server(hostname,port, path))  < 0){
        Close(fd);
        return NULL;
    }
    
    /*等待读取服务器响应,并送回请求客户端*/
    do{
       Rio_readinitb(&rp, serverfd);
       Rio_readlineb(&rp, buf, MAXLINE);
       
       if(strstr(buf, "Content-length:")){
            sscanf(buf, "Content-length: %d\r\n", &clength);
       Rio_writen(fd, buf, MAXLINE);
       }
      }while(strcmp(buf,"\r\n"));
     
    
       char logstring[MAXLINE]; 
       pthread_mutex_lock(&mutex);
       format_log_entry(logstring, &sockaddr, url, clength);
       pthread_mutex_unlock(&mutex);
       fprintf(logfile, "%s\n", logstring);
       fflush(logfile);
       close(fd);
       close(serverfd);

}

int connect_server(char *hostname, int port, char *path )
{
    static const char *user_agent = "User-Agent: Mozilla (X11; Linux i386; rv:10.0.3) Gecko/20120305 Firefox/10.0.3\r\n";
    static const char *accept_str= "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n";
    static const char *connection = "Connection: close\r\nProxy-Connection: close\r\n";
    
    char buf[MAXLINE];
    /* connect to server */
    int proxy_clientfd;
    proxy_clientfd=open_clientfd(hostname,port);

    /* if failed return */
    if(proxy_clientfd<0)
        return proxy_clientfd;

    /* write request to server */
    sprintf(buf,"GET %s HTTP/1.0\r\n", path);
    Rio_writen(proxy_clientfd,buf,strlen(buf));
    sprintf(buf,"Host: %s\r\n",hostname);
    Rio_writen(proxy_clientfd,buf,strlen(buf));
    Rio_writen(proxy_clientfd,user_agent,strlen(user_agent));
    Rio_writen(proxy_clientfd,accept_str,strlen(accept_str));
    Rio_writen(proxy_clientfd,connection,strlen(connection));
    Rio_writen(proxy_clientfd,"\r\n",strlen("\r\n"));
    printf("request to server is done.");
    return proxy_clientfd;
}

/* parse request url */
void parse_url(char *ur, char *hostname, char *query_path, int *port)
{
    char url[100];
    url[0]='\0';
    strcat(url,ur);
    hostname[0]=query_path[0]='\0';
    char *p=strstr(url,"//");        /* skip "http://" and "https://" */
    if(p!=NULL) {
        p=p+2;
    } else {
        p=url;
    }
    char *q=strstr(p,":");            /* read ":<port>" and "/index.html" */
    if(q!=NULL) {
        *q='\0';
        sscanf(p,"%s",hostname);
        sscanf(q+1,"%d%s",port,query_path);
    } else {
        q=strstr(p,"/");
        if(q!=NULL) {
            *q='\0';
            sscanf(p,"%s",hostname);
            *q='/';
            sscanf(q,"%s",query_path);
        } else {
            sscanf(p,"%s",hostname);
        }
        *port=80;
    }
    /* the default path */
    if(strlen(query_path)<=1)
        strcpy(query_path,"/index.html");

    return;
}

/*
 * format_log_entry - Create a formatted log entry in logstring. 
 * 
 * The inputs are the socket address of the requesting client
 * (sockaddr), the URI from the request (uri), and the size in bytes
 * of the response from the server (size).
 */
void format_log_entry(char *logstring, struct sockaddr_in *sockaddr, 
		      char *uri, int size)
{
    time_t now;
    char time_str[MAXLINE];
    unsigned long host;
    unsigned char a, b, c, d;

    /* Get a formatted time string */
    now = time(NULL);
    strftime(time_str, MAXLINE, "%a %d %b %Y %H:%M:%S %Z", localtime(&now));

    /* 
     * Convert the IP address in network byte order to dotted decimal
     * form. Note that we could have used inet_ntoa, but chose not to
     * because inet_ntoa is a Class 3 thread unsafe function that
     * returns a pointer to a static variable (Ch 13, CS:APP).
     */
    host = ntohl(sockaddr->sin_addr.s_addr);
    a = host >> 24;
    b = (host >> 16) & 0xff;
    c = (host >> 8) & 0xff;
    d = host & 0xff;


    /* Return the formatted log entry string */
    sprintf(logstring, "%s: %d.%d.%d.%d %s", time_str, a, b, c, d, uri);
}

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

    /* Build the HTTP response body */
    sprintf(body, "%s: %s\r\n", errnum, shortmsg);
    sprintf(body, "%s%s: %s", body, longmsg, cause);

    /* 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));
}

如何调试本程序:使用nc这个小巧的工具,常见用法自行man。

在这里我们先选择一个空闲到端口,并在一个终端执行以下命令:

nc -l 5000
此命令使得nc作为端口5000的监听服务器,可以接受信息。

然后我们再选择一个空闲端口运行代理程序:

./proxy 10000 &
接着,我们使用curl这个工具模拟客户端产生HTTP请求:
curl  -v  -proxy  http://127.0.0.1:10000/  http://127.0.0.1:500/

此时我们便能看到服务器接收到我们客户端发出的请求了。



  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答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 是一个非常重要的实验,它可以帮助学生领会计算机网络和系统编程的核心概念。通过完成这个实验,学生将获得深入的理解和实践经验,从而更好地了解计算机系统和网络技术。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值