套接字编程中使用非阻塞版本的connect()调用时使用的回调机制是什么?
What is the callback mechanism used when non-blocking version of connect() call is used in socket programming?
在套接字编程中,假设服务器正在侦听特定端口上的 TCP 连接。
现在,在客户端,我创建了一个 socket 并调用 connect() 与服务器建立连接。注意:connect() API 是在非阻塞模式下调用的。
因为它是一个非阻塞调用,并且在调用 connect() API 时没有传递回调方法以在事件完成时得到通知。所以,我想知道 客户端如何 知道 TCP 连接何时成功建立。以便它可以启动数据传输?
问题的第二部分 - WHEN。基本上,要建立 TCP 连接,应该有 3 次握手,如下所示-
我假设,当从客户端调用 connect() API 时,SYNC 数据包从客户端和连接建立过程被启动。由于 connect() API 是在非阻塞模式下调用的,它只是通过请求内核和 returns 返回函数调用来启动连接。一旦成功建立连接,内核必须通知客户端说——可以开始传输数据了。我在这里的困惑是,最后一个阶段是在服务器端完成的 3 次握手(在服务器端到达 ACK 数据包之后),那么客户端的内核如何知道连接过程完成了吗?
或者是内核在收到来自Server[=的SYNC+ACK后,会立即通知客户端进程建立连接35=] 进程?
当使用非阻塞套接字时,connect()
通常会return EINPROGRESS。
在这种情况下,您可以使用select()
函数来等待连接建立:
将套接字设置为 select()
调用的写入集。
当连接为 established/failed 时,select() 将 return 并且 write-set 表明您的套接字是可写的。然后你可以调用 getsockopt()
来获取非阻塞连接的结果:
if (getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, &len) != -1)
...
当客户端收到 SYN-ACK 时,阻止 TCP connect() returns。
与非阻塞 TCP 套接字类似的方式:select() returns 当收到 SYN-ACK 时:
为了看得更清楚,图片有一点不准确。我试图通过在 select 调用之后放置 SYN 并在 select return 之后放置 ACK 来说明网络的缓慢。
收到 SYN-ACK 后,客户端的 TCP 状态更改为 ESTABLISHED。当收到(SYN-ACK 的)ACK 时,服务器的 TCP 状态更改为 ESTABLISHED。因此客户端应用程序可以在服务器从 accept()
调用 returned 之前开始向服务器发送数据。也有可能 ACK(和重试)在网络中丢失,服务器永远不会进入 ESTABLISHED 状态。
没有回调机制。在某些 API 中,回调机制与 异步 I/O 相关联。不是非阻塞 I/O。不,它们不是一回事。
当非阻塞 connect()
没有立即完成时,通常不会,否则有什么意义,它 returns -1 与 errno
设置至 EINPROGRESS
。然后,您应该 select()
或 poll()
或 epoll()
可写性的套接字,等等,如 man connect 中所述。重申一下,这不是回调机制。它实际上是一种轮询机制。
在套接字编程中,假设服务器正在侦听特定端口上的 TCP 连接。 现在,在客户端,我创建了一个 socket 并调用 connect() 与服务器建立连接。注意:connect() API 是在非阻塞模式下调用的。
因为它是一个非阻塞调用,并且在调用 connect() API 时没有传递回调方法以在事件完成时得到通知。所以,我想知道 客户端如何 知道 TCP 连接何时成功建立。以便它可以启动数据传输?
问题的第二部分 - WHEN。基本上,要建立 TCP 连接,应该有 3 次握手,如下所示-
我假设,当从客户端调用 connect() API 时,SYNC 数据包从客户端和连接建立过程被启动。由于 connect() API 是在非阻塞模式下调用的,它只是通过请求内核和 returns 返回函数调用来启动连接。一旦成功建立连接,内核必须通知客户端说——可以开始传输数据了。我在这里的困惑是,最后一个阶段是在服务器端完成的 3 次握手(在服务器端到达 ACK 数据包之后),那么客户端的内核如何知道连接过程完成了吗?
或者是内核在收到来自Server[=的SYNC+ACK后,会立即通知客户端进程建立连接35=] 进程?
当使用非阻塞套接字时,connect()
通常会return EINPROGRESS。
在这种情况下,您可以使用select()
函数来等待连接建立:
将套接字设置为 select()
调用的写入集。
当连接为 established/failed 时,select() 将 return 并且 write-set 表明您的套接字是可写的。然后你可以调用 getsockopt()
来获取非阻塞连接的结果:
if (getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, &len) != -1)
...
当客户端收到 SYN-ACK 时,阻止 TCP connect() returns。 与非阻塞 TCP 套接字类似的方式:select() returns 当收到 SYN-ACK 时:
为了看得更清楚,图片有一点不准确。我试图通过在 select 调用之后放置 SYN 并在 select return 之后放置 ACK 来说明网络的缓慢。
收到 SYN-ACK 后,客户端的 TCP 状态更改为 ESTABLISHED。当收到(SYN-ACK 的)ACK 时,服务器的 TCP 状态更改为 ESTABLISHED。因此客户端应用程序可以在服务器从 accept()
调用 returned 之前开始向服务器发送数据。也有可能 ACK(和重试)在网络中丢失,服务器永远不会进入 ESTABLISHED 状态。
没有回调机制。在某些 API 中,回调机制与 异步 I/O 相关联。不是非阻塞 I/O。不,它们不是一回事。
当非阻塞 connect()
没有立即完成时,通常不会,否则有什么意义,它 returns -1 与 errno
设置至 EINPROGRESS
。然后,您应该 select()
或 poll()
或 epoll()
可写性的套接字,等等,如 man connect 中所述。重申一下,这不是回调机制。它实际上是一种轮询机制。