首页 > 解决方案 > 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() 是电平触发而不是边缘触发,那么我如何检测从套接字接收的数据?

谢谢!

标签: tcpunix-socket

解决方案


您的fd_set设置很好,但是在进入读取循环之前您没有检查select()for的返回值。> 0

您的阅读循环在错误的地方调用,实际上它根本break不应该调用。一旦任何客户端发送数据,您就会退出循环,忽略可能发送了等待读取的数据的其他客户端。您需要从处于可读状态的每个套接字循环读取整个读数。break fd_set

exit()如果recv()失败,您的阅读循环不应该调用。只是close()失败的套接字,从 中删除该套接字players[],然后继续下一个循环迭代。

如果recv()返回 -1,请务必检查errnoEWOULDBLOCKEAGAIN因为它们不是致命错误。此外,如果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;
    }
}

推荐阅读