为什么尝试读取和写入套接字时选择行为不同?

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

假设我们有一个通过accept()接受的客户端文件描述符

client_socket = accept(_socket, (sockaddr *)&client_addr, &len)

我们现在在读写 fd_set 中设置此文件描述符:

fd_set readfds;
fd_set writefds;

//zero them
FD_ZERO(readfds);
FD_ZERO(writefds);

//set the client_socket
FD_SET(client_socket, &readfds);
FD_SET(client_socket, &writefds);

现在我们使用 select 来检查套接字是否可读或可写:

select(FD_SETSIZE, &readfs, &writefds, NULL, NULL)

我们现在检查是否可以先读取并从中读取所有字节。

if (FD_ISSET(client_socket, &readfds) {
    read(client_socket, &buf, 4096);
}
//assume that buf is big enough and that read returns less than 4096

在下一个循环中,我们像以前一样重置 fd_sets。 现在选择将允许我们向客户写下我们的回复:

if (FD_ISSET(client_socket, &readfds) {
    write(client_socket, &buf, len(buf));
}

到目前为止,一切正常,但现在出现了奇怪的行为。 让我们假设我们的客户告诉我们保持连接处于活动状态,因为 如果我们会像以前一样设置 fd_set ,如下所示:

//zero them
FD_ZERO(readfds);
FD_ZERO(writefds);

//set the client_socket
FD_SET(client_socket, &readfds);
FD_SET(client_socket, &writefds);
// reading not allowed

现在使用 select 时,它将允许我们再次写入,但不允许从 client_socket 读取。 但是如果我们将 writefds 的设置更改为零,它将允许我们读取,尽管我们没有更改 readfds 中的任何内容。

//zero them
FD_ZERO(readfds);
FD_ZERO(writefds);

//set the client_socket
FD_SET(client_socket, &readfds);
//FD_SET(client_socket, &writefds); -> don't set the file-descriptors for write
// now reading is allowed

有人可以解释一下这是否是 select 的正确行为,或者如果这是我的错,可能是在我没有显示的代码的其他部分(太复杂了)。 对我来说,在设置两组(写入和读取)时, select 的行为似乎是随机的。 我知道有一种方法可以解决这个问题,通过保持一种状态来决定我们是否要设置读取文件描述符或写入文件描述符,但我希望有一个更干净的解决方案。

c++ c http sockets posix-select
1个回答
2
投票

select()
的目的是在你的程序有事情要做之前才返回。这样你的程序就可以在内部休眠,直到 I/O 准备好,立即唤醒执行 I/O,然后尽快返回休眠状态。
所以问题是,

select()

如何知道何时返回?答案是,您必须通过以适当的方式调用

select()
来告诉它什么会导致它返回。
通常,当任何套接字上的数据准备好读取时,您会希望 

FD_SET()

返回(以便您可以读取新到达的数据),因此您通常应该在所有套接字上调用

select()

FD_SET(mySock, &readFD)

有点微妙。它告诉

FD_SET(mySock, &writeFD)
当套接字有可用于写入输出字节的缓冲区空间时返回。然而,在许多情况下,当套接字有可用缓冲区空间时,您不希望返回,因为您当前没有任何要写入套接字的数据。在这种情况下,如果你总是调用
select()
那么 select() 将立即返回,即使你没有任何想要执行的任务,这将导致你的程序无用地使用大量 CPU 周期.
因此,您应该调用 
FD_SET(mySocket, &writeFD)
的唯一时间是当您知道要尽快向该套接字写入一些数据时。
在您的程序中,可能发生的情况是 

select()

导致

FD_SET(mySocket, &writeFD)
立即返回(因为

FD_SET(mySocket, &writeFD)

当前有可供写入的缓冲区空间),然后您的程序(错误地)假设因为

 select()
已返回,套接字已准备好读取,却发现尚未准备好。如果您注释掉了
mySocket
,OTOH,
select()
在套接字准备好读取之前不会返回,因此当您调用
FD_SET(mySocket, &writeFD)
时,您会得到预期的行为。
    

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