哈工大计算机网络实验一(HTTP 代理服务器的设计与实现)

流程示意图及代码

1. 整体框架

在这里插入图片描述

2. 两种报文

在这里插入图片描述

3. 缓存部分

在这里插入图片描述
与缓存相关的函数如下

//根据url构造文件名
void makeFilename(char *url, char *filename)
{
    while (*url != '\0')
    {
        if (*url != '/' && *url != ':' && *url != '.')
            // 这些字符通常在文件系统中是不允许作为文件名的
            *filename++ = *url; //将 url 指向的字符复制到 filename 指向的位置
        url++;
    }
    strcat(filename, ".txt");
}

// 将 HTTP 响应内容写入缓存文件中
void makeCache(char *buffer, char *url)
{
    char *p;                      // 指向分割后的字符串
    char num[10];                 // 状态码字符串
    char tempbuffer[MAXSIZE + 1]; // 临时数据
    const char *delim = "\r\n";   // 定义分隔符
    ZeroMemory(num, 10);
    ZeroMemory(tempbuffer, MAXSIZE + 1);
    memcpy(tempbuffer, buffer, strlen(buffer)); // 将接收到的数据复制到临时缓冲区(因为strtok会修改字符串)
    p = strtok(tempbuffer, delim);              // 使用分隔符分割临时缓冲区中的字符串,获取第一个子字符串(HTTP状态行)
    memcpy(num, &p[9], 3);                      // 从状态行中复制状态码(3个字符)到状态码字符串
    if (strcmp(num, "200") == 0)
    { // 状态码是200时表示请求成功,缓存
        char filename[1024] = {0};
        makeFilename(url, filename);
        printf("filename : %s\n", filename);
        ofstream of;                 // 文件流对象
        of.open(filename, ios::out); // 打开文件,准备写入
        of << buffer << endl;        // 将接收到的数据(包括HTTP头部和正文)写入文件
        of.close();
        printf("\n=====================================\n\n");
        printf("\n***********网页已经被缓存**********\n");
    }
}

//分析HTTP头部的field字段,如果包含该field则返回true,并获取日期
BOOL ParseDate(char *buffer, char *field, char *tempDate)
{
    char *p;
    char *temp[5];
    const char *delim = "\r\n";
    ZeroMemory(temp, 5);
    p = strtok(buffer, delim);
    int len = strlen(field) + 2; // 计算字段名的长度加上冒号和空格的宽度

    while (p)
    { // 循环遍历 HTTP 头部的每一行
        // 使用 strstr 查找当前行是否包含指定的字段名
        if (strstr(p, field) != NULL)
        {
            // 如果找到了字段名,从字段名之后的位置开始复制,直到行尾
            memcpy(tempDate, &p[len], strlen(p) - len);
            return TRUE; // 返回 TRUE 表示找到了字段,并已获取日期
        }
        // 继续获取下一行
        p = strtok(NULL, delim);
    }
    // 如果循环结束都没有找到字段,返回 FALSE
    return FALSE;
}

// 改造 HTTP 请求报文
void makeNewHTTP(char *buffer, char *value)
{
    const char *field = "Host";
    const char *newfield = "If-Modified-Since: ";
    char temp[MAXSIZE];
    ZeroMemory(temp, MAXSIZE);
    char *pos = strstr(buffer, field); // 在 buffer 指向的字符串中查找字段名 "Host" 的位置

    for (int i = 0; i < strlen(pos); i++)
        temp[i] = pos[i]; // 将找到的字段 "Host" 及其后面的内容复制到临时缓冲区
    *pos = '\0';          // 将字段 "Host" 的位置替换为字符串结束符,临时终止字符串

    while (*newfield != '\0')
        *pos++ = *newfield++; // 将新字段 "If-Modified-Since: " 复制到 buffer 的相应位置
    while (*value != '\0')
        *pos++ = *value++; // 将日期值复制到 buffer 的相应位置
    *pos++ = '\r';         // 插入行结束符的第一个字符 '\r'
    *pos++ = '\n';         // 插入行结束符的第二个字符 '\n'

    for (int i = 0; i < strlen(temp); i++)
        *pos++ = temp[i];
}

