본문 바로가기

진리는어디에

소켓 강제 종료시 파이프(pipe) 깨짐

상황 :
 서버 프로세스가 떠 있고, 거기에는 수많은(이라고 해봐야 1000개 미만)의 클라이언트 들이 물려 있다.
 서버는 select로 read 이벤트를 구분하고, 서버의 listen socket은 blocking이다.

 정상적으로 동작하다가 클라이언트(테스트 프로그램으로써 수백개의 connection thread를 생성 시켜 서버로 미친듯이 데이터를 주고 받는 놈)를 강제 종료 하면 항상 그런 것은 아니지만 종종 '파이프 깨짐'이라는 에러를 내고 서버가 종료 된다.

문제 :
 프로세스는 종료(그것이 타의든 자의든)를 하게 되면 모든 열려있는 파일디스크립터를 닫게 된다. socket 역시  일종의 파일디스크립터이므로 예외는 아니다.
 정상적인 수순을 따르자면

1. client가 죽는다.
2. 모든 열린 파일디스크립터들을 닫는다.
3. 파일디스크립터는 닫히면서 FIN을 날리게 된다.
4. server의 select에서는 FIN도 하나의 readable한 것이라고 판단하고 리턴한다.
5. read()는 0을 리턴, 이것으로 peer가 close했음을 알아 차림.

 위와 같지만, 서버가 client에 write하기 전이나, 하고 있는 중에는 selec에서 기다리고 있는 상황이 아니므로 write() 함수 호출시 SIGPIPE가 발생한다.

해결 :
 sigaction이나 signal같은 함수를 이용해 SIGPIPE 시그널을 ignore하도록 하고, write시에 리턴 값이 -1이고 errno가 EPIPE 경우를 처리 하도록한다.

추가 사항 : 2007.1.27
 write() 함수가 리턴하는 값이 -1이거나 errno를 체크하여 pipe가 깨짐을 확인하고 소켓을 close하였다.
 하지만 어찌된 일인지 정상적으로 동작하지 않았으며, 한번 컨넥션이 끊어지고 난뒤에는 재 접속이 안되었다.
 write를 확인하고 종료 할때만 그렇길래 write에 대한 처리를 하지 않으니 정상적으로 동작했다.
 왜 그런지 이유는 모르겠다. 혹시나 아시는 분..트랙백 좀 달아주오~

원문 :
 http://kldp.org/node/25608

참고 :
 Advance Programming in Unix Enviroment에 따르면 SIGPIPE 는 'reader가 없는데 쓸때' 발생 한다고 한다.

 If we write to a pipeline but the reader has terminated, SIGPIPE is generated. We describe pipes in Section 15.2. This signal is also generated when a process writes to a socket of type SOCK_STREAM that is no longer connected. We describe sockets in Chapter 16.
유익한 글이었다면 공감(❤) 버튼 꾹!! 추가 문의 사항은 댓글로!!