Proxy Shell总结和收货

目录

1.Proxy Shell的总体流程图如图所示

2.getaddrinfo和getnameinfo

3.定义一个结构体指针和定义一个结构体变量的区别

4.代理服务器缓存实现方法

5.在C语言中,结构体声明与C++有不一样的地方:

6.http数据包的具体格式

 

1.Proxy Shell的总体流程图如图所示

2.getaddrinfo和getnameinfo

其中在open_listenfd函数中,使用了getaddrinfo函数,这个函数封装了包括大小端转换和点分十进制转int型等多个函数的功能,并且有DNS解析的功能,能够调用完以后直接创建套接字。而这个函数的返回值就是一系列与套接字有关的结构体,可以直接使用其中的结构体创建套接字。

对应的想要获取主机的IP地址等信息,可以使用getnameinfo函数实现,避免大小端和IP的转换。函数的具体内容可以看CSAPP的656页。

3.定义一个结构体指针和定义一个结构体变量的区别

在自己写程序时,遇到这样一个问题,定义一个结构体变量不需要初始化就可以直接赋值,而定义一个结构体指针需要初始化才可以给它赋值,举例如下:

struct stu
{
    char name[256];
    int age;
}


//定义结构体变量后可直接赋值
struct stu val;
val.name="zhangsan";

//定义结构体指针不能直接赋值,下面程序是错误的,会报内存溢出的错误
struct stu *ptr;
ptr->name="zhangsan";

//但是定义动态指针是可以直接赋值的
//比如在C++中这样的定义方法是与结构体变量定义类似的,这个ptr是动态变量指针,
//并且在栈上分配了sizeof(stu)的空间
stu *ptr(new stu);

//又比如在C语言中
struct stu* ptr=(struct stu*)malloc(sizeof(struct stu));

原因是当定义一个变量时,系统会分配sizeof(struct stu)大小的内存给这个变量,但是当只定义一个结构体指针时,系统只会分配4个字节的大小给内存,所以肯定不能直接赋值,所以一般定义变量应该定义变量,这样就可以直接使用。或者在C++中使用动态内存指针的定义。

4.代理服务器缓存实现方法

在缓存的实现中,使用了LRU思想,如果缓存已满,就将使用最少的缓存去除,添加新缓存。具体实现就是使用双向链表,示意图如下

每次加入的元素都加在head_ptr下面,如果元素个数已满,就首先删除tail_ptr指向的元素。每次查找到元素时,也把该元素移到head_ptr下面,所以越靠近head_ptr的就是越近使用的,而越靠近tail_ptr的就是越早使用的,所以就把越早使用的删去。

5.在C语言中,结构体声明与C++有不一样的地方:

typedef struct stu
{
    int age;
    char name[20];
}student;

//在C语言中,以下的定义方法是可行的
struct stu a;
student a;

//在C++中,以下的定义方式是可行的
struct stu a;
student a;
stu a;

6.http数据包的具体格式

一个http请求数据包,主要由请求行,请求报头,空行和请求体四部分构成,其中每一个行都是由"\r\n"结尾的,结构如图所示:

其中请求行是必须的,

请求报头的格式为“报头名称:报头数据”可能没有请求报头,也有可能有多个请求报头

空行也就是“\r\n”,也是必须的,如果有报头就在报头后面,如果没有报头就是在请求行后面

请求体主要是在post方法下会有,主要是参数的传递。

一个http响应数据包,和请求数据包差不多,主要由响应行,响应报头,空行和响应体四部分构成

其中唯一不一样的就是响应行,格式是“HTTP版本 状态码 状态消息”

 

代码详解

/*主程序*/
#include <stdio.h>
#include "csapp.h"
#include <sys/types.h>         
#include <sys/socket.h>
#include <netdb.h>
#include <sys/wait.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
#include "cache.h"


/* Recommended max cache and object sizes */
//#define MAX_CACHE_SIZE 1049000
//#define MAX_OBJECT_SIZE 10240
#define USECACHE 1
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
CacheHead cacheinfo;

