我必须为我的 IT 学校制作一个简单的 IRC 客户端/服务器程序。主题要求我们使用
select(2)
进行套接字轮询,但禁止我们使用 O_NONBLOCK
套接字。
- 您的服务器将接受多个同时连接。
注意,禁止使用。所以你应该强制使用fork
select
- 您的服务器不得阻塞。
这与非阻塞套接字无关,非阻塞套接字是被禁止的(所以不要使用)fcntl(s, O_NONBLOCK)
我想知道是否有可能设计一个带有阻塞套接字的非阻塞服务器(不分叉),即使使用
select(2)
。
这是一个简单的例子:假设我们有一个简单的文本协议,每行一个命令。每个客户端都有一个缓冲区。当
select(2)
告诉我们客户端已准备好 read(2)
时,我们会读取,直到在客户端缓冲区中找到 \n
,因此我们处理该命令。对于非阻塞套接字,我们会一直读到EAGAIN
。
现在假设我们正在使用阻塞套接字,并且恶意客户端发送没有换行符的文本。
select(2)
告诉我们数据可用,然后我们在客户端上read(2)
。但我们永远不会读到预期的\n
。系统调用将无限期地阻塞,而不是返回 EAGAIN
。这是拒绝服务攻击。
真的有可能设计一个带有阻塞套接字和
select(2)
(没有fork(2)
)的非阻塞服务器吗?
是的,您从套接字中读取一次,
select
告诉您已准备好。如果 read
包含 \n
,则处理该行。否则,存储收到的所有数据,并立即返回到 select
。
这当然意味着对于每个打开的套接字,您必须维护状态信息以及迄今为止读取的数据缓冲区。这允许代码独立处理每个
read
,无需在返回到 select
之前完成整行。
这是不可能的。
select()
会阻塞,因此任何调用它的程序也会阻塞。 Posix 在阻塞模式下为
send()
定义的行为是,它会阻塞,直到提供的所有数据都传输到套接字发送缓冲区。除非您要深入研究低水位线等问题,否则不可能提前知道套接字发送缓冲区中是否有足够的空间供任何给定的 send()
在不阻塞的情况下完成,因此任何程序都不可能这要求 send()
不要阻塞。
请注意,
select()
对此没有帮助。它可以告诉您何时有一些空间,但不能告诉您何时有足够。