lab7 proxylab

在这里插入图片描述

前情提要,如果看了书本,这个lab难度不高,但是如果不看书,难度还是挺高的,并且这个lab会用到cachelab中学到的东西,需要阅读

  1. 第十章:系统编程
  2. 第十一章:网络编程
  3. 第十二章:并发

实验介绍

  1. 使用代理完成客户端和服务器的连接(HTTP操作,socket通信)
    1. 接受客户端的连接,读并分析请求
    2. 将请求发送给服务器
    3. 读取服务器的回应,并将回应发送给对应的客户端
  2. 实现多线程的功能
  3. 增加cache功能

测试

测试:./driver.sh
50 15 15

第一部分:实现一个顺序的网络代理

任务要求

  1. 最开始,代理应该监听某个端口来等待连接的请求,这个端口通过命令行给出
  2. 一旦建立连接,代理应该读取并解析请求。它需要确定这个请求是否发送了一个合法的HTTP请求
  3. 如果这个请求合法,则发送给服务器,然后将服务器的response返回给客户

具体实现

  1. main函数打开一个监听的描述符,如果通过这个监听描述符accept成功了,则打开了一个用于通信的描述符fd,将fd作为doit的函数,调用doit
  2. doit函数与描述符b建立通信,读取客户端发来的请求,这个请求一定是以下两种形式之一
    1. 指定端口 GET http://www.cmu.edu:8080/hub/index.html HTTP/1.1
    2. 固定端口80 GET http://www.cmu.edu/hub/index.html HTTP/1.1
  3. 将上面收到的请求分解,主要是得到中间的url,然后将url分解,得到hostportpath,以指定端口为例,这三个分别是
    1. www.cmu.edu
    2. 8080
    3. /hub/index.html
  4. 根据上面得到的三个参数,构建发往服务器的request
  5. 这个request是HTTP格式(具体实现上就把这个放到一个字符数组就行了,每一行通过\r\n隔开,并且最后要多一行\r\n),由请求头和请求行组成,实验文档要求格式如下:
    1. GET /hub/index.html HTTP/1.0
    2. Host: www.cmu.edu
    3. User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:10.0.3) Gecko/20120305 Firefox/10.0.3
    4. Connection: close
    5. Proxy-Connection: close
  6. 与服务器建立连接,得到server_fd描述符,将上面已经生成好的request发往服务器
  7. 不断地读服务器返回的值,写入fd文件描述符
