C/С++ и неблокирующий recv()
У меня возникла проблема, когда вызов системного вызова recv() не блокируется. У меня есть настройка структуры клиент-сервер в настоящий момент, и проблема, с которой я сталкиваюсь, - это отправить одно сообщение на сервер, а сервер настроен так, что это что-то вроде:
while (1) {
char buf[1024];
recv(fd, buf, sizeof(buf), flags);
processMsg(buf);
}
Он получает первое сообщение правильно, но recv() не блокирует и не принимает данные мусора, что не является желательным. Я хотел бы реагировать на сообщения только тогда, когда они отправляются. Может ли кто-нибудь посоветовать?
Ответы
Ответ 1
recv() не обязательно блокируется до полного выполнения запроса, но может возвращать частичный запрос. Код возврата информирует вас о том, сколько байтов было фактически получено, что может быть меньше, чем вы просили. Даже если вы укажете флаг MSG_WAITALL, он может вернуться меньше из-за сигнала и т.д.
В системах posix в режиме блокировки recv будет блокироваться только до тех пор, пока не будут прочитаны некоторые данные. Затем он вернет эти данные, которые могут быть меньше запрошенных, до запрашиваемой суммы. В неблокирующем режиме recv немедленно возвращается, если есть нулевые байты данных, которые нужно прочитать, и будет возвращать -1, установив errno в EAGAIN или EWOULDBLOCK.
Результат состоит в том, что вы обычно вызываете recv в цикле, пока не получите нужную сумму, а также проверьте коды возврата 0 (другая сторона отключена) или -1 (некоторая ошибка).
Я не могу говорить о поведении Windows.
Ответ 2
Существуют две возможности: либо ошибка, либо сокет установлен в неблокирующий режим. Чтобы узнать, произошла ли ошибка, проверьте возвращаемое значение recv
:
while() {
char buf[1024];
int ret = recv(,buf,,)
if(ret < 0) {
// handle error
printf("recv error: %s\n", strerror(errno));
} else {
// only use the first ret bytes of buf
processMsg(buf, ret);
}
}
Чтобы поставить сокет в неблокирующий режим или запросить, находится ли сокет в неблокирующем режиме, используйте fcntl(2)
с флаг O_NONBLOCK
:
// Test if the socket is in non-blocking mode:
if(fcntl(sockfd, F_GETFL) & O_NONBLOCK) {
// socket is non-blocking
}
// Put the socket in non-blocking mode:
if(fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK) < 0) {
// handle error
}
Обратите внимание, что если вы явно не изменяете поведение блокировки, сокет должен блокироваться по умолчанию, поэтому, скорее всего, происходит ошибка.
Ответ 3
Если вы работаете в Windows, запустите функцию wsagetlasterror() и посмотрите на возвращаемое значение.
http://msdn.microsoft.com/en-us/library/ms741580%28v=vs.85%29.aspx
Если вы находитесь в системе, совместимой с posix, посмотрите на errno
http://pubs.opengroup.org/onlinepubs/009695399/functions/errno.html