Почему recv возвращает -1 и errno = EINTR, когда установлено SO_RCVTIMEO?

Эта проблема возникает только тогда, когда для сокета установлено время ожидания с SO_RCVTIMEO.

recv должен блокировать на 3 сек. Но он возвращается из-за EINTR, как только начинается другой поток.

Если я запускаю поток t2, recv в потоке t1 вернусь -1 без блокировок и наборов errno в EINTR,

Но recv в потоке t1 работает нормально, когда поток t2 не запускается, просто блокируется на 3 секунды.

Если нить t2 работает до потока t1, recv также работает правильно.

Я обнаружил, что это терпит неудачу каждый раз, когда я отлаживал с SlickEdit или GDB. Но работает правильно, когда работает в терминале.

Вот код:

test.cpp: ссылка -pthread использовать <thread> или поток создает исключение

#include<unistd.h>
#include<netdb.h>
#include<string.h>
#include<thread>

int socket_fd;
sockaddr_in server_addr;

void recvThread()
{
char pData[4096];
int len = recv(socket_fd,pData,4096,0);
if(len<=0)
{
printf("len:%d\n",len);
printf("errno:%d\n",errno);
}
}

void otherThread()
{
while(1)
{
sleep(1);
}
}

int main()
{
hostent *host;
if((host=gethostbyname("127.0.0.1"))==NULL)
{
return 1;
}
memset(&server_addr, 0, sizeof(sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(8887);
server_addr.sin_addr=*((in_addr*)host->h_addr);
bzero(&(server_addr.sin_zero),8);

socket_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

timeval timeout = {3,0};
setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval));
if(connect(socket_fd, (sockaddr*)&server_addr, sizeof(server_addr))<0)
{
return 1;
}

std::thread t1(recvThread);
std::thread t2(otherThread);
t1.join();
t2.join();
}

1

Решение

См. Комментарий к EINTR от «Некоторый программист чувак»

Отладчик (gdb), если он не может устанавливать / изменять точки останова асинхронно, должен остановить цель (вашу задачу), установить точки останова, а затем возобновить его.
Чтобы остановить его, он может отправить SIGINT, что приведет к блокировке вызовов EINTR в вашей системе.

Если вы используете библиотеку GNU C, вы можете использовать макрос TEMP_FAILURE_RETRY, см. Этот пост:
TEMP_FAILURE_RETRY и __USE_GNU

0

Другие решения

Вот потенциальная ошибка

bzero(&(server_addr.sin_zero),8);

должно быть

bzero(&(server_addr.sin_zero), sizeof(server_addr.sin_zero));

Добавьте обработчики сигналов, чтобы выяснить, в чем заключается ошибка, для линукс есть способ справиться со всеми, пример.

1

По вопросам рекламы [email protected]