TCP Socket: как использовать FD_ISSET () на сервере для обнаружения данных, отправляемых клиентом? - PullRequest
0 голосов
/ 20 февраля 2019

У меня есть сервер, на котором есть несколько сокетов, связанных с другими клиентами, и я хочу использовать 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 () запускается по уровню, а не по фронту, тогда как я могу обнаружить данные, полученные из сокета?

Спасибо!

1 Ответ

0 голосов
/ 20 февраля 2019

Ваша fd_set установка в порядке, но вы не проверяете возвращаемое значение select() для > 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;
    }
}
...