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

时间:2017-06-19 15:32:39

标签: c sockets tcp fcntl

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

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

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

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

  4. 问题是,当我第一次设置非阻塞PASSIVE套接字时,接受的CONNECTION套接字也是非阻塞的吗?他们为什么不分开插座?

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

    fcntl(ps_fd, F_SETFL, O_NONBLOCK);
    

    我通过连接套接字进行身份验证:

    if((n_recv = recv(sock_fd, buf, sizeof(buf) - 1, 0)) <= 0) { ... }
    

    但是这里recv()没有阻止,客户端无法在EAGAIN错误之前的时间传递它的身份验证ID和密码。

    我是否可以将连接套接字恢复为阻塞模式,被动套接字是否为非阻塞状态?

2 个答案:

答案 0 :(得分:1)

阻止状态不会被Linux中接受的套接字继承。在BSD派生的系统上(比如macOS和可能是Windows(虽然我找不到任何指定的东西))继承了非阻塞。

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

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

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

答案 1 :(得分:1)

我尝试过这样的解决方案,似乎有效。

对于我来说,在被动套接字上从accept()接收的OS X连接套接字继承了它的非阻塞模式。所以接受后我将连接套接字模式改为再次阻塞。

示例代码

    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);