在我的TCP服务器上,我希望:
非阻塞被动套接字具有非阻塞的accept();
接受连接后,我想执行一些身份验证,例如验证客户端提供的Id和密码。所以我有很好的协议,我希望通过连接套接字阻止TCP服务器和客户端之间的recv()/ send()通话。
在客户端身份验证后,我希望有非阻塞连接套接字,以便从外部线程启用服务器关闭。
问题是,当我第一次设置非阻塞PASSIVE套接字时,接受的CONNECTION套接字也是非阻塞的吗?他们为什么不分开插座?
我已使用此代码将被动套接字设置为非阻塞模式:
fcntl(ps_fd, F_SETFL, O_NONBLOCK);
我通过连接套接字进行身份验证:
if((n_recv = recv(sock_fd, buf, sizeof(buf) - 1, 0)) <= 0) { ... }
但是这里recv()
没有阻止,客户端无法在EAGAIN
错误之前的时间传递它的身份验证ID和密码。
我是否可以将连接套接字恢复为阻塞模式,被动套接字是否为非阻塞状态?
答案 0 :(得分:1)
阻止状态不会被Linux中接受的套接字继承。在BSD派生的系统上(比如macOS和可能是Windows(虽然我找不到任何指定的东西))继承了非阻塞。
一种解决方案当然是再次使接受的套接字阻塞,然后在完成认证阶段后进行非阻塞。如果您是单线程的话,这将阻止程序的其余部分,这意味着当一个用户进行身份验证时您无法接受其他连接。
另一种可能的解决方案是使用线程甚至进程来处理连接。
或您可以使用某些轮询,例如select
,poll
,或者如果您使用的是epoll
(或BSD系统上的相应变体)知道何时在接受的套接字上接收数据。为此,您可以使用简单的状态机,其中包含WAITING_FOR_USERNAME
,WAITING_FOR_PASSWORD
和LOGGED_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);