Socket编程日志 Week4

Socket编程日志 Week4

梦猫 Lv2

一、实验概要

第四周实验中,需要让服务器在等待一个客户端发送下一个请求时,能够同时处理来自其它客户端的请求,进而实现对多个并发的客户端的同时处理。
具体而言,并发处理需要通过select()方法实现,并支持最多1024个并发连接。
此外,如果某些客户端在通信过程中“暂停”,或是出现错误,服务器应不受该客户端影响,继续为其它客户端提供服务。

二、协议设计

数据结构设计

在本次实验中,要求使用select()方法实现并发处理。select是一个用于监听文件状态的函数,函数会阻塞进程并持续监听所有文件描述符编号小于其首个参数MAX_FD的文件描述符,直至某些文件描述符状态发生改变,例如某文件进入“读就绪”状态,函数便会返回就绪文件的数量,并将改变的文件描述符fd加入其第二个参数READ_FD_SET(可读文件描述符集合)。而在实验中,每个客户端和服务器的socket也是一个可读文件,因此通过select函数监听所有新增的可读文件便可以接收到所有发送请求的客户端socket。

同时,还需要了解fd_set结构体的组织方式和相关操作,以便后续的设计和实现。fd_set以位图的形式存储了一个文件描述符集合的某种状态,每一位均对应一个文件描述符,而该位的0/1值便对应着该文件描述符的特定状态情况。由此,只需不断通过select方法监听客户端和服务器socket的状态集合,再遍历集合来检查每个客户端socket是否活跃,即可得到所有与服务器建立并发连接的客户端socket的文件描述符。

此外,为了正确的与客户端建立连接并通信,还需要组织一个socket数组,用于储存所有经过前述步骤,与服务器建立连接的客户端socket。至此,就能顺利得到所有与服务器建立并发连接的客户端socket,进而可以对其请求进行处理和响应。

协议规则设计

在掌握了以上结构的定义后,便可以按如下步骤来对并发的客户端请求进行接收、管理和响应:

  1. 使用select方法监听客户端和服务器fd;
  2. 获取活跃fd,据此获得待连接客户端socket的fd,并加入已连接fd_set中;
  3. 根据已连接fd_set中的fd获取所有已连接客户端socket,并组织一个在socket集合中;
  4. 遍历socket集合,对每个客户端接收并响应一次请求,并将其移出连接集合;
  5. 循环执行上述步骤,不断连接并逐个响应所有发送请求的客户端,以此实现并发处理的效果。

根据以上设计,可以得到并发处理模块的处理流程图如下。

Multi-Client Answering
Multi-Client Answering

三、协议实现

为了实现前面的协议设计,还需要清楚如何正确的对fd_set进行管理。在本实验环境中,可以通过宏定义来进行,以下是此次实验中使用到的部分宏定义。

  1. FD_ZERO(&fd_set): 将集合中所有fd状态清零(设为不活跃);
  2. FD_SET(fd, &fd_set): 设置fd在集合中的状态为活跃;
  3. FD_CLR(fd, &fd_set): 将fd在集合中的状态清零;
  4. FD_ISSET(fd, &fd_set): 检查fd在集合中状态是否活跃,若活跃返回1,否则返回0。

此外,还需注意将socket集合大小设为1024,并在将已连接客户端socket加入socket集合时判断客户端数量是否超限,以满足实验对最大连接数为1024的要求。

由此,就可以根据流程图编写伪代码,并成功实现客户端并发处理模块。伪代码如下。

Multi-Client Answering
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
fd_set ready_set, pos_set
int client_set[1024]

CLEAR ready_set
CLEAR pos_set
ADD server_fd TO ready_set
SET ALL client_set[i] = -1

LOOP
COPY ready_set TO pos_set
CALL select, SET client_num, pos_set
IF client_num < 0
select ERROR
BREAK LOOP
ELSE IF client_num == 0
WAIT
CONTINUE LOOP
END IF

IF server_fd IS IN pos_set
THEN ACCEPT client_socket
FIND -1 IN client_set
ADD client_socket TO client_set
ADD client_fd TO ready_set
END IF

FOR EACH socket IN client_set
RECV REQUEST AND ANSWER
IF ANY ERROR
THEN CLOSE socket
CONTINUE
END IF
CLOSE socket
REMOVE fd FROM ready_set
REMOVE socket FROM client_set
END LOOP

结果分析

本次实验的本地测试和结果分析部分要求使用Apache Bench来进行性能测试,并同时对第三周的部分进行测试,通过比较分析本次实验实现模块的作用。

AB测试

根据实验要求,使用Apache Bench测试在1000个HTTP请求下第三和第四周程序的响应时间。同时,分别使用10,100,1000个客户端并行发送请求,以体现支持多客户端并行后的特点。

  1. 10个客户端并行

    10 Week 3
    10 Week 3

    10 Week 4
    10 Week 4

  2. 100个客户端并行

    100 Week 3
    100 Week 3

    100 Week 4
    100 Week 4

  3. 1000个客户端并行

    1000 Week 3
    1000 Week 3

    1000 Week 4
    1000 Week 4

性能分析

从响应时间可以发现,在整体请求数量为1000的情况下,支持多客户端并发处理的程序性能上的整体表现其实弱于仅支持pipeline的程序。这可能是由于在为每个客户端建立连接时需要大量循环扫描socket集合,以寻找空位存储该客户端socket。同时,并发处理模块对每个客户端都采用仅接收一次请求就关闭连接,等待后续有请求时再建立连接的连接方式,而非为每个客户端建立持久连接,如此频繁的建立和断开连接也会大量较大的时间开销。
但值得注意的是,随着并发客户端数量的上升,第四周程序和第三周程序间的差距在不断缩小,第四周的性能表现也逐渐变好。因此,在更大规模的HTTP请求和并发客户端(超过实验要求的1024个客户端连接)的情况下,可以推测支持并发处理模块的程序最终会体现出更好的性能,这也是为什么在实际应用场景(请求数量和客户端数量通常远大于实验测试的规模)中通常都会采用支持客户端并发处理的服务器设计。

Autolab测试

Autolab
Autolab

实验总结

了解并初步掌握了select方法;对fd和fd_set有了更好的掌握,并更深入的理解了socket的结构和处理方式;了解并实践了如何接收并存储多个客户端socket连接,并据此实现了对客户端请求的并发处理;通过测试和分析明白了客户端并行处理模块对服务器性能带来的影响,以及并发处理技术和pipeline技术各自的适用场景。

  • Title: Socket编程日志 Week4
  • Author: 梦猫
  • Created at : 2024-06-03 17:32:32
  • Updated at : 2024-06-10 16:58:29
  • Link: https://mengmaor.github.io/2024/06/03/Socket编程记录-Week4/
  • License: All Rights Reserved © 梦猫