Linux: há uma leitura ou recv do soquete com tempo limite?

105

Como posso tentar ler dados do soquete com tempo limite? Eu sei, select, pselect, poll, tem um campo de tempo limite, mas o uso deles desativa "tcp fast-path" na pilha tcp reno.

A única ideia que tenho é usar recv (fd, ..., MSG_DONTWAIT) em um loop

osgx
fonte
Também existe a opção de usar threads :) mas os sinais de thread ainda são necessários
osgx

Respostas:

189

Você pode usar a função setsockopt para definir um tempo limite nas operações de recebimento:

SO_RCVTIMEO

Define o valor de tempo limite que especifica a quantidade máxima de tempo que uma função de entrada espera até ser concluída. Ele aceita uma estrutura de valor de tempo com o número de segundos e microssegundos especificando o limite de quanto tempo esperar pela conclusão de uma operação de entrada. Se uma operação de recebimento foi bloqueada por tanto tempo sem receber dados adicionais, ela deve retornar com uma contagem parcial ou errno definido como [EAGAIN] ou [EWOULDBLOCK] se nenhum dado for recebido. O padrão para esta opção é zero, o que indica que uma operação de recebimento não deve expirar. Esta opção tem uma estrutura temporal. Observe que nem todas as implementações permitem que essa opção seja definida.

// LINUX
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

// WINDOWS
DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);

// MAC OS X (identical to Linux)
struct timeval tv;
tv.tv_sec = timeout_in_seconds;
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);

Supostamente, no Windows, isso deve ser feito antes de ligar bind. Eu verifiquei por experiência que isso pode ser feito antes ou depois bindno Linux e no OS X.

Robert S. Barnes
fonte
1
Essa resposta salvou minha bunda. Eu estava preso na implementação daquela porcaria complicada de "selecionar", sem sucesso. Isso funcionou imediatamente, muito mais simples.
MiloDC
É por isso que o tempo limite no windows não está funcionando porque estou usando o código para linux. Obrigado. Se o Windows não estiver usando struct timeval tv;, isso significa que select () também não funcionará? Tentei portar meu código select () para o windows e ele atinge o tempo limite imediatamente, parece que está ignorando o valor que estou definindo em timeval.
kuchi
1
Defino o valor do tempo limite para 5 segundos. Por que sempre leva 5 segundos para cada ciclo de leitura, independentemente de haver dados de entrada ou não?
Han
isso também funciona no Windows, mesmo após a operação de ligação. experimentado no windows 10
cahit beyaz
1
@ user463035818 Essa resposta afirma que não.
Tomeamis
22

Aqui está um código simples para adicionar um tempo limite à sua recvfunção usando pollem C:

struct pollfd fd;
int ret;

fd.fd = mySocket; // your socket handler 
fd.events = POLLIN;
ret = poll(&fd, 1, 1000); // 1 second for timeout
switch (ret) {
    case -1:
        // Error
        break;
    case 0:
        // Timeout 
        break;
    default:
        recv(mySocket,buf,sizeof(buf), 0); // get your data
        break;
}
Abdessamad Doughri
fonte
isso não funcionaria exatamente como esperado. pollirá esperar pelo recebimento de pelo menos um byte ou timeout, ao passo que ao chamar a recvfunção irá esperar os sizeof(buf)bytes, fazendo com que bloqueie novamente se esta contagem ainda não tiver chegado, mas desta vez sem ter um timeout.
LoPiTaL
0

// funciona também após a operação de ligação para WINDOWS

DWORD timeout = timeout_in_seconds * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof timeout);
cahit beyaz
fonte
-1

Instale um manipulador para SIGALRMe use alarm()ou ualarm()antes de um bloqueio regular recv(). Se o alarme disparar, o recv()retornará um erro com errnodefinido para EINTR.

caf
fonte
8
alarmes (e sinais) são o caminho errado para essa tarefa. Se eu quiser usar o caminho rápido tcp, preciso de latência mínima. Os sinais são lentos.
osgx
2
@osgx O sinal ocorre apenas se houver um tempo limite.
David Schwartz
-4

LINUX

struct timeval tv;
tv.tv_sec = 30;        // 30 Secs Timeout
tv.tv_usec = 0;        // Not init'ing this can cause strange errors
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,sizeof(struct timeval));

JANELAS

DWORD timeout = SOCKET_READ_TIMEOUT_SEC * 1000;
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout));

NOTA : Você colocou esta configuração antes da bind()chamada de função para uma execução adequada

vivek
fonte
4
Esta pergunta já foi respondida anos atrás. Que novo valor a sua solução traz?
Maciej Jureczko
Você colocou esta configuração antes da chamada de função bind () para uma execução adequada, esta parte não é mencionada em ans
vivek