/* 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 clienterror(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);
void build_request_header(rio_t *rp, char *method, char *other, char *request);
void parse_uri(char *uri, char **hostname, char **other, int *port);
int connect_endserver(char *hostname, int port);
void *thr(void *arg);
void build_response_header(rio_t *rp, char *request);
int main(int argc, char** argv)
{
    int listenfd;
    char host[MAXLINE],port[MAXLINE];
    struct sockaddr_storage clientaddr;
    socklen_t addrlen=sizeof(clientaddr);
    InitCache(&cacheinfo);
    printf("%s", user_agent_hdr);
    if (argc!=2)
    {
        printf("arguments are not enough\n");
        return 0;
    }
    listenfd = open_listenfd(argv[1]);
    if (listenfd<=0)
    {
        printf("open_listenfd is error");
        return -1;
    }
    while (1)
    {
        int acceptfd = Accept(listenfd, (SA*)&clientaddr, &addrlen);
        if (acceptfd>=0)
        {
            Getnameinfo((SA*)&clientaddr, addrlen, host, MAXLINE, port, MAXLINE,0);
            printf("Accept connection from %s:%s", host, port);
            pthread_t tid;
            pthread_create(&tid, NULL, thr, (void *)&acceptfd);
            
        }
    }
    FreeAllObj(&cacheinfo);
    return 0;
}   

void *thr(void *arg)
{
    int acceptfd=*(int *)arg;
    pthread_detach(pthread_self());
    pthread_mutex_lock(&mutex);
    doit(acceptfd);
    close(acceptfd);
    printf("tid:%lu\n", (long signed int)pthread_self());
    pthread_mutex_unlock(&mutex);
    return NULL;
}

void doit(int listenfd)
{
    rio_t rp, server_rio;//这里之所以用变量而不是用指针,是因为定义一个变量就会把整个变量的空间都分配出来,
             //但是定义指针只会分配四个字节用于存放指针变量指向的地址值,如果这里定义了指针变量就会在
             //Rio_readinitb函数中出现内存溢出,因为这个结构体的空间根本没有定义出来。
    char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE];
    char request[MAXBUF];
    char *hostname,*other;
    int port,clientfd;
    Rio_readinitb(&rp, listenfd);
    Rio_readlineb(&rp, buf, MAXLINE);
    printf("request headers:\n");
    printf("%s\n",buf);
    sscanf(buf,"%s %s %s", method, uri, version);
    if (strcmp(method,"GET"))
        clienterror(listenfd, method, "501", "Not Implemented","Tiny does not implement this method");
    parse_uri(uri, &hostname, &other, &port);
    if (hostname==NULL||other==NULL)
    {
        printf("parse_uri is error\n");
        return;
    }
#ifdef USECACHE//带缓存的代理服务器
    if (!FindObj(&cacheinfo, uri, request))//在缓存中没有找到对应内容
    {
        printf("--connect server--\n");
        build_request_header(&rp, method, other, request);
        if ((clientfd=connect_endserver(hostname, port))<0)
        {
            printf("connect_endserver is error\n");
            return;
        }

        Rio_writen(clientfd, request, strlen(request));
        Rio_readinitb(&server_rio, clientfd);
        build_response_header(&server_rio, request);
        if (AddObj(&cacheinfo, uri, request)<0)
        {
            printf("AddObj is error\n");
            return;
        }
        Close(clientfd);
    }
    
    printf("-----------request--------------------\n");
    printf("%s\n",request);
    printf("------------count--------------\n");
    printf("%d\n", cacheinfo.count);
    Rio_writen(listenfd, request, strlen(request));
#else//不带缓存的代理服务器
    build_request_header(&rp, method, other, request);
    if ((clientfd=connect_endserver(hostname, port))<0)
    {
        printf("connect_endserver is error\n");
        return;
    }

    Rio_writen(clientfd, request, strlen(request));
    Rio_readinitb(&server_rio, clientfd);
    int n;
    while ((n=Rio_readlineb(&server_rio, buf, MAXLINE))!=0)
    {
        printf("proxy received message: %s", buf);
        Rio_writen(listenfd, buf, strlen(buf));
    }
#endif
    
    
    
}

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

    /* 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, "%s<hr><em>The Tiny Web proxy</em>\r\n", body);

    /* 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 build_request_header(rio_t *rp, char *method, char *other, char *request)
{
    sprintf(request, "%s %s HTTP/1.0\n", method, other);
    char buf[MAXLINE];
    do 
    {
        Rio_readlineb(rp,buf,MAXLINE);
        //printf("%s",buf);
        strcat(request, buf);

    }while(strcmp(buf,"\r\n"));
    printf("%s", request);
}

void build_response_header(rio_t *rp, char *request)
{
    memset(request, 0, strlen(request));
    char buf[MAX_OBJECT_SIZE];
    int n;
    while ((n=Rio_readlineb(rp, buf, MAXLINE))!=0)
    {
        strcat(request, buf);
    }
    printf("%s", request);
}

void parse_uri(char *uri, char **hostname, char **other, int *port)
{
    *port=80;//默认端口为80
    static char temp[256];
    char *other_buf;//带_buf的都是临时存放的变量
    char *hostname_buf;
    char *port_buf;
    strcpy(temp, uri);
    
    if ((hostname_buf=strchr(temp, '/'))==NULL)//找到第一个反斜杠,往后两个位置就是主机名
    {
        printf("temp solve1 error\n");
        return;
    }
    if (strlen(hostname_buf)<3)
    {
        printf("temp solve2 error\n");
        return;
    }
    hostname_buf+=2;
    other_buf=strchr(hostname_buf,'/');//找到主机名后面的反斜杠,斜杠后面就是具体参数值
    //*other_buf='\0';
    //other_buf++;
    if ((port_buf=strchr(hostname_buf, ':'))!=NULL)//在主机名中找是否存在冒号,如果有,就改变端口值
    {
        *port_buf='\0';
        port_buf++;
        *port=atoi(port_buf);
    }
    *other=other_buf;
    *hostname=hostname_buf;
    
}

int connect_endserver(char *hostname, int port)
{
    char PortStr[256];
    sprintf(PortStr,"%d",port);
    return open_clientfd(hostname, PortStr);
}

/*cache.h*/
#include <stdio.h>
#include "csapp.h"

