流程示意图及代码
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; // 跳转到错误处理部分,结束本次请求处理
}