Socket编程日志 Week2
一、实验概要
第二周需要实现的功能模块主要有以下几部分。
请求响应模块
1.正确响应GET/HEAD/POST请求,并能建立持久连接
2.支持四种错误代码:400,404,501,505
3.妥善管理缓冲区,避免缓冲区溢出错误
文件错误处理模块
1.能够处理文件读写过程中遇到的错误,如权限问题、文件不存在、IO错误等
日志模块
1.按“Error Log”格式记录服务器的出错情况
2.按“Access Log”的“Common Log Format”格式记录服务器处理的请求
3.其它辅助调试的日志记录(不做格式要求)
二、协议设计
1. 响应生成模块
首先,需要实现对GET、HEAD、POST三种请求的响应生成。
GET响应生成
数据结构设计
响应数据结构
为了实现HTTP GET响应,首先需要了解正确的响应结构是什么样的。如下所示是需要实现的HTTP GET响应结构示意图。
根据手册要求,响应中必须包含"HTTP/1.1 200 OK\r\n"
,而响应实体部分则需从请求文件中获取。因此可以定义响应的结构为一个字符串,具体组成如下。"HTTP/1.1 200 OK\r\n" + Data_in_file
此外,根据手册要求,缓冲区大小应设为8192
。文件状态结构
根据手册提示,使用stat
函数来获取文件状态,并以此检测文件权限、大小等信息,处理可能产生的文件读写错误。而stat
的返回值为一个自定义结构struct stat
,该结构定义在头文件sys/stat.h
中,其部分重要定义如下。struct stat 1
2mode_t st_mode; /* [XSI] Mode of file (see below) */ \
off_t st_size; /* [XSI] file size, in bytes */ \其中,
st_mode
为该文件的模式码,包含该文件的类型、权限信息等重要信息;而st_stze
为该文件的大小,可用于判断文件大小是否超限。
而对st_mode
值的定义,继续深入查看,可以在sys/stat.h
和sys/_types/_s_ifmt.h
文件中找到对于该值的部分定义如下。st_mode 1
2
3
4
5
6
7
8
9
10
11/* file "sys/stat.h" */
/* file "sys/_types/_s_ifmt.h" */
/* File type */
/* File mode */根据定义,如果宏
S_ISREG(st_mode)
返回TRUE
,则代表该文件为常规媒体文件,文件类型正确;
同时,可以仿照宏S_ISREG(m)
,通过表达式((st_mode) & S_IRWXU) == S_IRUSR
定义宏S_ISRFL(m)
检查文件的权限,若返回TRUE
则代表用户有可读权限,权限类型正确。
协议规则设计
要生成针对HTTP GET请求的响应,大体可以分为一下几部分:
从请求中获取请求文件的
URL
,并以此设置文件路径
若为默认URL
,则将路径设为默认文件路径;
若为指定的URL
,则根据URL
拼接路径;
若URL
长度超限,返回生成失败。根据路径打开文件,检查文件权限、大小等信息
根据前面解析出的路径尝试获取该文件的stat
信息,若获取失败则返回生成失败;
比对文件的st_mode
,判断其是否为常规媒体类型文件,且用户拥有可读权限,若不符合则返回生成失败;
获取文件的st_size
,计算其与200 OK
响应的长度和是否小于缓冲区长度,若不符合则返回生成失败。将文件内容和
200 OK
响应拷贝至缓冲区中作为响应
打开该文件,打开失败则返回生成失败;
清空缓冲区,将200 OK
响应拷贝至缓冲区中,再将文件内容拼接至缓冲区中,生成响应。
根据以上设计,可以绘制出生成HTTP GET响应的流程图。
HEAD响应生成
HTTP HEAD请求的响应生成与GET类似,只不过在最后生成响应时只需包含200 OK
响应的内容即可。
因此,容易得到HTTP HEAD响应的流程图如下。
POST响应生成
HTTP POST响应的生成则更为简单,根据手册要求,只需将请求echo回客户端即可。而这一功能在Week 1中已经成功实现了,因此此处不再讨论。
2. 服务器响应模块
随后,还需要对Week 1中实现的服务器响应模块进行一定更改,以支持对GET、HEAD、POST请求的分别响应,以及对404、505这两种新错误的响应。
此次实验不需要在此模块中定义新的数据结构,因此跳过数据结构设计部分。
协议规则设计
根据实验要求,响应模块总共需要支持7种不同类型的响应,分别如下:
HTTP GET 请求
HTTP HEAD 请求
HTTP POST 请求
400 Bad Request 错误
501 Not Implement 错误
404 Not Found 错误
505 Version not supported 错误
对于GET、HEAD、POST请求,响应的主要功能已经实现,只需按照本次实验的要求对其进行分别处理,并通过前面设计的响应生成模块生成对应的响应内容,存储至缓冲区中发送即可;
对于400和501错误,前面已经实现,无需更改;
对于505错误,要求如下:
请求消息解析完成后,将其HTTP版本与服务器支持HTTP版本(HTTP/1.1
)进行比对,若版本不匹配,则响应"HTTP/1.1 505 HTTP Version not supported\r\n\r\n"
。
对于404错误,要求如下:
若在GET和HEAD请求的响应生成模块中,对文件进行获取和读入时出现任何错误,导致响应生成失败,程序返回HTTP_FAIL
,则响应"HTTP/1.1 404 Not Found\r\n\r\n"
。
3. 日志记录模块
最后,还需要按照给定格式记录服务器响应请求的Access Log
,和服务器错误的Error Log
。此外,也可以根据自己的需求记录相关日志信息,无格式要求。
数据结构设计
显然,为了顺利记录日志,需要在程序中对文件进行读写操作。在C语言中使用FILE
结构即可定义一个文件,因此无需自行定义相关数据结构。
随后,还需理清Apache手册中Access Log
和Error Log
的格式。经过阅读,并按实验的实际情况作出适当调整后,可以得到格式如下。
Access Log: IP:Port - frank [Time] "http_method URL http_version" 200 OK body_size
Error Log: [Time] [core:error] [client: IP:Port] error_info
注:IP:Port在本实验环境中统一为127.0.0.1:9999
对于Access Log
,使用之前分析或定义过的数据结构即可完成日志的生成和写入,无需进一步分析;
而对于Error Log
,可以发现在其最后需要输出当前错误类型对应的错误信息error_info
。由于错误种类较多,错误信息均为字符串,且需考虑到程序对支持其它错误的可扩展性,因此在此可以通过自定义枚举和查找表数据结构来更便捷的生成错误日志。
首先,需要设计一个枚举类型,用于存储每种错误类型,形如如下伪代码。
1 | enum ERROR_TYPE |
随后,再定义一个字符串数组,按前面枚举类型的顺序依次存储对应的错误信息,形如如下伪代码。
1 | log_error_1[] = Error_Info_1 |
如此,便得到了一个错误信息的查找表。通过error_table[ERROR_TYPE]
就能得到对应的错误信息,以结构化生成相应的日志记录。同时,可以便捷、快速的扩展新的错误类型,只需为其添加ERROR_TYPE
和对应的log_error
,并插入至error_table
的正确位置即可。
协议规则设计
在完成日志记录格式和相关数据结构的定义后,很快便能完成对日志模块的设计。只需编写两个日志记录函数,用于参数化写入日志记录,并在需要记录的地方调用即可。此外,对于自定义的日志记录,直接在需要记录处对日志文件进行写入即可。
4. 缓冲区管理
协议设计
实验手册中要求对缓冲区进行合理的管理,以避免通信过程中出现缓冲区溢出错误。按照手册要求,共有3种潜在的缓冲区溢出风险。
URL长度超限
响应内容长度超限
请求头部长度超限
对于前两种情况,在先前的设计中已经进行了相关的设计和说明,不再赘述,此处只对请求头部长度超限的情况进行设计。
根据要求,请求头部大小应不超过8192
字节。因此首先需要将缓冲区大小更改为8192
字节。随后,在从客户端接收到请求后,只需对接收到的消息长度进行判断,若等于8192
(由于缓冲区长度为8192
,故接收消息的长度最大也为8192
,但当长度等于8192
时,消息占据了最后一位空字符位,即长度超限,会产生缓冲区溢出错误),则不进行词法分析,直接返回400错误。
三、协议实现
1. 响应生成模块
前面提到过,在此模块中需要对GET
请求和HEAD
请求生成对应的响应,而POST
响应的生成与Week 1中已实现的一致,因此无需再作更改。同时,对于HEAD
请求,其整体处理流程和GET
请求的一致,只需在最终生成响应时去掉拼接文件实体的部分即可。因此下面只给出对GET
请求生成响应的详细实现过程。
URL解析部分
首先,需要定义函数来对请求中的URL进行解析,以生成文件路径。请求文件的URL包括两部分,第一部分是服务器的根目录(在此实验中为./static_site
),第二部分为请求行中提供的uri
。当uri
为/
时,使用默认文件路径(/index.xml
)与根目录拼接得到URL
,否则使用请求给出的uri
(request->http_uri
)与根目录拼接生成URL
,解析URL
成功后应返回HTTP_SUCC
。而在生成时,还应注意URL
的长度不得超限,若超限则应返回HTTP_FAIL
。
根据以上描述,可以得到URL
解析函数的伪代码如下。
1 | root = "./static_site" |
响应生成部分
随后,根据解析出的URL,可以开始获取对应文件并生成响应。此处的设计流程在协议设计部分已经有了详细的说明,也给出了清晰的流程图,故不再赘述,只给出一些实现时需要掌握的框架代码细节。
1. 使用stat
结构变量存储文件信息;
2. 文件属性st_mode
和文件内容长度st_size
均应从stat
结构中获取;
3. 使用open
函数根据URL
打开文件;
4. 使用read
函数从文件中读取内容至缓冲区;
5. 若响应生成成功,应返回HTTP_SUCC
,若在上述任一步骤中出错,应返回HTTP_FAIL
。
由此,可以顺利得到GET
请求的响应生成函数伪代码如下。
1 | _200 = "HTTP/1.1 200 OK\r\n" |
根据以上伪代码,就可以顺利的实现HTTP GET响应的生成,而稍加修改后就能实现HTTP HEAD响应的生成。
2. 服务器响应模块
对于服务器响应模块,只需将Week 1已实现的模块进行更改,将GET
、HEAD
、POST
三种请求分开响应,并额外增加对505错误和404错误的判断和响应即可。
1. 对于三种不同请求的响应,在Week 1中已经阐述了如何判断,只需将其拆分为单独判断即可;
2. 对于505错误的判断,只需在解析后得到的HTTP版本号(存储在request->http_vision
中)与支持版本号(HTTP/1.1
)进行匹配,若匹配失败则产生505错误,响应对应的错误信息;
3. 而对于404错误的判断则较为简单,如果在调用GET
响应生成函数或HEAD
响应生成函数时返回了HTTP_FAIL
,则产生404错误,响应对应的错误信息。
由此,可以绘制服务器响应模块的响应结构图如下。
根据结构图,就能顺利的完成对服务器响应模块的修改和实现。
3. 日志记录模块
对于日志记录模块,前面的设计已经较为详尽,在此补充一些实现细节。
1. 使用fprintf()函数可以向日志文件中写入数据,而在每次写入后,还应调用fflush()函数来刷新缓冲区,以保证写入内容的正确性;
2. 对于Access Log
记录函数,其请求方法,URI和HTTP版本等信息,应使用Request
结构体中的http_method
、http_uri
、http_version
来生成,而文件大小的部分则应使用stat
结构中的file_size
来生成;
3. 对于Error Log
记录函数,则只需传入对应的ERROR_TYPE
,便可以完成日志记录的生成。
由此,可以得到两种日志记录的伪代码如下。
1 | ok_recored(request, file_stat) |
随后,只需在正确的地方调用以上两种日志记录函数便可以完成日志的记录工作。
4. 缓冲区管理
前面提到过,如果在接收HTTP请求消息后发现请求头部过长(超过8192
字节),则不对其进行词法分析,直接返回400错误。因此,应在接收消息后、词法分析前对消息的长度进行判断,并决定是否响应400错误。
在框架代码中,readret
用于存储读取到的文件长度,而BUF_SIZE
则存储了缓冲区的最大长度(8192
),因此容易写出此部分的伪代码如下。
1 | WHILE(...) // 框架函数中接收消息的循环 |
由此,就能成功对长度超限的请求报错,以避免出现缓冲区溢出错误。
结果分析
根据功能要求,分别需要对GET
、HEAD
、POST
请求进行测试,对400
、404
、501
、505
四种错误进行测试,以及对因请求头部过长而产生的400
错误进行测试,并查看是否正确生成了相应的日志记录。
各部分测试结果如下:
GET/HEAD/POST请求
四种错误请求
请求头部超限的请求
日志记录
Autolab测试
实验总结
了解并实践了如何根据请求查找服务器中的文件,并按正确的格式为客户端发送响应,对网络通信有了更清晰的认识;同时,新增了对404和505两种错误类型的判断和响应,为服务器添加了更多功能;在响应过程中涉及到许多对缓冲区的操作,对缓冲区管理有了更好的掌握;新增了日志模块,了解了如何通过日志记录服务器所执行的各种操作,对分析程序行为有很大帮助。
- Title: Socket编程日志 Week2
- Author: 梦猫
- Created at : 2024-05-22 11:57:57
- Updated at : 2024-05-31 22:00:43
- Link: https://mengmaor.github.io/2024/05/22/Socket编程记录-Week2/
- License: All Rights Reserved © 梦猫