tcp - TCP Socket:如何在服务器上使用 FD_ISSET() 来检测客户端的发送数据?
问题描述
我有一个服务器,它有几个与对应客户端连接的套接字,我想使用 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() 在第一次进入 for 循环后立即返回。我从 stackoverflow 中的其他问题中读到 select() 是电平触发而不是边缘触发,那么我如何检测从套接字接收的数据?
谢谢!
解决方案
您的fd_set
设置很好,但是在进入读取循环之前您没有检查select()
for的返回值。> 0
您的阅读循环在错误的地方调用,实际上它根本break
不应该调用。一旦任何客户端发送数据,您就会退出循环,忽略可能也发送了等待读取的数据的其他客户端。您需要从处于可读状态的每个套接字循环读取整个读数。break
fd_set
exit()
如果recv()
失败,您的阅读循环不应该调用。只是close()
失败的套接字,从 中删除该套接字players[]
,然后继续下一个循环迭代。
如果recv()
返回 -1,请务必检查errno
和EWOULDBLOCK
,EAGAIN
因为它们不是致命错误。此外,如果recv()
返回 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;
}
}
推荐阅读
- anylogic - 为不同的流程使用中央数据库并通过动态事件获取访问权限
- javascript - 如何在 react-native 的父组件内的特定位置显示多个组件?
- angular - material-ui, angular: 表中的表
- python - Selenium 动态抓取代码仅在 python 中运行多次时才有效
- firebase - 为什么 Crashlytics 会因未处理的异常进入无限循环?
- java - 为什么我的 Mono 应该被订阅,而另一种情况它运行正常
- java - 为什么集合不能使用时间戳正确排序对象?
- python-3.x - 使用 pycparser 获取枚举/结构的成员
- elasticsearch - 弹性搜索索引经常被删除
- laravel - Foreach 循环未在 Laravel 中运行