为什么在尝试读取和写入套接字时,选择行为会有所不同



假设我们有一个用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()的目的是在程序有事情要做之前不返回。这样,您的程序可以在select()内休眠,直到I/O准备就绪,然后立即醒来进行I/O,然后尽快返回休眠状态。

所以问题是,select()如何知道何时返回?答案是,您必须通过以适当的方式调用FD_SET()来告诉它应该是什么导致它返回。

通常,您希望select()在任何套接字上的数据准备好读取时返回(这样您就可以读取新到达的数据(,因此您通常应该在所有套接字上调用FD_SET(mySock, &readFD)

FD_SET(mySock, &writeFD)有点微妙。它告诉select()在套接字具有可用于写入输出字节的缓冲区空间时返回。但是,在许多情况下,不希望select()在套接字具有可用缓冲区空间后返回,原因很简单,因为您当前没有任何要写入套接字的数据。在这种情况下,如果您总是调用FD_SET(mySocket, &writeFD),那么即使您没有任何要执行的任务,select()也会立即返回,这将导致您的程序毫无用处地占用大量CPU周期。

因此,您应该调用FD_SET(mySocket, &writeFD)的唯一时间是,如果您知道要尽快向该套接字写入一些数据。

在您的程序中,可能发生的情况是FD_SET(mySocket, &writeFD)导致select()立即返回(因为mySocket当前有可写入的缓冲区空间(,然后您的程序(错误地(假设由于select()已返回,套接字已准备好读取,但却发现它不是。在您注释掉FD_SET(mySocket, &writeFD)的情况下,OTOH、select()在套接字准备好读取之前不会返回,因此您在调用FD_ISSET(mySocket, &readFD)时会得到预期的行为。

最新更新