Другая проблема разъема.
В моем клиентском коде я отправляю некоторый пакет и ожидаю некоторый ответ от стороны сервера:
отправьте ()
Сразу после отправляют (), катастрофические отказы сервера и перезагрузил себя. Тем временем recv () ожидает. Но даже после того, как сервер возрос, принимать вызов зависает. Я добавил обработку сигнала SIGPIPE, но ее все еще не способный распознать, что сокет повреждается.
Когда я отменяю операцию, я получил ошибку от recv (), что прерывание было выпущено.
Кто-либо мог помочь мне, как исправить эту ошибку?
Это находится в общей библиотеке, работающей на машине Соляриса.
Как уже упоминалось, вы можете использовать select (), чтобы установить ограничение по времени, по истечении которого сокет станет доступным для чтения.
По умолчанию сокет становится доступным для чтения, когда в приемном буфере сокета есть один или несколько байтов. Я говорю «по умолчанию», потому что это количество можно настроить, установив для буфера приема сокета «нижнюю отметку» с помощью параметра сокета SO_RCVLOWAT.
Ниже приведена функция, с помощью которой можно определить, готов ли сокет к чтению в течение заданного периода времени. Он вернет 1, если в сокете есть данные, доступные для чтения. В противном случае он вернет 0, если истечет время ожидания.
Код основан на примере из книги Unix Network Programming (www.unpbook.com), который может предоставить вам дополнительную информацию.
/* Wait for "timeout" seconds for the socket to become readable */
readable_timeout(int sock, int timeout)
{
struct timeval tv;
fd_set rset;
int isready;
FD_ZERO(&rset);
FD_SET(sock, &rset);
tv.tv_sec = timeout;
tv.tv_usec = 0;
again:
isready = select(sock+1, &rset, NULL, NULL, &tv);
if (isready < 0) {
if (errno == EINTR) goto again;
perror("select"); _exit(1);
}
return isready;
}
Используйте это так:
if (readable_timeout(sock, 5/*timeout*/)) {
recv(sock, ...)
Вы упомянули обработку SIGPIPE на стороне клиента, что является отдельной проблемой. Если вы получаете это, значит, ваш клиент пишет в сокет, даже после того, как получил RST от сервера. Это отдельная проблема от проблемы с блокирующим вызовом recv ().
Причина может заключаться в том, что сервер аварийно завершает работу и перезагружается, теряя свое состояние TCP. Ваш клиент отправляет данные на сервер, который отправляет обратно RST, поскольку у него больше нет состояния для подключения. Ваш клиент игнорирует RST и пытается отправить больше данных, и именно этот второй send () заставляет вашу программу получать сигнал SIGPIPE.
Какую ошибку вы получили при вызове recv ()?
May be you should set a timeout delay in order to manage this case. It can easily done by using setsockopt and setting SO_RECVTIMEO flag on your socket:
struct timeval tv;
tv.tv_sec = 30;
tv.tv_usec = 0;
if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof tv))
{
perror("setsockopt");
return -1;
}
Another possibility is to use non blocking sockets and manage read/write stuff with poll(2) or select(2). You should take a look on Beej's Guide to Network Programming.
The problem is that the connection is never actually closed. (No FIN packages are sent etc, the other end just goes away.)
What you want to do is set a timeout for recv'ing on the socket, using setsockopt(3) with SO_RCVTIMEO
as option_name.
Другой способ сделать вызов recv() неблокируемым на Solaris - использовать fcntl() для установки неблокирующего дескриптора сокета:
fcntl(sockDesc, F_SETFL, O_NONBLOCK);
Это можно использовать вместе с select() для защиты recv() от ошибочного возвращаемого значения select() (в случае, если select() возвращает положительное значение, а на сокете нет данных).