//获取缓存
void getCache(char *buffer, char *filename)
{
    char *p;
    char num[10];
    char tempBuffer[MAXSIZE + 1];
    const char *delim = "\r\n";
    ZeroMemory(num, 10);
    ZeroMemory(tempBuffer, MAXSIZE + 1);
    memcpy(tempBuffer, buffer, strlen(buffer));
    p = strtok(tempBuffer, delim); //分割出第一个子字符串
    memcpy(num, &p[9], 3);
    if (strcmp(num, "304") == 0)
    { //主机返回的报文中的状态码为304时返回已缓存的内容
        printf("\n=====================================\n\n");
        printf("***********从本机获得缓存**************\n");
        ZeroMemory(buffer, strlen(buffer));
        FILE *in = NULL;
        if ((in = fopen(filename, "r")) != NULL)
        {
            fread(buffer, sizeof(char), MAXSIZE, in);
            fclose(in);
        }
        needCache = FALSE;
    }
}

4. 扩展部分

4.1 网站过滤

bool ForbiddenToConnect(char *url)
{
    char *forbiddernUrl = (char *)"http://www.hit.edu.cn/";
    if (!strcmp(url, forbiddernUrl))
    {
        return true;
    }
    else
        return false;
}

4.2 用户过滤

//用户过滤
bool UserIsForbidden(char *userID)
{
    for (int i = 0; i < IPnum; i++) // 遍历被禁止访问代理服务器的 IP 地址数组
    {
        if (strcmp(userID, ForbiddenIP[i]) == 0)
        {
            //用户IP在禁用IP表中
            return true;
        }
    }
    return false;
}

4.3 网站引导

//用户过滤
bool UserIsForbidden(char *userID)
{
    for (int i = 0; i < IPnum; i++) // 遍历被禁止访问代理服务器的 IP 地址数组
    {
        if (strcmp(userID, ForbiddenIP[i]) == 0)
        {
            //用户IP在禁用IP表中
            return true;
        }
    }
    return false;
}

网站引导在主函数的部分代码:

if (GotoFalseWebsite(httpHeader->url)) // 如果请求的URL是预设的需要重定向的网站
    {
        char *pr;                                                                                 // 定义一个指针,用于构建HTTP响应报文
        int fishing_len = 0;                                                                      // 定义一个变量,用于存储字符串长度
        fishing_len = strlen("HTTP/1.1 302 Moved Temporarily\r\n");                               // 计算HTTP状态行的长度
        memcpy(FishBuffer, "HTTP/1.1 302 Moved Temporarily\r\n", fishing_len);                    // 将状态行复制到响应缓冲区
        pr = FishBuffer + fishing_len;                                                            // 将指针移动到状态行之后,准备添加新的头部字段
        fishing_len = strlen("Connection:keep-alive\r\n");                                        // 计算Connection头部的长度
        memcpy(pr, "Connection:keep-alive\r\n", fishing_len);                                     // 添加Connection头部到响应报文
        pr = pr + fishing_len;                                                                    // 再次移动指针,准备添加下一个头部字段
        fishing_len = strlen("Cache-Control:max-age=0\r\n");                                      // 计算Cache-Control头部的长度
        memcpy(pr, "Cache-Control:max-age=0\r\n", fishing_len);                                   // 添加Cache-Control头部到响应报文
        pr = pr + fishing_len;                                                                    // 再次移动指针
        fishing_len = strlen("Location: http://today.hit.edu.cn/\r\n\r\n");                       // 计算Location头部的长度
        memcpy(pr, "Location: http://today.hit.edu.cn/\r\n\r\n", fishing_len);                    // 添加Location头部到响应报文
        ret = send(((ProxyParam *)lpParameter)->clientSocket, FishBuffer, sizeof(FishBuffer), 0); // 将构建好的响应报文发送给客户端
        goto error;                                                                               // 跳转到错误处理部分,结束本次请求处理
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值