对 select() 的困惑

Confusion about select()

忙了2天了还是没看懂。 select()在这段代码中做了什么?

我知道如果有一个可以接受的传入连接,copy.fd_array[] 将包含 ListenSocket 但是当 while 循环重复时它仍然存在。那么我们如何知道客户端是否断开连接呢? fd_set copyselect() 调用之后包含什么?

fd_set master;
FD_ZERO(&master);
FD_SET(ListenSocket, &master);

while (1)
{
    fd_set copy = master;

    select(FD_SETSIZE, &copy, NULL, NULL, NULL);

    for (int i = 0; i < FD_SETSIZE; i++)
    {    
        // If new connection
        if (FD_ISSET(ListenSocket, &copy)) 
        {
            printf("[+] New connection\n");
            // Accept connection
            SOCKET AcceptedClient = accept(ListenSocket, NULL, NULL);
            FD_SET(AcceptedClient, &master);

            // Send welcome message to client
            char buff[128] = "Hello Client!";
            send(AcceptedClient, buff, sizeof(buff), 0);
        }
    }
}

I'm busy with this for 2 days now and still don't understand it.

难怪看不懂代码:例子里的代码都是废话

检查 ListenSocket 应该在 for 循环之外完成。并且 FD_ISSET 还必须检查使用 accept.

接受的连接

while 循环中的正确代码如下所示:

fd_set copy = master;

select(FD_SETSIZE, &copy, NULL, NULL, NULL);

// If new connection
if (FD_ISSET(ListenSocket, &copy)) 
{
    ...
}

for (int i = 0; i < FD_SETSIZE; i++)
{    
    // If an existing connection has data
    // or the connection has been closed
    if ((i != ListenSocket) && FD_ISSET(i, &copy)) 
    {
        nBytes = recv(i, buffer, maxBytes, 0);
        // Connection dropped
        if(nBytes < 1)
        {
            close(i); // other OSs (Linux, MacOS ...)
            // closesocket(i); // Windows
            FD_CLR(i, &master);
        }
        // Data received
        else
        {
            ...
        }
    }
}

I know that if there is an incoming connection that can be accepted, the copy.fd_array[] will contain ListenSocket but when the while loop repeats it's still there.
What does fd_set copy contain after the select() call?

首先:在调用select()之前,copy.fd_array[]必须包含所有你感兴趣的套接字句柄。这意味着它必须包含ListenSocket所有 句柄由 accept().

返回

master.fd_array[] 包含所有这些句柄,因此 fd_set copy = master; 将确保 copy.fd_array[] 也包含所有这些句柄。

select()NULL 作为最后一个参数)将等待至少一个套接字变为“可用”。这意味着它将等到至少以下条件之一为真:

  • 使用 accept() 接受的连接已被对方关闭
  • 使用accept()接受的连接有可以接收的数据
  • 有一个可以接受的新连接 accept(ListenSocket...)

只要满足一个条件,select() 就会从 copy.fd_array[] 中删除 所有其他句柄:

    如果没有传入连接,
  • ListenSocket 将从copy.fd_array[] 中删除
  • 如果连接既没有关闭也没有新数据可用,则 accept() 返回的句柄将从该数组中删除

如果两个事件同时发生,copy.fd_array[]将包含多个句柄。

您使用 FD_ISSET() 检查一些句柄是否仍在数组中。

So how do we know if a client is disconnected?

当您检测到 FD_ISSET(i, &copy) 的值 i 已由 accept() 返回时,您必须调用 recv()(在 Linux read() 也可以):

如果recv() returns 0(或错误情况下为负),另一台计算机已断开连接。您必须调用 close()(Windows 上的 closesocket())并从 copy.fd_array[] 中删除句柄(这意味着:您必须从 master.fd_array[] 中删除它,因为行 fd_set copy = master;).

如果recv() returns 为正值,则这是已接收的字节数。