#include "csapp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
#define MAXLINE 8192/* 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";typedef struct {char host[MAXLINE];char port[MAXLINE];char path[MAXLINE];
} URI;void cout_uri_format_error() { printf("wrong uri format\n"); }void parse_line(URI *req_uri, char *uri) {char *host_start = strstr(uri, "://");if (host_start == NULL) {cout_uri_format_error();return;}host_start += 3;char *port_start = strstr(host_start, ":");char *path_start = strstr(host_start, "/");if (path_start == NULL) {cout_uri_format_error();return;}strcpy(req_uri->path, path_start);*path_start = '\0';if (port_start != NULL) {strcpy(req_uri->port, port_start + 1);*port_start = '\0';} else {strcpy(req_uri->port, "80");}strcpy(req_uri->host, host_start);return;
}void build_req_server(char *req_server, URI *req_uri) {sprintf(req_server, "GET %s HTTP/1.0\r\n", req_uri->path);sprintf(req_server, "%sHost: %s\r\n", req_server, req_uri->host);sprintf(req_server, "%s%s", req_server, user_agent_hdr);sprintf(req_server, "%sConnection: close\r\n", req_server);sprintf(req_server, "%sProxy-Connection: close\r\n", req_server);sprintf(req_server, "%s\r\n", req_server);
}void doit(int fd) {// 初始化rio类函数的缓冲区rio_t rio;Rio_readinitb(&rio, fd);// 读入这一行http请求char buf[MAXLINE];Rio_readlineb(&rio, buf, MAXLINE);printf("Request headers:\n");printf("%s", buf);char method[MAXLINE], uri[MAXLINE], version[MAXLINE];// 解析这一行http请求,总共三个部分if (sscanf(buf, "%s %s %s", method, uri, version) != 3) {printf("HTTP Requset Format Wrong!\n");return;}// 判断是否是GET请求,这个比较函数忽略大小写,get也行if (strcasecmp(method, "GET")) {printf("method: %s not implemented\n", method);return;}// 至此,已经完成了对客户端请求的解析,接下来要构造出对服务器的请求// 首先解析我们的uri,得到host port pathURI *req_uri = (URI *)malloc(sizeof(URI));parse_line(req_uri, uri);// 根据我们的信息,构造出真正的发往服务器的请求char req_server[MAXLINE];build_req_server(req_server, req_uri);// 开始连接服务器int server_fd = Open_clientfd(req_uri->host, req_uri->port);if (server_fd < 0) {printf("connection failed\n");return;}// 连接成功,设置缓冲区,将request写入rio_t server_rio;Rio_readinitb(&server_rio, server_fd);Rio_writen(server_fd, req_server, strlen(req_server));// 等待服务器的返回,并写入客户端的fd中size_t rec_bytes;while ((rec_bytes = Rio_readlineb(&server_rio, buf, MAXLINE)) != 0) {printf("proxy received %d bytes\n", (int)rec_bytes);Rio_writen(fd, buf, rec_bytes);}Close(server_fd);
}int main(int argc, char **argv) {if (argc != 2) {fprintf(stderr, "usage: %s <port>\n", argv[0]);exit(1);}// 监听请求连接的端口int listenfd = Open_listenfd(argv[1]);// 与客户端进行连接int connfd;char hostname[MAXLINE], port[MAXLINE];socklen_t clientlen;struct sockaddr_storage clientaddr;while (1) {clientlen = sizeof(clientaddr);connfd = Accept(listenfd, (SA *)(&clientaddr), &clientlen);Getnameinfo((SA *)(&clientaddr), clientlen, hostname, MAXLINE, port,MAXLINE, 0);printf("Accepted connection from(%s,%s)\n", hostname, port);doit(connfd);Close(connfd);}return 0;
}

第二部分:并发

任务要求

  1. 实现并发即可,没有要求用什么样的方式

具体实现

  1. 采用生产者消费者的方式,和书上第12.5.5节的代码几乎完全一样
  2. 需要在main函数中加入一个Signal(SIGPIPE, SIG_IGN);以屏蔽SIGPIPE信号。我不太清楚不屏蔽会怎么样,可能是不屏蔽的话,客户端如果意外挂了,会导致代理服务器一起挂了
#define SUBFSIZE 16
#define NTHREADS 4typedef struct {int *buf;int n;int front;int rear;sem_t mutex;sem_t slots;sem_t items;
} sbuf_t;sbuf_t sbuf;void sbuf_init(sbuf_t *sp, int n) {sp->buf = Calloc(n, sizeof(int));sp->n = n;sp->front = sp->rear = 0;Sem_init(&sp->mutex, 0, 1);Sem_init(&sp->slots, 0, n);Sem_init(&sp->items, 0, 0);
}void sbuf_deinit(sbuf_t *sp) { Free(sp->buf); }void sbuf_insert(sbuf_t *sp, int item) {P(&sp->slots);P(&sp->mutex);sp->buf[(++sp->rear) % (sp->n)] = item;V(&sp->mutex);V(&sp->items);
}int sbuf_remove(sbuf_t *sp) {P(&sp->items);P(&sp->mutex);int item = sp->buf[(++sp->front) % (sp->n)];V(&sp->mutex);V(&sp->slots);return item;
}void *thread(void *vargp) {Pthread_detach(Pthread_self());while (1) {int connfd = sbuf_remove(&sbuf);doit(connfd);Close(connfd);}
}int main(int argc, char **argv) {if (argc != 2) {fprintf(stderr, "usage: %s <port>\n", argv[0]);exit(1);}// 监听请求连接的端口Signal(SIGPIPE, SIG_IGN);int listenfd = Open_listenfd(argv[1]);// 线程池sbuf_init(&sbuf, SUBFSIZE);pthread_t pid;for (int i = 0; i < NTHREADS; i++) {Pthread_create(&pid, NULL, thread, NULL);}// 与客户端进行连接int connfd;char hostname[MAXLINE], port[MAXLINE];socklen_t clientlen;struct sockaddr_storage clientaddr;while (1) {clientlen = sizeof(clientaddr);connfd = Accept(listenfd, (SA *)(&clientaddr), &clientlen);sbuf_insert(&sbuf, connfd);}return 0;
}

