TCP Socket:如何在服务器上使用 FD_ISSET() 来检测客户端发送的数据?
TCP Socket: How to use FD_ISSET() on server to detect client's sending data?
我有一个服务器,它有几个套接字与对应的客户端连接,我想使用 select() 来检测客户端是否正在向对应的套接字发送数据。我的代码是:
fd_set player_fd_set;
FD_ZERO(&player_fd_set);
int max_fd = players[0].connected_socket_on_master;
for (int i = 0; i < num_players; i++) {
FD_SET(players[i].connected_socket_on_master, &player_fd_set);
if (players[i].connected_socket_on_master > max_fd) {
max_fd = players[i].connected_socket_on_master;
}
}
select(max_fd + 1, &player_fd_set, NULL, NULL, NULL);
for (int i = 0; i < num_players; i++) {
printf("Check fd # %d\n", i);
if (FD_ISSET(players[i].connected_socket_on_master, &player_fd_set)) {
printf("Coming from player # %d\n", i);
ssize_t recvStatus = recv(players[i].connected_socket_on_master,
potato.trace, sizeof(potato.trace), 0);
if (recvStatus == -1) {
printf("Error: Could not recv final potato from player # %d\n", i);
exit(EXIT_FAILURE);
}
break;
}
}
似乎 FD_ISSET() returns 在第一次进入 for 循环后立即执行。我从 Whosebug 中的其他问题中读到 select() 是电平触发而不是边缘触发,那么如何检测从套接字接收到的数据?
谢谢!
您的 fd_set
设置没问题,但您在进入读取循环之前没有检查 select()
的 return 值是否为 > 0
。
您的阅读循环在错误的地方调用了 break
,实际上它根本不应该调用 break
。一旦 any 客户端发送数据,您将退出循环,忽略 other 可能 also 的客户端已发送等待读取的数据。您需要遍历整个 fd_set
从处于可读状态的每个套接字读取。
如果 recv()
失败,您的阅读循环不应调用 exit()
。只是 close()
失败的套接字,从 players[]
中删除该套接字,然后继续下一个循环迭代。
如果 recv()
returns -1,请务必检查 errno
是否有 EWOULDBLOCK
和 EAGAIN
,因为它们不是致命错误。另外,如果 recv()
returns 0,则客户端已断开连接。
试试像这样的东西:
fd_set player_fd_set;
FD_ZERO(&player_fd_set);
int max_fd = players[0].connected_socket_on_master;
for (int i = 0; i < num_players; ++i)
{
FD_SET(players[i].connected_socket_on_master, &player_fd_set);
if (players[i].connected_socket_on_master > max_fd)
max_fd = players[i].connected_socket_on_master;
}
if (select(max_fd + 1, &player_fd_set, NULL, NULL, NULL) > 0)
{
int offset = 0;
for (int i = 0; i < num_players; ++i)
{
printf("Check fd # %d\n", i);
if (!FD_ISSET(players[offset].connected_socket_on_master, &player_fd_set))
continue;
printf("Coming from player # %d\n", i);
ssize_t recvStatus = recv(players[offset].connected_socket_on_master, potato.trace, sizeof(potato.trace), 0);
if (recvStatus > 0)
{
// process potato.trace up to recvStatus bytes as needed...
++offset;
continue;
}
if (recvStatus < 0)
{
if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
{
// no more data to read right now...
++offset;
continue;
}
printf("Error: Could not recv final potato from player # %d\n", i);
}
else
printf("Player # %d disconnected\n", i);
close(players[offset].connected_socket_on_master);
for(int j = i+1; j < num_players; ++j)
players[j-1] = players[j];
--num_players;
}
}
我有一个服务器,它有几个套接字与对应的客户端连接,我想使用 select() 来检测客户端是否正在向对应的套接字发送数据。我的代码是:
fd_set player_fd_set;
FD_ZERO(&player_fd_set);
int max_fd = players[0].connected_socket_on_master;
for (int i = 0; i < num_players; i++) {
FD_SET(players[i].connected_socket_on_master, &player_fd_set);
if (players[i].connected_socket_on_master > max_fd) {
max_fd = players[i].connected_socket_on_master;
}
}
select(max_fd + 1, &player_fd_set, NULL, NULL, NULL);
for (int i = 0; i < num_players; i++) {
printf("Check fd # %d\n", i);
if (FD_ISSET(players[i].connected_socket_on_master, &player_fd_set)) {
printf("Coming from player # %d\n", i);
ssize_t recvStatus = recv(players[i].connected_socket_on_master,
potato.trace, sizeof(potato.trace), 0);
if (recvStatus == -1) {
printf("Error: Could not recv final potato from player # %d\n", i);
exit(EXIT_FAILURE);
}
break;
}
}
似乎 FD_ISSET() returns 在第一次进入 for 循环后立即执行。我从 Whosebug 中的其他问题中读到 select() 是电平触发而不是边缘触发,那么如何检测从套接字接收到的数据?
谢谢!
您的 fd_set
设置没问题,但您在进入读取循环之前没有检查 select()
的 return 值是否为 > 0
。
您的阅读循环在错误的地方调用了 break
,实际上它根本不应该调用 break
。一旦 any 客户端发送数据,您将退出循环,忽略 other 可能 also 的客户端已发送等待读取的数据。您需要遍历整个 fd_set
从处于可读状态的每个套接字读取。
如果 recv()
失败,您的阅读循环不应调用 exit()
。只是 close()
失败的套接字,从 players[]
中删除该套接字,然后继续下一个循环迭代。
如果 recv()
returns -1,请务必检查 errno
是否有 EWOULDBLOCK
和 EAGAIN
,因为它们不是致命错误。另外,如果 recv()
returns 0,则客户端已断开连接。
试试像这样的东西:
fd_set player_fd_set;
FD_ZERO(&player_fd_set);
int max_fd = players[0].connected_socket_on_master;
for (int i = 0; i < num_players; ++i)
{
FD_SET(players[i].connected_socket_on_master, &player_fd_set);
if (players[i].connected_socket_on_master > max_fd)
max_fd = players[i].connected_socket_on_master;
}
if (select(max_fd + 1, &player_fd_set, NULL, NULL, NULL) > 0)
{
int offset = 0;
for (int i = 0; i < num_players; ++i)
{
printf("Check fd # %d\n", i);
if (!FD_ISSET(players[offset].connected_socket_on_master, &player_fd_set))
continue;
printf("Coming from player # %d\n", i);
ssize_t recvStatus = recv(players[offset].connected_socket_on_master, potato.trace, sizeof(potato.trace), 0);
if (recvStatus > 0)
{
// process potato.trace up to recvStatus bytes as needed...
++offset;
continue;
}
if (recvStatus < 0)
{
if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
{
// no more data to read right now...
++offset;
continue;
}
printf("Error: Could not recv final potato from player # %d\n", i);
}
else
printf("Player # %d disconnected\n", i);
close(players[offset].connected_socket_on_master);
for(int j = i+1; j < num_players; ++j)
players[j-1] = players[j];
--num_players;
}
}