Linux: ¿hay una lectura o una recepción del socket con tiempo de espera?

105

¿Cómo puedo intentar leer datos del socket con tiempo de espera? Lo sé, select, pselect, poll, tiene un campo de tiempo de espera, pero su uso desactiva "tcp fast-path" en tcp reno stack.

La única idea que tengo es usar recv (fd, ..., MSG_DONTWAIT) en un bucle

osgx
fuente
También existe la opción de usar hilos :) pero aún se necesitan señales de hilo
osgx

Respuestas:

189

Puede utilizar la función setsockopt para establecer un tiempo de espera en las operaciones de recepción:

SO_RCVTIMEO

Establece el valor de tiempo de espera que especifica la cantidad máxima de tiempo que espera una función de entrada hasta que se completa. Acepta una estructura de valores de tiempo con la cantidad de segundos y microsegundos que especifican el límite de cuánto tiempo esperar para que se complete una operación de entrada. Si una operación de recepción se ha bloqueado durante tanto tiempo sin recibir datos adicionales, volverá con un recuento parcial o errno establecido en [EAGAIN] o [EWOULDBLOCK] si no se reciben datos. El valor predeterminado para esta opción es cero, lo que indica que una operación de recepción no se agotará. Esta opción tiene una estructura de tiempo. Tenga en cuenta que no todas las implementaciones permiten configurar esta opción.

// 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);

Según se informa, en Windows, esto debe hacerse antes de llamar bind. He verificado mediante un experimento que se puede hacer antes o después binden Linux y OS X.

Robert S. Barnes
fuente
1
Esta respuesta me salvó el culo. Me quedé atascado implementando esa complicada mierda de "seleccionar", sin éxito. Esto funcionó de inmediato, mucho más simple.
MiloDC
Ahora es por eso que el tiempo de espera en Windows no funciona porque estoy usando el código para Linux. Gracias. Si Windows no se usa struct timeval tv;, ¿significa que select () no funcionará también? Intenté portar mi código select () a Windows y simplemente se agota inmediatamente, parece que está ignorando el valor que estoy configurando en timeval.
Kuchi
1
Establecí el valor del tiempo de espera en 5 segundos. ¿Por qué siempre toma 5 segundos para cada ciclo de lectura independientemente de que haya datos entrantes o no?
Han
esto también funciona en Windows incluso después de la operación de enlace. probado en windows 10
cahit beyaz
1
@ user463035818 Esta respuesta afirma que no debería.
Tomeamis
22

Aquí hay un código simple para agregar un tiempo de espera a su recvfunción usando pollen 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
fuente
esto no funcionaría exactamente como se esperaba. pollesperará recibir al menos un byte o timeout, mientras que al llamar a la recvfunción esperará sizeof(buf)bytes, provocando que se bloquee nuevamente si aún no ha llegado este conteo, pero esta vez sin tener timeout.
LoPiTaL
0

// funciona también después de la operación de vinculación para WINDOWS

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

Instale un controlador para SIGALRM, luego use alarm()o ualarm()antes de un bloqueo regular recv(). Si la alarma se dispara, recv()devolverá un error con el errnoajuste a EINTR.

coste y flete
fuente
8
las alarmas (y señales) son el camino equivocado para esta tarea. Si quiero usar tcp fast path, necesito una latencia mínima. Las señales son lentas.
osgx
2
@osgx La señal solo ocurre si hay un tiempo de espera.
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));

VENTANAS

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

NOTA : ha puesto esta configuración antes de la bind()llamada de función para una ejecución adecuada

vivek
fuente
4
Esta pregunta ya ha sido respondida hace años. ¿Qué nuevo valor aporta su solución?
Maciej Jureczko
Ha puesto esta configuración antes de la llamada a la función bind () para una ejecución adecuada, esta parte no se menciona en ans
vivek