《TCP/IP 网络编程》第 12 章——I/O 复用(学习笔记)

代码链接

第 12 章 I/O 复用

本章讨论并发服务器的第二种实现方法——基于 I/O 复用的服务器构建。

12.1 基于 I/O 复用的服务器端

12.1.1 多进程服务器端的缺点和解决方法

为了构建并发服务器,只要有客户端连接请求就会创建新进程。这的确是实际操作中采用的一种方案,但并非十全十美,因为创建进程时需要付出极大代价。这需要大量的运算和内存空间,由于每个进程都具有独立的内存空间,所以相互间的数据交换也要求采用相对复杂的方法(IPC属于相对复杂的通信方法)

Q:那有何解决方案?能否在不创建进程的同时向多个客户端提供服务?

A:I/O 复用技术

12.1.2 理解复用

『复用』在电子及通信工程领域很常见,『在 1 个通信频道中传递多个数据(信号)的技术』

还有一堆解释略过。

12.1.3 复用技术在服务器端的应用

多进程服务器端模型是一个子进程服务一个客户端,I/O 复用服务器端模型是一个进程向多个客户端提供服务。

12.2 理解 select 函数并实现服务器端

运用 select 函数是最具代表性的实现复用服务器端方法。Windows 平台下也有同名函数提供相同功能,因此具有良好的移植性。

12.2.1 select 函数的功能和调用顺序

使用 select 函数时可以将多个文件描述符集中到一起统一监视

  • 是否存在套接字接收数据?
  • 无需阻塞传输数据的套接字有哪些?
  • 哪些套接字发生了异常?

上述监视项称为『事件』。发生监视项对应情况时,称『发生了事件』。

select 函数的使用方法与一般函数区别较大,更准确地说,它很难使用。接下来介绍 select 函数的调用方法和顺序。

步骤 1:设置文件描述符,指定监视范围,设置超时。

步骤 2:调用 select 函数

步骤 3:查看调用结果

这是调用 select 函数到获取结果所经过程,接下来按照上述顺序逐一讲解。

12.2.2 设置文件描述符

利用 select 函数可以同时监视多个文件描述符。当然,监视文件描述符可以视为监视套接字。此时首先需要将要监视的文件描述符集中到一起。集中时也要按照监视项(接收、传输、异常)进行区分,即按照上述 3 种监视项分成 3 类。

使用 fd_set 数组变量执行此项操作,该数组是存有 0 和 1 的位数组。

最左端的位表示文件描述符 0(所在位置)。如果该位设置为 1,则表示该文件描述符是监视对象。

fd_set 变量的操作是以位为单位进行的,这也意味着直接操作该变量会比较繁琐。可以通过宏完成。

  • FD_ZERO(fd_set *fdset):将 fd_set 变量的所有位初始化为 0
  • FD_SET(int fd, fd_set *fdset):在参数 fdset 指向的变量中注册文件描述符 fd 的信息(置 1)
  • FD_CLR(int fd, fd_set *fdset):从参数 fdset 指向的变量中清除文件描述符 fd 的信息(置 0)
  • FD_ISSET(int fd, fd_set *fdset):若参数 fdset 指向的变量中包含文件描述符 fd 的信息,则返回『真』

12.2.3 设置检查(监视)范围及超时

select 函数介绍

#include <sys/select.h>
#include <sys/time.h>
int select(int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
/*
成功时返回大于 0 的值,失败时返回-1
maxfd:监视对象文件描述符数量
readset:将所有关注『是否存在待读取数据』的文件描述符注册到 fd_set 型变量,并传递其地址值。
writeset:将所有关注『是否可传输无阻塞数据』的文件描述符注册到 fd_set 型变量,并传递其地址值。
exceptset:将所有关注『是否发生异常』的文件描述符注册到 fd_set 型变量,并传递其地址值。
timeout:调用 select 函数后,为防止陷入无限阻塞的状态,传递超时(time-out)信息
返回值:发生错误时返回 -1,超时返回时返回 0。因发生关注的事件返回时,返回大于 0 的值,该值是发生事件的文件描述符数。
*/

如上所述,select 函数用来验证 3 种监视项的变化情况。根据监视项声明 3 个 fd_set 型变量,分别向其注册文件描述符信息,并把变量的地址值传递到上述函数的第二到第四个参数。但在此之前(调用 select 函数之前)需要决定下面 2 件事

  • 文件描述符的监视(检查)范围
  • 如何设定 select 函数的超时事件

第一,文件描述符的监视范围与 select 函数的第一个参数有关。select 函数要求通过第一个参数传递监视对象文件描述符的数量。只需将最大的文件描述符值加 1 再传递到 select 函数即可。

第二,select 函数的超时时间与 select 函数的最后一个参数有关,其中 timeval 结构体定义如下。

struct timeval {
    long tv_sec; // seconds
    long tv_usec; // microseconds
};

如果不想设置超时,则传入 NULL 参数。

12.2.4 调用 select 函数后查看结果

select 函数的返回值,如果返回大于 0 的整数,说明相应数量的文件描述符发生变化。怎样获知哪些文件描述符发生变化?

select 函数调用完成后,fd_set 变量种将发生变化。原来为 1 的所有位均变为 0,但发生变化的文件描述符对应位除外。

12.2.5 函数调用示例

select 函数调用示例,代码参考 select.c 文件

运行结果

wzy@wzypc:~/TCP-IP-NetworkNote/chapter-12$ gcc select.c -o select.exe
wzy@wzypc:~/TCP-IP-NetworkNote/chapter-12$ ./select.exe
fuck
message from console: fuck
q  
message from console: q
Time-out!
Time-out!
^C

12.2.6 实现 I/O 复用服务器端

下面是基于 I/O 复用的回声服务器端。可以用其他版本的回声客户端配合,这里选用第四章的回声客户端。

代码参考 echo_selectserv.c 文件

运行结果

# 服务端结果,多个客户端可以进行并发
wzy@wzypc:~/TCP-IP-NetworkNote/chapter-12$ gcc echo_selectserv.c -o selserv.exe
wzy@wzypc:~/TCP-IP-NetworkNote/chapter-12$ ./selserv.exe 9190
Connected client: 6 
Connected client: 7 
closed client: 7 
closed client: 6 
^C

12.3 基于 Windows 的实现

12.4 习题

以下是我的理解,详细题目参照原书

  1. 请解释复用技术的通用含义,并说明何为 I/O 复用。

复用技术的通用含义:在 1 个通信频道中传递多个数据(信号)的技术。

I/O 复用:进程预先告诉内核需要监视的 I/O 条件,使得内核一旦发现进程指定的一个或多个 I/O 条件就绪,就通过进程处理,从而不会在单个 I/O 上阻塞。

  1. 多进程并发服务器的缺点有哪些?如何在 I/O 复用服务器端中弥补?

创建进程代价大,需要大量的运算和内存空间。I/O 复用服务器通过 select 函数同时监视多个文件描述符,实现多客户端的并发需求。

  1. 复用服务器端需要 select 函数。下列关于 select 函数使用方法的描述错误的是?

b,select 会更新 fd_set

c,这句话应该是想考验能不能并发。

  1. select 函数的观察对象中应包含服务器端套接字(监听套接字),那么应将其包含到哪一类监听对象集合?请说明原因。

包含到 readset 集合,因为监听套接字需要接收数据进行连接。

  1. 涉及 Windows

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值