22

我想创建一个非阻塞连接。像这样:

socket.connect(); // returns immediately

为此,我使用了另一个线程、无限循环和 Linux epoll。像这样(伪代码):

// in another thread
{
  create_non_block_socket();
  connect();

  epoll_create();
  epoll_ctl(); // subscribe socket to all events
  while (true)
  {
    epoll_wait(); // wait a small time(~100 ms)
    check_socket(); // check on EPOLLOUT event
  }
}

如果我运行服务器,然后运行客户端,则一切正常。如果我首先运行一个客户端,稍等片刻,运行一个服务器,然后客户端就无法连接。

我究竟做错了什么?也许它可以做得不同?

4

3 回答 3

46

您应该使用以下步骤进行异步连接:

  • 创建套接字socket(..., SOCK_NONBLOCK, ...)
  • 开始连接connect(fd, ...)
  • 如果返回值既不是0也不是EINPROGRESS,则错误中止
  • 等待直到fd发出信号准备好输出
  • 检查套接字的状态getsockopt(fd, SOL_SOCKET, SO_ERROR, ...)
  • 完毕

没有循环 - 除非你想处理EINTR.

如果首先启动客户端,您应该会ECONNREFUSED在最后一步看到错误。如果发生这种情况,请关闭套接字并从头开始。

如果不查看更多详细信息,很难判断您的代码有什么问题。我想,您不会因check_socket操作中的错误而中止。

于 2013-07-21T08:39:27.473 回答
7

有几种方法可以测试非阻塞连接是否成功。

  1. 首先调用 getpeername(),如果它失败并出现错误 ENOTCONN,则连接失败。然后使用 SO_ERROR 调用 getsockopt 以获取套接字上的未决错误
  2. 调用read,长度为0。如果读取失败,则连接失败,read的errno表示连接失败的原因;如果连接成功,read 返回 0
  3. 再次调用connect;如果 errno 是 EISCONN,则连接已经连接并且第一次连接成功。

参考:UNIX 网络编程 V1

于 2019-04-12T09:40:11.270 回答
2

DJ Bernstein 收集了各种方法来检查异步connect()调用是否成功。其中许多方法在某些系统上确实存在缺陷,因此为此编写可移植代码是出乎意料的困难。如果有人想阅读所有可能的方法及其缺点,请查看此文档

对于那些只想要 tl;dr 版本的人来说,最便携的方法如下:

一旦系统向套接字发出可写的信号,首先调用getpeername()它以查看它是否连接。如果该调用成功,则套接字已连接,您可以开始使用它。如果该调用失败并显示ENOTCONN,则连接失败。要找出它失败的原因,请尝试从 socket 读取一个字节read(fd, &ch, 1),这也会失败,但你得到的错误是connect()如果它不是非阻塞的你会得到的错误。

于 2020-07-14T09:24:15.497 回答