Мне трудно описать мой вопрос, хотя название выше, может быть, почему-то непонятно.
У меня есть обычный tcp-сервер, у которого есть сокет для прослушивания, и он может принимать до 32 клиентов, я создал сокеты для каждого клиента, и я использую select system call, чтобы контролировать, какой клиент может быть прочитан, следующий код является фрагментом моей программы,
где, rset
является переменной-членом моего класса, а его тип - fd_set
. Я обнулял его с помощью FD_ZERO
в конструкторе.
timeo
также является переменной-членом, тип - struct timeval
инициализированный 10 секундами.
this->sock
снова является переменной-членом, используемой для прослушивания и приема новых клиентов. Я уже вызвал FD_SET(this->sock, &rset)
.
print_trace
- это просто макрос, который печатает сообщение и добавляет '\n'.
while(1) {
int count = select(FD_SETSIZE, &rset, /*&wset*/ NULL, NULL, &timeo);
printf("%d fds\n", count);
if(count) {
if(FD_ISSET(this->sock, &rset)) {
// new connection comes and now this line will not blocked
if((csocks[sock_count] = accept(this->sock, NULL, NULL))) {
FD_SET(csocks[sock_count], &rset);
++sock_count;
}
} else {
print_trace("there are clients can be read.");
for(int i = 0 ; i < sock_count ; ++i) {
if(FD_ISSET(csocks[i], &rset)) {
char buffer[512] = {0};
recv(csocks[i], buffer, 512, 0);
printf("here client socket number: %d, i=%d, message: %s\n", csocks[i], i, buffer);
}
}
}
}
timeo.tv_sec = 10;
timeo.tv_usec = 0;
}
Я знаю, что я не повторно this->sock
используя FD_SET
, для выбора будет очищать все биты при тайм-ауте, но это не касается моей проблемы.
моя проблема в том, что когда я запускаю серверную программу, и через 10 секунд я запускаю клиентскую программу для подключения к этому серверу, select
возвращает 1 нормально, так что клиентский сокет будет создан и добавлен в rset
, а затем сервер переходит к следующему циклу, осторожно! сейчас, прямо сейчас, прекратить программу клиента сразу, не ждать, select
возвраты.
Хорошо, теперь проблема снова появится, программа сервера сохранит следующую информацию:
1 fds
there are clients can be read.
here client socket number: 6, i=2, message:
1 fds
there are clients can be read.
here client socket number: 6, i=2, message:
1 fds
there are clients can be read.
here client socket number: 6, i=2, message:
1 fds
there are clients can be read.
here client socket number: 6, i=2, message:
...
...
Я использую tcpdump
мониторинга соединения, когда он завершает работу клиента, он просто отправляет пакет FIN, а серверная программа просто отправляет ACK-пакет, никаких других данных, связанных с соединением, нет.
почему select
продолжает находить клиентский сокет, может быть прочитан, пока он просто читает пустое сообщение (как показывает сообщение печати)?
Любая помощь будет оценена.
update: Я знаю, что использование метода select
ясно, я думал, что не использую этот метод правильно, поэтому я потратил около часа на изучение этого метода, чтобы решить мою проблему, но не нашел результата.
Вы игнорируете результат, возвращаемый recv(), который сам по себе является ошибкой, и, в частности, вы игнорируете возможность того, что он равен нулю, который является концом потока, на котором вы должны закрыть сокет, поэтому вы не просто снова выберите EOS.
FD_SETSIZE
в вашемselect
. Отслеживать дескрипторы файлов немного сложнее, но это избавляет от необходимости просматривать весь набор.