第三部分:cache

任务要求

  1. 这里说是cache,还不如说是一个大号的哈希表,以uri为键,以对应的资源为值。然后对这个哈希表的长度有点要求,大概10个表项。因为题目要求#define MAX_CACHE_SIZE 1049000#define MAX_OBJECT_SIZE 102400,其中object的意思就是一行,差不多就是十倍的样子
  2. 如果某个uri对应的资源太大了, 那就不考虑加入cache
  3. 对这个cahce需要实现并发访问,即加上锁,这里加入读写锁

具体实现

  1. 结合cachelab中cache的结构,还需要额外加上data字段
  2. 如果要实现真正的LRU,在并发访问的基础上,还需要对timestamp也加锁,否则就要用原子类型的变量
  3. 这个实现结合代码来看,思路还是比较清晰的,不再赘述
    我在这里犯了两个小错,结果导致debug了好久
  4. cacheline中的tagdata的长度是不一样的,我一开始把data长度弄成了MAXLINE,结果0分
  5. doit中我们用uri去读cache以及写cache,但是我们在doitparse_line函数里,是修改了uri的,因此要给uri搞一个备份,否则在写cache的时候,就错了
/* Recommended max cache and object sizes */
#define MAX_CACHE_SIZE 1049000
#define MAX_OBJECT_SIZE 102400
#define MAXLINE 8192typedef struct {int is_valid;char tag[MAXLINE];char data[MAX_OBJECT_SIZE];long long access_time;int read_cnt;sem_t read_lock;sem_t write_lock;
} cacheline;#define MAX_CACHE_LINES 10
cacheline Cache[MAX_CACHE_LINES];sem_t time_mutex;
long long time_stamp = 1;void init_cache() {for (int i = 0; i < MAX_CACHE_LINES; i++) {Cache[i].is_valid = 0;Cache[i].access_time = 0;Cache[i].read_cnt = 0;Sem_init(&Cache[i].read_lock, 0, 1);Sem_init(&Cache[i].write_lock, 0, 1);}Sem_init(&time_mutex, 0, 1);
}void read_in(int i) {P(&Cache[i].read_lock);if (Cache[i].read_cnt == 0) {P(&Cache[i].write_lock);}Cache[i].read_cnt++;V(&Cache[i].read_lock);
}void read_out(int i) {P(&Cache[i].read_lock);if (Cache[i].read_cnt == 1) {V(&Cache[i].write_lock);}Cache[i].read_cnt--;V(&Cache[i].read_lock);
}int read_cache(int fd, char *uri) {int flag = 0;for (int i = 0; i < MAX_CACHE_LINES; i++) {read_in(i);if (Cache[i].is_valid && !strcmp(uri, Cache[i].tag)) {flag = 1;P(&time_mutex);Cache[i].access_time = time_stamp++;V(&time_mutex);Rio_writen(fd, Cache[i].data, strlen(Cache[i].data));}read_out(i);if (flag) {return 0;}}return -1;
}void write_cache(char *uri, char *data) {int has_empty = -1;int lru_evict = 0;for (int i = 0; i < MAX_CACHE_LINES; i++) {read_in(i);if (Cache[i].is_valid == 0) {has_empty = i;}if (Cache[i].access_time < Cache[lru_evict].access_time) {lru_evict = i;}read_out(i);if (has_empty != -1) {break;}}int write_index = (has_empty == -1) ? lru_evict : has_empty;P(&Cache[write_index].write_lock);Cache[write_index].is_valid = 1;P(&time_mutex);Cache[write_index].access_time = time_stamp++;V(&time_mutex);strcpy(Cache[write_index].tag, uri);strcpy(Cache[write_index].data, data);V(&Cache[write_index].write_lock);
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/28111.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Licheepi Nano屏幕驱动并输出打印信息

Licheepi Nano买回来好长时间&#xff0c;没咋玩&#xff0c;最近看了一个利用F1C100S自制迷你电脑的博客&#xff0c;里面主要参考的就是Licheepi Nano。我打算先在Licheepi Nano上完成屏幕操作、Debian文件系统和USB键盘等内容&#xff0c;这里介绍怎样利用Licheepi Nano外接…

Oracle单实例升级补丁

目录 1.当前DB环境2.下载补丁包和opatch的升级包3.检查OPatch的版本4.检查补丁是否冲突5.关闭数据库实例&#xff0c;关闭监听6.应用patch7.加载变化的SQL到数据库8.ORACLE升级补丁查询 oracle19.3升级补丁到19.18 1.当前DB环境 [oraclelocalhost ~]$ cat /etc/redhat-releas…

记录--说一说css的font-size: 0

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 平常我们说的font-size&#xff1a;0&#xff1b;就是设置字体大小为0对吧&#xff0c;但是它的用处不仅仅如此哦&#xff0c;它还可以消除子行内元素间额外多余的空白&#xff01; 问题描述&#xff…

leetcode 图算法小结

文章目录 1 DFS和BFS797. 所有可能的路径200. 岛屿数量 1 DFS和BFS 深度优先遍历一般采用回溯算法进行解决。回溯算法&#xff0c;其实就是dfs的过程。 void dfs(参数) {处理节点dfs(图&#xff0c;选择的节点); // 递归回溯&#xff0c;撤销处理结果 }广度优先搜索理解为层次…

Qt小项目贪吃蛇实线,主要掌握定时器、信号与槽、按键事件、绘制事件、坐标运算、随机数生成等

Qt小项目贪吃蛇实线&#xff0c;主要掌握定时器、信号与槽、按键事件、绘制事件、坐标运算、随机数生成等 Qt 贪吃蛇演示QWidget 绘制界面项目源文件 注释清晰widget.hwidget.cpp 拓展QTimerQKeyEventQRectFQPointFQPainterQIcon Qt 贪吃蛇演示 QWidget 绘制界面 项目源文件 注…

开关电源控制--电流纹波率

什么是电流纹波率 电流纹波率&#xff08;Current Ripple Ratio&#xff09;是开关电源控制中一个重要的参数。它表示输出电流的波动程度&#xff0c;通常以百分比表示。 当电流纹波率为0.4时&#xff0c;意味着输出电流的波动相对较小&#xff0c;波动范围约为输出电流的0.4…

C# 有效的字母异位词

242 有效的字母异位词 给定两个字符串 和 &#xff0c;编写一个函数来判断 是否是 的字母异位词。stts 注意&#xff1a;若 和 中每个字符出现的次数都相同&#xff0c;则称 和 互为字母异位词。stst 示例 1: 输入: s “anagram”, t “nagaram” 输出: true 示例 2: 输…

11. Redis基础知识

文章目录 一、概述二、数据类型STRINGLISTSETHASHZSET 三、数据结构字典跳跃表 四、使用场景计数器缓存查找表消息队列会话缓存分布式锁实现其它 五、Redis 与 Memcached数据类型数据持久化分布式内存管理机制 六、键的过期时间七、数据淘汰策略八、持久化RDB 持久化AOF 持久化…

【C++从0到王者】第十八站:手把手教你写一个简单的优先级队列

文章目录 一、优先级队列简介二、优先级队列的接口说明1.基本介绍及其使用2.构造函数3.求数组中第k个最大的元素 三、手撕优先级队列四、仿函数1.仿函数介绍2.优先级队列添加仿函数3.需要自己写仿函数的情形 五、优先级队列完整代码 一、优先级队列简介 优先级队列是一种容器适…

Java经典面试题总结(一)

Java经典面试题总结&#xff08;一&#xff09; 题一&#xff1a;Java编译运行原理题二&#xff1a;JDK&#xff0c;JVM&#xff0c;JRE三者之间的关系题三&#xff1a;谈一下对冯诺依曼体系的了解题四&#xff1a;重载与重写的区别题五&#xff1a;拆箱装箱是指什么&#xff1…

Netty 入门指南

文章目录 前言Netty介绍Netty发展历程Netty核心组件实现HTTP服务器总结 前言 上文《BIO、NIO、IO多路复用模型详细介绍&Java NIO 网络编程》介绍了几种IO模型以及Java NIO&#xff0c;了解了在网络编程时使用哪种模型可以提高系统性能及效率。即使Java NIO可以帮助开发人员…

44.实现爱尔兰B公式计算并输出表格(matlab程序)

1.简述 1.话务量定义 话务量指在一特定时间内呼叫次数与每次呼叫平均占用时间的乘积。 话务量反映了电话负荷的大小&#xff0c;与呼叫强度和呼叫保持时间有关。呼叫强度是单位时间内发生的呼叫次数&#xff0c;呼叫保持时间也就是占用时间。 话务量计算方法 话务量公式为…

低功耗LoRaWAN国产低功耗LoRa+RF射频前端芯片XD6500S

目录 典型应用XD6500S简介芯片特性 LoRa系列选型参考 LoRa是为低数据速率、远距离距离和超低功耗而优化的扩频协议&#xff0c;用于LPWAN应用程序的通信。 典型应用 一、智慧农业   智慧农业大田解决方案利用传感设备、自动化控制设备、气象站实时监测采集田间土壤墒情、气象…

HTTP协议——应用层

HTTP协议 只要保证, 一端发送时构造的数据, 在另一端能够正确的进行解析, 就是ok的. 这种约定, 就是 应用层协议 HTTP简介 HTTP&#xff08;Hyper Text Transfer Protocol&#xff09;协议又叫做超文本传输协议&#xff0c;是一个简单的请求-响应协议&#xff0c;HTTP通常运行…

MyBatis简介及环境配置

文章目录 一、什么是MyBatis二、MyBatis开发环境配置1.创建数据库表2.添加MyBatis框架支持3.配置连接字符串和MyBatis4.添加业务代码流程 一、什么是MyBatis MyBatis是一种持久层框架&#xff0c;也是一种ORM框架&#xff08;Object Relational Mapping即对象关系映射&#xf…

【AutoLayout案例1-按钮居中显示 Objective-C语言】

一、按钮居中显示 1.接下来,我们就用这个autoLayout,自动布局,给大家写一个,实现几个案例,给大家看一下 那么,首先,第一个,大家注意, 当我们使用autoLayout,自动布局的时候,我们新建一个项目, 这个新建的项目,里面有一个控制器,这个控制器,是不是默认,是四四…

基于短信宝API零代码实现短信自动化业务

场景描述&#xff1a; 基于短信宝开放的API能力&#xff0c;实现在特定事件&#xff08;如天气预警&#xff09;或定时自动发送短信&#xff08;本文以定时群发短信为例&#xff09;。通过Aboter平台如何实现呢&#xff1f; 使用方法&#xff1a; 首先创建一个IPaaS流程&…

iPhone苹果手机地震预警功能怎么开启?

iPhone苹果手机地震预警功能怎么开启&#xff1f; 1、打开iPhone苹果手机设置&#xff1b; 2、在iPhone苹果手机设置内找到辅助功能&#xff1b; 3、在辅助功能内找到触控&#xff1b; 4、在iPhone苹果手机辅助功能触控内找到振动&#xff0c;如果是关闭状态请启&#xff1b; …

C++ STL vector

目录 一.认识vector 二.vector的使用 1.vector的构造函数 2.vector的迭代器 2.1 begin&#xff08;&#xff09;&#xff0c;end&#xff08;&#xff09; 2.2 rbegin&#xff08;&#xff09;&#xff0c;rend&#xff08;&#xff09; 2.3 迭代器初始化对象 3. vector…

Linux中安装Tomcat

Linux安装Tomcat 操作步骤: 1、使用FinalShell自带的上传工具将Tomcat的二进制发布包上传到Linux 2、解压安装包&#xff0c;命令为tar -zxvf apache-tomcat-7.0.57.tar.gz -C /usr/local 3、进入Tomcat的bin目录启动服务&#xff0c;命令为sh startup.sh或者./ startup.sh …