2014-09-03 3 views
0

서버/클라이언트 프로그램을 작성했습니다. 그리고 select 체크 소켓을 사용하십시오. 그러나 클라이언트 클로즈 소켓 (서버의 TCP 상태가 close_wait에 들어갈 때)을 선택하면 항상 1을 반환하고 errno는 0입니다.tcp in close_wait, select always returns 1

select이 반환하나요? Tcp 소켓은 지금 읽을 것이 없습니다!

서버 :

int sock = socket(AF_INET, SOCK_STREAM, 0); 

struct sockaddr_in addr; 
addr.sin_family = AF_INET; 
addr.sin_addr.s_addr = htonl(INADDR_ANY); 
addr.sin_port = htons(6999); 
socklen_t socklen = sizeof(struct sockaddr_in); 
bind(sock, (struct sockaddr *)&addr, socklen); 
listen(sock, 0); 

int clisock; 
clisock = accept(sock, NULL, NULL); 

fd_set backset, rcvset; 
struct timeval timeout; 
timeout.tv_sec = 3; 

int maxfd = clisock+1; 
FD_SET(clisock, &rcvset); 
backset = rcvset; 

int ret; 
while(1) { 
    rcvset = backset; 
    timeout.tv_sec = 3; 
    ret = select(maxfd, &rcvset, NULL, NULL, &timeout); 
    if(ret <= 0) 
     continue; 

    sleep(1); 
    printf("ret:%d, %s\n", 
     ret, strerror(errno)); 
} 

클라이언트 :

int sock = socket(AF_INET, SOCK_STREAM, 0); 

struct sockaddr_in addr; 
addr.sin_family = AF_INET; 
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
addr.sin_port = htons(6999); 

socklen_t socklen = sizeof(struct sockaddr_in); 
connect(sock, (struct sockaddr *)&addr, socklen); 

sleep(3); 
close(sock); 
sleep(100); 

출력 :

./server 
ret:1, Success 
ret:1, Success 
ret:1, Success 
+2

원격 종점이 연결을 닫으면 로컬 종단점은 읽을 수있게되지만 '읽기'는 '0'을 반환합니다. 이는 원격 종단점이 닫혔다는 표시이며, '읽기'는 '0'을 반환합니다. –

+1

'errno'의 사용에 관해서는, 실제로 * 에러가 아닌 한 체크하지 마시고 오류가있는 함수 호출 후에 직접 체크하십시오 (즉,'select'가 실패하면'errno'를' 선택 '). '수면'이 실패하면 어떻게 될지 생각해보십시오. 그러면'errno'는'sleep' 함수의 에러를 포함하게됩니다. –

+0

루프가 너무 빠르기 때문에 잠을 듭니다. errno는 항상 0입니다. 감사합니다! –

답변

1

선택 반환 모니터링하는 소켓 하나에 이벤트가 있기 때문에. 이 문서에서는 "읽을 수있는"이라는 용어를 사용합니다. 이 캐스는 다른 끝의 소켓이 닫혀 있고 읽을 바이트가 없으므로 다소 오도 된 것입니다. select가 모든 종류의 파일 설명자에서 작동하기 때문입니다. "파일"은 소켓, 파이프 또는 일반 파일 일 수 있습니다. 그들은 서로 다른 종류의 파일 기술자의 특성에 얽매이지 않기를 원했습니다.

다른 쪽 끝의 소켓이 닫혀있는 것은 정상이므로 선택하면이 경우 오류가 반환되지 않습니다. 소켓에서 실제로 읽으려고하면 연결이 닫힌 경우 사용 가능한 모든 데이터를 읽은 후에 오류가 발생합니다.

select는 여러 파일 설명자를 한 번에 모니터링 할 수 있고 각 파일 설명자에 대해 단일 비트를 사용하기 때문에 "데이터가 도착했습니다"와 "다른 끝의 소켓이 닫혔습니다"를 구분할 수 없습니다. 두 이벤트 모두 소켓을 "읽을 수있는"플래그로 표시합니다.

쓰기를 모니터링 할 때도 마찬가지입니다. 다른 쪽이 끝점을 닫으면 소켓은 select와 관련하여 "쓰기 가능"으로 플래그됩니다. 실제로 소켓에 쓰려고 시도 할 때까지는 오류가 발생하지 않습니다.

+2

소켓이 닫히지 않았습니다. 연결은 피어에 의해 닫혀 있습니다. 소켓은 아직 열려 있습니다. 그렇지 않으면 select()가 오류와 함께 즉시 반환됩니다. 연결로 소켓을 혼합하고 있습니다. 그리고 스트림을 끝까지 읽을 때 오류가 발생하지 않습니다. 스트림 종료 표시가 나타납니다. recv()는 0을 반환합니다. – EJP

+0

아니야. 그것은 여전히 ​​'소켓이 닫혀있다'고 말하고 '사용 가능한 모든 데이터를 읽으면 오류가 발생합니다'라고 말합니다. 이 두 진술 중 어느 것도 사실이 아닙니다. 또한 세 번째 단락의 추론은 중요합니다. 진짜 이유는 EOS가 독자적인 이벤트가 아닌 읽기 이벤트로 취급된다는 것입니다. 마지막 단락이 잘못되었습니다. 송신 버퍼에 여유가있는 경우에만 소켓에 쓰기 가능으로 플래그가 지정됩니다. 원격으로는 아무런 관련이 없습니다. – EJP

+2

내가 볼 수있는 것은 8 시간 전에 지적한 오류를 수정하지 않은 것입니다. 필자의 마지막 요점은 실제로 맞다.'select()'는 리모트 닫기를받은 것을 쓰기 가능으로 표시하지 않는다. 그렇게하는 것은 잘못된 것입니다. 알고있는 것 중 하나는 FIN을 수신했기 때문입니다.이 FIN은 쓰기를위한 종료 일 수 있습니다. 피어는 여전히 읽을 수 있습니다. – EJP

2

피어가 그것을 닫았 기 때문에 소켓이 읽기 쉽고 읽으면 소켓이 종료됩니다. 오히려 아무것도.

CLOSE_WAIT는 (는) 소켓을 닫기 위해 TCP가 대기 중임을 나타냅니다. 그렇게해라.