2017-06-19 173 views
-1

在我的TCP服务器,我想连接(活动)插座有:非阻塞PASV袜子和阻塞

  1. 非阻塞被动插座有无阻塞接受();

  2. 接受连接后,我想喜欢核实客户提供的ID和密码进行一些认证。所以我有明确的协议,我想通过连接套接字阻止TCP服务器和客户端之间的recv()/ send()对话。

  3. 客户端身份验证后,我想有无阻塞连接插座,以便从外部线程服务器关闭。

问题是,当我第一次设置非阻塞PASSIVE套接字然后接受CONNECTION套接字也是非阻塞?他们为什么不分开套接字?

fcntl(ps_fd, F_SETFL, O_NONBLOCK); 

我通过连接插座做认证:

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

但这里recv()不会阻止和客户不能提供

我已经使用这个代码被动套接字设置为非阻塞模式它是EAGAIN错误之前的身份验证标识和密码。

我可以恢复连接插座在阻断模式又和被动插座左无阻塞?

+0

这听起来非常错误的...不要在一个客户端上阻止你的整个服务!相反,使用一些上下文和一个状态机。同时建议'选择()'(或什么特定平台的像'的epoll()'),而不是'O_NONBLOCK' –

+0

啊,但他们似乎从被动套接字继承状态,并不会阻止:( –

+1

所以把它们放到阻塞模式是什么阻止你? – EJP

回答

1

阻塞状态不是由Linux的接受的套接字继承。在BSD衍生的系统(如MacOS和可能的Windows(虽然我无法找到指定的任何东西))无阻塞被继承。

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

另一种可能的解决方案是使用线程,或甚至过程,以处理连接。

或者你可以使用一些投票站像selectpoll或者如果你在Linux上使用epoll(或相应的变种BSD系统),知道什么时候有被接受的套接字上接收到的数据。对于这个工作,你可以使用一个简单的状态机,在那里你有国家如WAITING_FOR_USERNAMEWAITING_FOR_PASSWORDLOGGED_IN

+0

一切都被继承。阻塞/非阻塞模式,接收缓冲区大小,... – EJP

+0

@EJP你说得对。该规范的实施BSD非可以阻断遗传。答案更新,保持可行的解决方案。 –

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