c - 非阻塞 pasv 袜子和阻塞连接(活动)插座



在我的TCP服务器上,我希望:

非阻塞
  1. 被动套接字具有非阻塞接受();

  2. 接受连接后,我想执行一些身份验证,例如验证客户提供的ID和密码。所以我有明确定义的协议,我希望通过连接套接字在 TCP 服务器和客户端之间阻止 recv()/send() 通信。

  3. 客户端身份验证后,我希望使用非阻塞连接套接字来启用从外部线程关闭服务器。

问题是,当我第一次设置非阻塞无源套接字时,接受的连接套接字也是非阻塞的吗?为什么它们不是单独的插座?

我使用以下代码将被动套接字设置为非阻塞模式:

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(尽管我找不到任何指定的内容)),非阻塞是继承的。

一种解决方案当然是再次使接受的套接字阻塞,然后在身份验证阶段完成后进行非阻塞。如果您是单线程的,这将阻止程序的其余部分,这意味着您无法在一个用户进行身份验证时接受其他连接。

另一种可能的解决方案是使用线程甚至进程来处理连接。

或者你可以使用一些轮询,如selectpoll或者如果你在Linux上使用epoll(或BSD系统上的相应变体)来知道何时在接受的套接字上接收数据。为此,您可以使用一个简单的状态,其中有WAITING_FOR_USERNAMEWAITING_FOR_PASSWORDLOGGED_IN等状态。

最新更新