#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 10240
#define MAX_COUNT 10

typedef struct CACHE
{
    char uri[MAXLINE];
    char obj[MAX_OBJECT_SIZE];
    struct CACHE *pre_ptr;
    struct CACHE *next_ptr;
}Cache;
//在C语言中,结构体如果不加typedef,就要用struct CACHE来命名
//但C++中可以
typedef struct CACHEHEAD
{
    int count;
    Cache *head_ptr;
    Cache *tail_ptr;
}CacheHead;

int InitCache(CacheHead *cacheinfo);

int AddObj(CacheHead *cacheinfo, char *uri, char *obj);

int FindObj(CacheHead *cacheinfo, char *uri, char *obj);

int FreeAllObj(CacheHead *cacheinfo);
/*cache.c*/
#include "cache.h"
//采用LRU的思想,双向链表,最近使用的放在链表前面,以前使用的放在链表后面
int InitCache(CacheHead *cacheinfo)
{
    FreeAllObj(cacheinfo);
    cacheinfo->count=0;
    cacheinfo->head_ptr=NULL;
    cacheinfo->tail_ptr=NULL;
    return 0;
}

//cache_signal用malloc去申请
int AddObj(CacheHead *cacheinfo, char *uri, char *obj)
{
    if (strlen(obj)>MAX_OBJECT_SIZE)
    {
        printf("obj is lager, add fail\n");
        return -1;
    }
    Cache *signal_cache=(Cache*)malloc(sizeof(Cache));
    if (signal_cache==NULL)
    {
        printf("malloc is error\n");
        return -2;
    }
    strcpy(signal_cache->uri, uri);
    strcpy(signal_cache->obj, obj);
    if (cacheinfo->count==0)//内存链表上没有元素
    {
        signal_cache->pre_ptr=NULL;
        signal_cache->next_ptr=NULL;
        cacheinfo->count++;
        cacheinfo->head_ptr=signal_cache;
        cacheinfo->tail_ptr=signal_cache;
    }else if ((cacheinfo->count>0)&&(cacheinfo->count<MAX_COUNT))//链表上元素数目在0-10之间
    {
        signal_cache->pre_ptr=NULL;
        signal_cache->next_ptr=cacheinfo->head_ptr;
        cacheinfo->head_ptr->pre_ptr=signal_cache;
        cacheinfo->count++;
        cacheinfo->head_ptr=signal_cache;
    }else if(cacheinfo->count>=MAX_COUNT)//链表上挂满元素
    {
        cacheinfo->head_ptr->pre_ptr=signal_cache;
        signal_cache->pre_ptr=NULL;
        signal_cache->next_ptr=cacheinfo->head_ptr;
        cacheinfo->head_ptr=signal_cache;
        Cache *temp=cacheinfo->tail_ptr;
        cacheinfo->tail_ptr=temp->pre_ptr;
        temp->pre_ptr->next_ptr=NULL;
        free(temp);
    }
    return 0;
}

int FindObj(CacheHead *cacheinfo, char *uri, char *obj)
{
    Cache *signal_cache=cacheinfo->head_ptr;
    /*在链表中寻找是否存在这个uri*/
    while ((signal_cache!=NULL)&&(strcmp(signal_cache->uri, uri)))
    {
        signal_cache=signal_cache->next_ptr;
    }
    if (signal_cache==NULL)
    {
        return 0;
    }
    /*如果找到了就把该元素移到第一个去*/
    strcpy(obj, signal_cache->obj);
    if (cacheinfo->head_ptr==signal_cache)//找到的元素在第一个位置,什么都不用改变
    {}else if(cacheinfo->tail_ptr==signal_cache)//找到的元素在最后一个位置,移到最前面去
    {
        cacheinfo->tail_ptr=signal_cache->pre_ptr;
        signal_cache->next_ptr=cacheinfo->head_ptr;
        signal_cache->pre_ptr=NULL;
        cacheinfo->head_ptr=signal_cache;
    }
    else//找到的元素在中间位置,移到第一个去
    {
        signal_cache->pre_ptr->next_ptr=signal_cache->next_ptr;
        signal_cache->next_ptr->pre_ptr=signal_cache->pre_ptr;
        cacheinfo->head_ptr->pre_ptr=signal_cache;
        signal_cache->pre_ptr=NULL;
        signal_cache->next_ptr=cacheinfo->head_ptr;
        cacheinfo->head_ptr=signal_cache;
    }
    return 1;
}

int FreeAllObj(CacheHead *cacheinfo)
{
    Cache *signal_cache=cacheinfo->head_ptr;
    Cache *temp;
    while(signal_cache)
    {
        temp=signal_cache->next_ptr;
        free(signal_cache);
        signal_cache=temp;
    }
    cacheinfo->head_ptr=NULL;
    cacheinfo->tail_ptr=NULL;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值