在我的TCP服务器上,我希望:
非阻塞被动套接字具有非阻塞接受();
接受连接后,我想执行一些身份验证,例如验证客户提供的ID和密码。所以我有明确定义的协议,我希望通过连接套接字在 TCP 服务器和客户端之间阻止 recv()/send() 通信。
客户端身份验证后,我希望使用非阻塞连接套接字来启用从外部线程关闭服务器。
问题是,当我第一次设置非阻塞无源套接字时,接受的连接套接字也是非阻塞的吗?为什么它们不是单独的插座?
我使用以下代码将被动套接字设置为非阻塞模式:
fcntl(ps_fd, F_SETFL, O_NONBLOCK);
我通过连接插座进行身份验证:
if((n_recv = recv(sock_fd, buf, sizeof(buf) - 1, 0)) <= 0) { ... }
但是在这里recv()
不会阻止,客户端无法在错误发生前EAGAIN
时间提供其身份验证ID和密码。
我可以将连接套接字恢复为再次处于阻塞模式,而被动套接字保持非阻塞状态吗?
我已经尝试过这样的解决方案,它似乎有效。
对我来说,在 OS X 连接套接字上从被动套接字上的 accept() 接收到的套接字继承了其非阻塞模式。因此,接受后,我将连接套接字模式更改为再次阻止一个。
示例代码
fcntl(ps_fd, F_SETFL, O_NONBLOCK);
int cs_fd = accept(ps_fd);
// revert connection socket to non-blocking
int opts = fcntl(cs_fd, F_GETFL);
opts = opts & (~O_NONBLOCK);
fcntl(cs_fd, F_SETFL, opts);
// then authentication via cs_fd
// after authentication change it to non-blocking again
fcntl(cs_fd, F_SETFL, O_NONBLOCK);
Linux 中接受的套接字不会继承阻塞状态。在BSD派生的系统上(如macOS和Windows(尽管我找不到任何指定的内容)),非阻塞是继承的。
一种解决方案当然是再次使接受的套接字阻塞,然后在身份验证阶段完成后进行非阻塞。如果您是单线程的,这将阻止程序的其余部分,这意味着您无法在一个用户进行身份验证时接受其他连接。
另一种可能的解决方案是使用线程甚至进程来处理连接。
或者你可以使用一些轮询,如select
,poll
或者如果你在Linux上使用epoll
(或BSD系统上的相应变体)来知道何时在接受的套接字上接收数据。为此,您可以使用一个简单的状态机,其中有WAITING_FOR_USERNAME
、WAITING_FOR_PASSWORD
和LOGGED_IN
等状态。