关于 select() 的困惑

问题描述 投票:0回答:1

我已经忙了两天了,还是没明白。 这段代码中

select()
做了什么?

我知道,如果有一个可以接受的传入连接,

copy.fd_array[]
将包含
ListenSocket
,但是当 while 循环重复时它仍然存在。那么我们如何知道客户端是否断开连接呢?
fd_set copy
调用后
select()
包含什么?

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);
        }
    }
}
c sockets posix-select
1个回答
1
投票

我忙了两天了,还是没明白。

难怪你看不懂代码:示例中的代码是废话。

检查

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
        {
            ...
        }
    }
}

我知道,如果有可以接受的传入连接,

copy.fd_array[]
将包含
ListenSocket
,但当
while
循环重复时,它仍然存在。
select() 调用后 fd_set 副本包含什么?

首先:在调用

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()
检查某个句柄是否仍在数组中。

那么我们如何知道客户端是否断开连接呢?

当您检测到

FD_ISSET(i, &copy)
中存在由
i
返回的值
accept()
时,您必须调用
recv()
(在 Linux 下
read()
也可以工作):

如果

recv()
返回 0(如果出现错误则返回负值),则另一台计算机已断开连接。您必须调用
close()
(在 Windows 上为
closesocket()
)并从
copy.fd_array[]
中删除句柄(这意味着:由于
master.fd_array[]
行,您必须从
fd_set copy = master;
中删除它)。

如果

recv()
返回正值,则这是已接收的字节数。

© www.soinside.com 2019 - 2024. All rights reserved.