Linux : 시간 초과가있는 소켓에서 읽기 또는 수신이 있습니까?


105

시간 초과로 소켓에서 데이터를 읽으려면 어떻게해야합니까? 나는 select, pselect, poll, timeout 필드가 있다는 것을 알고 있지만, 그것들을 사용하면 tcp reno stack에서 "tcp fast-path"가 비활성화됩니다.

내가 가진 유일한 아이디어는 루프에서 recv (fd, ..., MSG_DONTWAIT)를 사용하는 것입니다.


스레드를 사용하는 옵션도 있지만 스레드 신호는 여전히 필요합니다
osgx

답변:


189

setsockopt 함수를 사용하여 수신 작업에 대한 시간 제한을 설정할 수 있습니다.

SO_RCVTIMEO

입력 함수가 완료 될 때까지 대기하는 최대 시간을 지정하는 제한 시간 값을 설정합니다. 입력 작업이 완료 될 때까지 대기하는 시간에 대한 제한을 지정하는 초 및 마이크로 초 수로 timeval 구조를 허용합니다. 추가 데이터를받지 않고이 시간 동안 수신 작업이 차단 된 경우 데이터가 수신되지 않으면 [EAGAIN] 또는 [EWOULDBLOCK]으로 설정된 부분 카운트 또는 errno와 함께 반환됩니다. 이 옵션의 기본값은 0이며 수신 작업이 시간 초과되지 않음을 나타냅니다. 이 옵션은 timeval 구조를 취합니다. 모든 구현에서이 옵션을 설정할 수있는 것은 아닙니다.

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

보도에 따르면 Windows에서이 호출하기 전에 수행해야합니다 bind. 나는 실험을 bind통해 Linux 및 OS X에서 이전 또는 이후 에 수행 할 수 있음을 확인했습니다.


1
이 대답은 내 엉덩이를 구했습니다. 나는 성공하지 못한 채 복잡한 "선택"쓰레기를 구현하고 있었다. 이것은 즉시 효과가 있었고 훨씬 간단했습니다.
MiloDC 2011

이제 Linux 용 코드를 사용하고 있기 때문에 Windows의 시간 초과가 작동하지 않는 이유입니다. 감사. Windows가 사용되지 않으면 struct timeval tv;select ()도 작동하지 않음을 의미합니까? 내 select () 코드를 창으로 포팅하려고 시도했지만 timeval에서 설정하는 값을 무시하는 것처럼 즉시 시간 초과가 발생합니다.
kuchi

1
타임 아웃 값을 5 초로 설정했습니다. 들어오는 데이터가 있는지 여부에 관계없이 각 읽기주기마다 항상 5 초가 걸리는 이유는 무엇입니까?
Han

바인드 작업 후에도 윈도우에서 작동합니다. 윈도우 10에 시도
cahit beyaz

1
@ user463035818 이 답변 은 안된다고 주장합니다.
Tomeamis

22

다음 은 C에서 recv사용 하여 함수에 시간 초과를 추가하는 간단한 코드입니다 poll.

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;
}

이것은 예상대로 정확하게 작동하지 않습니다. poll적어도 한 바이트 또는 타임 아웃을 받기 위해 대기하는 반면, recv함수를 호출 할 때 sizeof(buf)바이트 를 기다릴 것입니다. 이 카운트가 아직 도착하지 않은 경우 다시 차단되지만 이번에는 타임 아웃이 없습니다.
LoPiTaL

0

// WINDOWS에 대한 바인드 작업 후에도 작동합니다.

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

-1

에 대한 처리기를 설치 한 SIGALRM다음 일반 차단 전에 alarm()또는 사용하십시오 . 알람이 울리면은 로 설정된 오류를 반환합니다 .ualarm()recv()recv()errnoEINTR


8
경보 (및 신호)는이 작업에 대한 잘못된 방법입니다. tcp 빠른 경로를 사용하려면 최소한의 대기 시간이 필요합니다. 신호가 느립니다.
osgx 2010 년

2
@osgx 시간 초과가있는 경우에만 신호가 발생합니다.
David Schwartz

-4

리눅스

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

WINDOWS

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

참고 : bind()적절한 실행을 위해 함수 호출 전에이 설정을 지정했습니다.


4
이 질문은 이미 몇 년 전에 답변되었습니다. 솔루션이 제공하는 새로운 가치는 무엇입니까?
Maciej Jureczko 2017-08-30

적절한 실행을 위해 bind () 함수 호출
앞에이
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.