在套接字编程中,我们说服务器正在侦听特定端口上的TCP连接。 现在,在客户端上,我创建一个 socket 并调用 connect()与服务器建立连接。注意: connect() API是在非阻塞模式下调用的。
由于这是一个非阻塞调用,因此在调用 connect() API时不会传递任何回调方法,以便在事件完成时得到通知。因此,我想知道客户端如何知道TCP连接成功建立的时间。以便它可以启动数据传输?
问题的第二部分-何时。基本上,对于要建立的TCP连接,应该发生以下三种方式的握手-
我假设,当从客户端调用 connect() API时,正在从客户端发送 SYNC 数据包并启动连接建立过程。由于 connect() API是在非阻塞模式下调用的,因此它只是通过请求内核来启动连接并返回函数调用。一旦成功建立连接,内核必须通知客户说-进行数据传输是一件好事。我的困惑是,最后一个阶段是服务器端完成握手的3种方式(在服务器上到达 ACK 数据包之后),因此客户端内核如何知道连接过程完成了吗?
或者就像内核一旦收到来自 Server 进程的 SYNC + ACK 一样,就会通知客户端进程连接的建立?
答案 0 :(得分:4)
没有回调机制。在某些API中,回调机制与异步 I / O相关联。不能与非阻塞I / O一起使用。不,他们不是同一回事。
当非阻塞connect()
不能像通常那样不立即完成时,否则会是关键所在,它将返回-1,并且errno
设置为EINPROGRESS
。然后,应使用select()
或poll()
或epoll()
套接字以提高可写性,诸如man connect中所述。重申一下,这不是回调机制。实际上,这是一种轮询机制。
答案 1 :(得分:3)
使用非阻塞套接字时,connect()
通常将返回EINPROGRESS。
在这种情况下,可以使用select()
函数来等待连接建立:
将套接字设置为select()
调用的写集。
建立/失败连接后,select()将返回,并且写集指示您的套接字可写。然后,您可以调用getsockopt()
以获得非阻塞连接的结果:
if (getsockopt(socket, SOL_SOCKET, SO_ERROR, &error, &len) != -1)
...
当收到客户端SYN-ACK时,阻塞TCP connect()返回。 使用非阻塞TCP套接字的类似方法:收到SYN-ACK时,select()返回:
图片中的一点点误差使它更加清晰。我试图通过在选择调用之后放置SYN,在选择返回之后放置ACK来说明网络的速度。
当接收到SYN-ACK时,客户端的TCP状态更改为ESTABLISHED。收到(SYN-ACK的)ACK时,服务器的TCP状态更改为ESTABLISHED。因此,客户端应用程序可以在从accept()
调用返回服务器之前开始向服务器发送数据。 ACK(和重试)也有可能在网络中丢失,并且服务器从不进入ESTABLISHED状态。