문서 정보
  • Archive-name: unix-faq/socket
  • Posting-Frequency: monthly
  • 최근 수정일: 1997/12/21
  • URL: http://www.auroraonline.com/sock-faq/
  • 한글판작성자: 안창선(csan@coresw.co.kr, http://genesis.yonsei.ac.kr/~kabin)

이 부분은 번역이 아주 까다롭더군요 말이 안되는것도 많을겁니다.(저 한테만)

  1. 클라이언트와 서버 양쪽에 관한 질문 (TCP/SOCK_STREAM)
    1. 언제 상대방쪽 소켓이 끊겼는지 알 수 있는 방법이 있는가?
    2. bind()의 두 번째 파라메터는 무엇인가?
    3. 주어진 서비스의 port번호를 얻는 방법은?
    4. bind() 가 실패 했을 때 소켓 디스크립터를 가지고 할 수 있는 일은?
    5. 정확하게 소켓을 닫는 방법은 무엇인가?
    6. 언제 shutdown()을 써야 하는가?
    7. TIME_WAIT 상태에 대해 설명해 달라.
    8. 상대방쪽이 죽었는지 알아 내는데 왜 이렇게 오래 걸리나?
    9. select(), non-blocking I/O 그리고 SIGIO의 잇점들은 무엇인가?
    10. read()가 EPROTO 를 리턴했다. 무엇인가?
    11. 소켓 버퍼에 있는 내용을 강제로 전달 할 수 있는 방법은 없나?
    12. 소켓 프로그래밍을 위한 라이브러리가 있는가?
    13. select는 데이터가 있다고 하는데 읽으면 0을 리턴한다?
    14. select() 와 poll()의 차이점은 무엇인가?
    15. 어떻게 [this]를 소켓을 통해 보낼 수 있는가?
    16. TCP_NODELAY는 어떻게 사용하는가?
    17. Nagle 알고리즘은 어떤일을 하는 것인가?
    18. read() 와 recv()의 차이점은?
    19. send()/write()가 SIGPIPE 신호를 생성할 수 있던데. 이 신호를 무시하거나 EPIPE오류에 대한 검사를 하지 않고 다른 처리를 했을때의 어떤 잇점이 있는가? 시그널을 잡는 잡는 함수 에 넘겨지는 어떤 유용한 파라미터가 있는가?
    20. chroot()후에 socket()이 실패했다. 왜 그런가?
    21. 소켓 호출로 부터 전달되는 EINTR을 유지해야 하는 이유는 무엇인가?
    22. 언제 나의 프로그램이 SIGPIPE 신호를 받게 되는가?
    23. 소켓 예외는 있는가? out-of-band데이타란 무엇인가?
    24. 어떻게 완전한 내 시스템의 호스트이름인 (FQDN)을 얻을 수 있는가?

2. 클라이언트와 서버 양쪽에 관한 질문 (TCP/SOCK_STREAM)

2.1. 언제 상대방쪽 소켓이 끊겼는지 알 수 있는 방법이 있는가?

Andrew Gierth (andrew@erlenstar.demon.co.uk)로 부터:

AFAIK:

만약 SO_LINGER상태로 엉망이 된 peer가 close()를 호출 하거나 exit하면 read()하기 위한 호출이 0을 리턴한다. 이 경우에 w rite() 호출을 하기 위한 어떤 일이 일어났는지에 대해서는 불확실 하다. 다음 호출이 아니라 계속적인 호출에서 EPIPE가 생길 것이다.

만약 peer가 리부팅 하거나 또는 l_onoff를 1로 그리고 l_linger를 0으로 설정하고 close하면 우리는 read()로 부터 ECONNRES ET(결국은)를 얻게 되거나 write()로 부터 EPIPE를 얻게 될 것이다.

나는 또한 다음을 지적하고 싶다. write()가 EPIPE를 리턴했을 때 그것은 또한 SIGPIPE로 함께 되돌린다는 것이다. - 여러분 은 이 시그널을 무시하거나 핸들하는 것을 구현하지 않는한 이 EPIPE오류를 결코 볼 수 없다.

만약 peer가 도달할수 없는(unreachable) 상태로 지속된다면, 또다른 몇몇의 오류를 얻게 될 것이다.

나는 write()가 합법적으로 0을 리턴할수 있다고 생각지 않는다. read()는 peer로부터 FIN을 받았을 때 그리고 다음의 모든 호출에서 0을 리턴해야 한다.

그러면 read()가 0을 리턴하게 될 것이다.

예제로, TCP link로부터 파일을 다운 받는다고 가정하자; 여러분은 read()로 부터의 리턴값을 다음과 같이 핸들 할 것이다.

rc = read(sock,buf,sizeof(buf));
if (rc > 0)
{
   write(file,buf,rc);
   /* error checking on file omitted */
}
else if (rc == 0)
{
   close(file);
   close(sock);
   /* file received successfully */
}
else /* rc < 0 */
{
   /* close file and delete it, since data is not complete
   report error, or whatever */
}

2.2. bind()의 두 번째 파라미터는 무엇인가?

매뉴얼 페이지는 "struct sockaddr *my_addr"이라고 되어 있다. 이 sockaddr 구조체는 원하는 구조체에 대한 홀더 일 뿐이다. 물론 가지고 있는 소켓의 종류에 따라 다른 구조체를 써야 한다. AF_INET소켓에서는 sockaddr_in 구조체를 써야 한다 .

구성요소에는 세가지가 있는데

sin_family
이건 AF_INET로 설정하라.

sin_port
byte-order된 16 bit 포트 번호

sin_addr
호스트 ip 번호. 이것은 in_addr구조체인데 u_long형태의 s_addr이라는 하나의 필드만 포함되어 있다.

2.3. 주어진 서비스에 대한 포트번호를 어떻게 얻을 수 있는가?

getservbyname()을 쓰면 된다. 이것은 servent구조체를 리턴할 것이다. 여러분은 포트 번호를 가지고 있는 올바른 바이트 순 서로된 s_port필드에 흥미 있을 것이다.(여러분은 htons()를 호출할 필요가 없다.) 예제를 보라.

/* 서비스 이름과 서비스 형태를 받고 포트 번호를 리턴한다.
   만약 서비스 이름이 발견되지 않으면 그것은 10진 수로
   다시 시 도 한다. 리턴된 숫자는 네트웍을 위한
   바이트 순서(byte ordered)화된 것이다. */

int atoport(char *service, char *proto)
{
   int      port;
   long int lport;
   struct   servent *serv;
   char    *errpos;

   /* 우선 /etc/services로 부터 읽는다 */
   serv = getservbyname(service, proto);
   if (serv != NULL)
      port = serv->s_port;
   else {                  /* 서비스에 없으면 숫자인가 */
      lport = strtol(service,&errpos,0);
      if ( (errpos[0] != 0) || (lport < 1) || (lport > 5000) )
         return -1;           /* 잘못된 포트 번호이다 */
      port = htons(lport);
   }
   return port;
}

2.4. bind()가 실패했다면 소켓 디스크립터로 뭘 해야 하는가?

exit하고 있다면, 모든 유닉스 시스템은 파일 디스크립터를 닫을 것이다. 만약 exit하고 있는 것은 아니라면 일반 close()로 닫아야 한다.

2.5. 정확하게 소켓을 닫는 방법은?

이 질문은 보통 close()함수를 쓰려고 하는 사람들이 하는데, 그들은 그렇게 해야 한다고 보고 있기 때문이다. 보통 그렇게 하고 netstat등으로 보면 소켓이 여전히 활동상태(active)라는 걸 알게 될 것이다. 그러나 close()를 쓰는건 맞다. TIME_WAIT상 태 값이 왜 중요한지 그 이유가 여기에 있다. 2.7의 "TIME_WAIT 상태에 대해 설명해 달라"를 참조하라.

2.6. 언제 shutdown()를 써야 하는가?

Michael Hunter (mphunter@qnx.com)로부터:

shutdown()은 여러분은 TCP를 사용하여 서버로 요청 하는일을 언제 끝냈는지 알아 내는데 유용하다. 전형적인 사용예는 서버 로 요청이 전달되고 나후에 shutdown()이 이루어 졌을 경우에 유용하다. 서버는 여러분의 호출을 EOF일때까지 받는다. 즉 이것 은 서버가 여러분의 완전한 요청을 받았다는 것을 의미한다. 그리고 나서 소켓에서의 read는 블록된다. 서버는 요청을 처리하고 필요한 데이터를 여러분에게 보내고 소켓을 닫게 된다. 여러분이 필요한 모든 내용을 소켓에서 읽었다면 마지막에 EOF를 읽게 될 것이다. 이것은 TTCP(TCP for Transactions)가 보다 나은 트랜잭션 처리를 위한 도구 라는 것을 보여준다.

S.Degtyarev (deg@sunsr.inp.nsk.su)는 이것에 관한 상세한 메시지를 내게 보내 주었다. 그는 하나는 "reader"이고 다른 하나는 "write" 프로세스인 클라이언트 프로세스의 동기화를 도와주는 shutdown()에 대한 실제 예제까 들었다. 여기 그것의 일부를 여기 싣는다.

소켓은 데이터 전달과 클라이언트/서버 트랜잭션에 사용된다는 점에서 파이프와 매우 비슷하다 그러나 파이프와 다른 점은 양 방향 성이라는 것이다. 소켓을 사용하는 프로그램은 자주 fork()하고 각 프로세스는 소켓 드스크립터를 상속받는다. 파이프에 기 본을 둔 프로그램은 데이터 상실이나 deadlock을 피하는 단방향성의 데이터 스트림으로 pipe line을 만들기 위해 사용되지 않는 모든 끝은 닫도록 엄격히 권고되고 있다. 그러나 소켓의 경우에는 한쪽은 쓰기만 하고 한쪽은 받기만 하는 것은 허용되지 않기 때문에 여러분들은 그 순서에 유념해야 할 필요가 있다.

일반적으로 close()와 shutdown()의 차이점은: close()는 프로세스에 대해 소켓 ID를 닫지만 만약 다른 프로세스가 소켓 ID를 공유하고 있다면 연결은 여전히 열려 있게 된다. 그 연결은 읽기,쓰기를 위해 유지 되며, 어떤때, 이것은 매우! 중요하다. shut down()은 그 소켓 ID를 공유하고 있는 모든 프로세스에 대해 연결을 끊어버린다. 커널 소켓 버퍼가 꽉 차이 있으면 읽기를 시도 하는 프로세스는 EOF를 발견하게 되고, 쓰기를 시도하는 프로세스는 SIGPIPE를 받게 된다. 게다가 shutdown()은 그 연결을 어떻 게 끊을지를 나타내는 두 번째 파라메터를 가지고 있는데, 0은 이후의 읽기를 못하게하고, 1은 쓰기를, 2는 양쪽 모두를 못하게 한다.

이 쉬운 예제는 단순한 클라이언트 프로세스를 자른 것이다. 연결을 설립한 후에 서버는 fork한다. 그리고 나서 child는 EOF 를 받거나 부모프로세스가 서버로부터 응답을 받을 때까지 키 입력을 서버에게 전송한다.

/*
 * 예제 클라이언트 쪼가리..
 * 변수선언과 에러 핸들링은 제외 됐음
 */

s=connect(...);

if( fork() ){     /* 자식은 그 stdin을 소켓으로 복사한다 */
   while( gets(buffer) >0)
   write(s,buf,strlen(buffer));

   close(s);
   exit(0);
   }
else {            /* 부모가 응답을 받는다 */
   while( (l=read(s,buffer,sizeof(buffer)){
      do_something(l,buffer);

   /* 서버가 연결을 끊는다 */
   /* ATTENTION: deadlock here */
   wait(0);       /* 자식이 나갈때까지 기다림 */
   exit(0);
   }

무엇을 기대할 수 있는가? 자식프로세스는 stdin으로부터 EOF를 받으면 소켓을 닫고 빠져 나간다. 서버는 EOF를 받으면 연결 을 끊고 빠져 나간다. 부모 프로세스는 EOF를 받으면 wai()을 호출하고 빠져 나간다. 이것 대신 우린 뭘 할 수 있을까? 비록 부 모 프로세스가 쓰기를 하지 않을지 모르지만 부모 프로세스에 있는 소켓 인스턴스는 읽기와 쓰기를 위해 여전히 열려 있다. 서버 는 EOF를 발견하지 못한다. 그리고 영원히 클라이언트로부터의 데이터를 기다린다. 그리고 서버도 죽~. (여기서 deadlock이 발생 된다.)

여러분은 클라이언트 프로그램을 다음과 같이 바꿔야 한다.

if( fork() ) { /* 자식 */
   while( gets(buffer) )
      write(s,buffer,strlen(buffer));

   shutdown(s,1); /* 쓰기를 위해 연결을 끊는다
      서버는 EOF를 받게 된다. Note: 소켓으로부터의 읽기는 여전히 허용된다.
      서버는 EOF를 받은 후에 몇 데이터를 보낼 것이다. */
   exit(0);
   }

나는 이 다듬어 지지 않은 예제가 여러분들이 가지고 있는 클라이언트/서버의 동기화의 문제를 설명해 줄 수 있기를 바란다. 일반적으로 여러분들이 모든 프로세스의 특정 소켓 인스턴스에서 기억하고 있어야 할 것이 있는데, 만약 여러분이 하나의 프로세 스 안에서 연결을 끊기 위해 close()또는 shutdown()을 사용하길 바란다면, 그들 모두를 공유하고 있다가 한 번에 닫아라.

2.7. TIME_WAIT 상태에 대해 설명해 달라.

TCP는 모든 전송 데이터에 신뢰성을 가진다. 소켓을 닫을 때 서버는 TIME_WAIT상태가 되고 모든 데이터는 사라지게 된다는 것 을 *정말*정말*. 소켓이 닫혀지면 양쪽으로 전송되던 데이터는 더 이상의 전송은 되지 않게 된다. 문제는 두 개다. 첫째, 마지막 ack가 성공적으로 전달 되었는지 알 수 있는 방법이 없다는 것이다. 두 번째, 전송되었으면 처리 되었을 "헤메는 복사본 "이 네트웍에 남아 있게 된다는 것이다.

Andrew Gierth (andrew@erlenstar.demon.co.uk)는 다음의 유즈넷 포스팅에서의 close순서에 대한 설명을 도와줬다.

다음을 가정하자. 연결은 ESTABLISHED상태에 있고, 클라이언트는 순종한다. 클라이언트의 순서는 없고 Sc라고 표시하며 서버 는 Ss라고 한다. 파이프는 양쪽방향으로 비어있다.

            클라이언트   서버
  =========   ======
  ESTABLISHED
(client closes)
  ESTABLISHED
  ESTABLISHED   ESTABLISHED
    <CTL=FIN+ACK><SEQ=Sc><ACK=Ss> ------->>  
  FIN_WAIT_1    
    <<-------- <CTL=ACK><SEQ=Ss><ACK=Sc+1> CLOSE_WAIT
(server closes)
    <CTL=ACK>,<SEQ=Sc+1><ACK=Ss+1> ------->> LAST_ACK
  TIME_WAIT
(2*msl elapses...)
  CLOSED
  CLOSED    

Note: 순서번호에서 +1 는 FIN이 데이터를 1바이트로 세기 때문이다.(위의 diagram은 작은 것이다. RFC 793의 13.)

이제 만약 이 패킷의 마지막것이 발생했을 때 어떤 일이 일어나는지 보자. 클라이언트는 연결을 마쳤다. 이제 더 이상 보낼 제어 데이터가 없고 앞으로도 없을 것이다. 그러나 서버는 클라이언트가 모든 데이터를 잘 받았는지 알 수 없다. 그 이유은 마지 막 ack 때문이다. 이제 서버는 클라이언트가 그 데이터를 갖고 있는지가 걱정거리다. 그러나 TCP에 문제가 있는건 아니다. 왜냐. . TCP는 신뢰성 있는 프로토콜이기 때문이다. 그리고 모든 데이터 전송이 제대로 됐으면 close되며, 데이터를 잃어 버리게 되면 연결은 중지(abort)된다.

그래서 만약 마지막 패킷이 날라가 버리면, 서버는 그것을 다시 전송하고(결국 이 패킷은 acknowledge되지 않은 세그먼트이다 .) 적당한 ACK 세그먼트가 되돌아 오길 기다린다. 만약 클라이언트가 바로 CLOSED되면 되돌아 올 수 있는 재전송은 데이터를 잃 어 버린 서버를 지정하게되는 RST가 된다.

명심해야 할 것은 서버의 FIN세그먼트는 추가적으로 데이터를 포함하고 있다는 사실이다.

포기: 이것은 RFC의 TCP관련된 것을 읽은 것에 대한 번역일 뿐이다. 그러나 이 것을 소스 코드를 이용해 테스트를 하고 확인 해보지는 않았다. 비록 나는 이 로직이 옳다는 것에 대해 만족하지만.

Vic의 추가적인 설명:

두 번째 논쟁은 "Unix Network Programming"의 저자인 Richard Stevens(rstevens@noao.edu) 의 말이다.

Richard Stevens (rstevens@noao.edu):

만약 TIME_WAIT의 반복 상태가 단지 TCP의 양방향 close를 핸들링 하기 위한 것이라면 시간은 더 작게 될 것이다. 그리고 그 것은 MSL(packet lifetime)이 아니라 현재의 RTO(retransmission timeout)의 몇몇 함수로 될 수 있을 것이다.

TIME_WAIT에 대한 몇 개의 관점을 소개한다.

o 첫번째 FIN을 보내는 목적은 그것이 마지막 ACK을 보내는 목적이기 때문에 TIME_WAIT상태에 도달한다. 만약에 또다른 목적 의 FIN이 분실되거나 마지막 ACK이 분실되면, 접속에 관한 첫번째 FIN의 유지상태를 보낸 결과가 그것이 마지막 ACK을 재전송할 정보를 충분히 가지고 있음을 보증해 준다.(미치겠군)

  • TCP 시퀀스 번호는 2의 32승 바이트 정도의 수이다.
    A.1500(호스트 A, 포트 1500)와 B.2000사이에 연결이 되었다고 가정하면 한 세그먼트 접속은 상실(lost)되고 재전송 된다. 그러나 세그먼트가 정말 상실된건 아니고, 중간 라우터에 보류 중이며, 다시 네트웍 괘도에 올라가게 된다.(이것을 "헤메 는 복사본:wandering duplicate"라고 부른다.) 그러나 패킷이 상실되고 재전송되고 다시 보이게(reappearing) 되는 사이에 접속은 닫혀지고 그래서 다른 연결이 그 같은 호스트의 같은 포트에서 만들어 진다. 이것을 또다른 "실현:incarnation "이라고 부른다. 그러나 새로운 incarnation을 위해 선택된 시퀀스 번호도 이전에 나타났던 헤메는 복사본의 시퀀스 번호와 중복 되게 된다. (이건 정말 가능한 일이다. 주어진 시퀀스 번호 선택 방법에 따라 TCP 에 대한 연결을 만들기 때문이다.) 여러 분이 헤메는 복사본을 새로운 연결의 incarnation에 보내려 한다면 이 방법은 피하라. 이 방법을 피하기 위해 재 설립될 접 속의 incarnation을 TIME_WAIT상태가 끝날 때 까지 허용하지 말라. 비록 TIME_WAIT상태가 "TIME_WAIT암살"이라고 불리우 는 이 두 번째 문제를 완벽히 해결해 주지는 못할 지라도. 자세한건 RFC 1337을 보라.
  • TIME_WAIT상태의 반복이 2*MSL인 이유는 패킷이 네트웍을 헤매는 시간의 최대량이 MSL초가 된다는 가정 때문이다. '2'는 왕복을 위한 때문이다. MSL을 위한 추천값은 120초이나 버클리로부터 구현된 제품의 경우 30초를 사용한다. 이것은 1과 4분 사이 의 TIME_WAIT딜레이를 의미한다. 솔라리스 2.x는 추천사항인 120초를 사용한다.

"헤매는 복사본"은 상실된 것으로 보이고 재전송되는 패킷을 말한다. 그러나 이것이 정말 상실된 것은 아니고... 몇몇 라우터들이 문제가 있어서 그 패킷을 얼마동안(몇초 정도, TTL이 크면 몇분이 될 수도 있다) 보유하고 있기 때문이고 그리 고나서 패킷을 다시 네트웍에 보낸다. 그러나 다시 그 패킷이 보이는 순간에는 이미 어플리케이션이 원래의 데이터를 재전송 한 상태이다.

TIME_WAIT암살로 인한 이런 잠재적인 문제 때문에 사람들은 SO_LINGER옵션을 설정하여 일반 TCP연결 끝내는것(FIN/ACK/FIN/AC K)에 대신한 RST를 보내는 것으로 TIME_WAIT상태를 피하려 하지는 않는다.
TIME_WAIT상태는 여러분의 친구이도 도움을 준다.

나는 나의 "TCP/IP Illustrated, Volume 3"에서 이 주제에 관해서만 오랜 토론을 벌였다. TIME_WAIT상태는 정말로 TCP의 가장 오해할 만한 부분이다.

나는 지금 "Unix Network Programming"을 다시 쓰고 있다. 그리고 이 주제에 관해 더 자세한 내용을 다룰 것이다. 왜냐면 자주 이 주제가 오해를 불러 오고 혼란스럽게 하기 때문이다.

추가로 Andrew의 설명:

소켓을 닫는거: 만약 SO_LINGER가 소켓에서 호출되지 않았다면 close()는 데이터를 버리는 것을 가정하지 않을 것이다. 이것 은 SVR4.2에서 사실이고, SVR4에서는 아직 확인 안해봤다. shutdown() 또는 SO_LINGER 둘다 모든 데이터의 전달을 보장하는 것을 요구하는 듯하다.

2.8. 왜 상대편의 죽었는지 확인하는데 그렇게 오래 걸리나?

Andrew Gierth (andrew@erlenstar.demon.co.uk)로부터:

기본적으로, 전달할 데이터가 없거나 ack가 없으면 TCP연결은 아무런 데이터도 보내지 않는다.

그래서 만약 단지 여러분이 반대쪽으로부터의 데이터를 기다리는 것이라면 반대peer가 가만히 있는건지 데이터를 전송할 준비 가 아직 안된 경우인지 알 수 가 없다. 이것은 문제가 될 수 있다.(특히, 만약 그 peer가 PC라면 그리고 그 사용자가 그냥~ 전원 을 내려버린거라면)

하나의 해결책은 SO_KEEPALIVE 옵션을 사용하는 것이다. 이 옵션은 Peer가 여전히 존재하는지 정기적으로 시험해 준다.
경고: 이 옵션에 대한 기본 타임아웃시간은 *적어도* 2시간이다. 이 타임아웃시간은 바뀌어질 수 있다.(시스템 의존적이다.) 그러나 보통 매번의 접속에 기본한 것은 아니다.(AFAIK)

RFC1122는 이 타임아웃 시간이 설정 가능하다고 하고 있다. 주요 유닉스에서 이 설정은 전역적으로 설정되어 모든 TCP연결에 영향을 준다. 더욱이 이 값을 변경하는 방법은 어렵고 매뉴얼도 별로 없으며, 어떤 경우에도 존재하는 거의 모든 버전이 다 틀리 다

만약 굳이 바꾸어야 한다면 tcp_keepidle이라는 말 과 같은 것이 kernel 설정파일이나 네트웍 옵션에 있는지 확인해 보라.

Peer로 전송하면, 좀더 나은 보장을 받게 된다. 왜냐면 전송되는 데이터 자체가 peer로부터의 ACK를 받는 것을 의미하기 때문 이다. 그리고 나면 알 것이다. 재전송 타임아웃 후에 그 peer가 여전히 살아 있는지 그렇지 못한지. 그러나 재전송 타임아웃은 T CP연결은 작은 네트웍 문제의 결과로서 전송이 취소되지 않는 것을 의도하여 우발적인 사고를 허용하도록 설계가 되었다.
그래서 실패 통지를 받기 전에 몇분의 지연은 있을 수 있는 일이다.

이 접근 방식은 현재 인터넷에서 쓰이고 있는 대부분의 응용 프로토콜에서 서버로의 읽기 타임아웃을 구현하기 위해 사용되고 있다.(FTP, SMTP 등등); 서버는 단순히 정해진 시간동안(보통 15분) 클라이언트로부터의 요청이 없으면 포기한다. 연결이 오랜 동안 놀고 있는 경우의 프로토콜은 두가지 선택이 있다.

  1. SO_KEEPALIVE를 사용
  2. 고수준의 keepalive 체제를 사용(매번 자주 null요청을 서버에게 보냄)

2.9. select(), non-blocking I/O 그리고 SIGIO의 잇점들은 무엇인가?

non-blocking I/O를 사용하면 소켓으로 부터 읽을 것이 있는지 알아 보기 위해 poll해야 한다. polling은 보통 그것이 다른 기술들 보다 CPU시간을 많이 소모 하기 때문에 피해야 한다.

SIGIO를 사용하면 어플리케이션이 무엇을 하고 OS는 소켓에 대기중인 데이터가 있다는 것을 시그널을 통해 알려 준다. 한가 지 혼돈될 수 있는 결점이라면 만약 여러개의 소켓을 사용 한다면 읽을 준비가 된 소켓을 찾아 내는데 select()를 사용 해야만 한다는 것이다.

select()를 사용하면 여러개의 소켓으로부터 동시에 데이터를 받을 수 있을 때 아주 좋다. 왜냐면 소켓들중의 하나에 데이터 가 준비될 때까지 다른 것들은 block되기 때문이다.

다른 하나의 select()사용에 대한 잇점은 소켓이 데이터를 가지고 있는지를 여러분에게 알려주는 타임 아웃 시간을 설정할 수 있다는 것이다.

2.10. read()가 EPROTO 를 리턴했다. 무엇인가?

Steve Rago (sar@plc.com)로부터:

EPROTO는 프로토콜이 복구할 수 없는 에러를 만났을 시점에 발생한다. EPROTO는 EPROTO이외의 다른 적당한 에러 코드가 없을 때 STREADM에 기반한 드라이버에 의해 사용되는 다목적용(catch-all)의 에러코드들 중의 하나이다.

Andrew (andrew@erlenstar.demon.co.uk)의 추가 단신:

read()로 부터 EPROTO를 받는건 그리 대단한 일은 아니다. 그러나 몇몇 STREAM기반의 구현(implementation)들에서 EPROTO는 만약 incoming연결이 accept가 완전히 이루어 지기 전에 reset되었을 경우 accept()에 의해서도 리턴될 수 있다.

이것이 발생된다면 다른 몇몇 구현에서도 accept는 블록킹 될 수 있다. select()가 듣고있는(listening)소켓이 읽기 가능한 상태고 그래서 accept() 호출에서 일반적으로 블록되지 않는다고 말해지기 때문에 이것은 중요하다. 물론 이 고정(fixing)은 만 약 그것에서 select()를 사용 하려고 할 때 listening 소켓에서의 비블록킹 모드를 설정하기 위함이다.

2.11. 소켓 버퍼에 있는 내용을 강제로 전달 할 수 있는 방법은 없나?

Richard Stevens (rstevens@noao.edu)으로 부터:

강제로 할 수는 없다. 시간이 지나야 한다. TCP는 자신의 데이터를 언제 보낼 것인가를 생각하고 있다. 보통 TCP소켓에서 wr ite()를 호출 했을 때, TCP는 실제로 세그먼트를 보낸다. 그러나 이것을 강제로 시킬 수 있는 방법이나 보증이 없다. 왜 TCP가 세그먼트를 보내지 않는가에 대한 많은 이유가 있다. : "윈도우 담힘:closed window"와 Nagle 알고리즘은 즉시 마음에 와 닫는 그런 이유중의 두가지이다.

(Andrew Gierth의 TCP_NODELAY을 사용 하는 것에 대한 작은 제안)

이것을 설정하는 것은 많은 시험의 하나, Nagle알고리즘를 사용불가능하게 한다. 그러나 만약 그 원래의 포스터의 문제가 사 실이라면 이렇게 옵션을 설정하는 것도 도움이 된다.

tcp_output()을 잠시 보면 11번의 테스트 정도로 TCP가 세그먼트를 보내야 할질 말아야 할지를 결정 해야 한다.

Dr. Charles E. Campbell Jr. (cec@gryphon.gsfc.nasa.gov):

우리가 추측 했을 때, Nagle 알고리즘을 불가능하게 만들어 놔도 아무런 문제가 없었다. 그것은 기본적인 버퍼링 수단이다. 거기엔 작고의 문제가 아니라 모든 패킷에 대하여 혼합된 오버헤드가 발생된다. 그러므로 Nagle알고리즘은 작은 패킷을 모으고( 기껏해야 0.2초정도의 딜레이발생) 그러므로 전송하는 것에 대한 오버헤드를 줄이게 된다. 이 접근방식은 rcp에 대해서 잘 작동 한다. 예를 들어 0.2초 딜레이는 인간에게는 별로 못느끼는 시간이다. 그리고 다중 사용자 환경에서 사용자들은 자신의 패킷을 보다 효율적으로 전송할 수 있다. 대학교 같은 대부분의 일반 사용자가 표준툴, 예를 들어 rcp나 ftp나 telnet, 그런것들을 사용 하는데서는 도움을 줄 수 있을 것이다.

그러나 Nagle알고리즘은 리얼타임시스템에서의 제어와 특히나 인터렉티브한 키 입력을 하는 어플리케이션에서는 안좋은 영 향을 미친다. 선택적으로 Nagle알고리즘을 통과하는 한가지 방법은 Out-of-bind메시지 시스템을 쓰는 것이다. 그러나 이것은 내 용물에 제약이 있고 또 다른 문제(순서의 상실:loss of sequentiality)를 일으킬 수 있다.

Vic의 또다른 내용:

요약하면, 만약 여러분이 문제가 있어서 소켓을 플러시하기를 원한다면, 보통은 TCP_NODELAY를 설정함으로써 문제를 해결할 수 있다. 그러나 Andrew에 의하면 "out-of-bind데이타는 그 자신의 문제를 가지고 있고, 버퍼링 딜레이의 문제를 해결해 주 는 충분한 수단으로 생각지 않는다. 이것은 몇몇 다른 프로토콜에도 존재한다는 점에서 '재촉된 데이터(expedited data)'가 아니 다.; 이것은 스트림 형태로 그러나 어디로 가는지의 포인터를 가지고 전송된다" 라고 한다.

나는 Andrew에게 "TCP가 데이터를 네트웍에 쓸 때 TCP가 무슨약속을 하는가?"의 효과를 요청했다. 그의 대답이 이 것의 대답이 될 거라고 생각한다. 많은 약속은 아니만 약간..

나는 시도하고 이것에 대해 출처를 명확히 하겠다.

참조:

RFC 1122, "Requirements for Internet Hosts" (also STD 3)
RFC 793, "Transmission Control Protocol" (also STD 7)

  1. 소켓 인터페이스는 TCP PUSH flag를 접근하는 것을 지원하지 않는다.
  2. RFC1122 는 다음과 같이 적고 있다 (4.2.2.2):
    TCP는 PUSH flag를 SEND 호출에서 구현하고 있다. 만약 PUSH flag가 구현되지 않았다면 그 전송 TCP는: 1. 불명확하게도, 데이터를 버퍼링 하지 않을 것이다. 그리고 2. 마지막 버퍼링된 세그먼트에 PSH bit를 설정할 것이다.(예를들어, 전송될 데이터 가 큐에 더 이상 없을 때)
  3. RFC793 는 다음과 같이 적고 있다. (2.8):
    수신하고 있는 TCP가 PUSH flag를 보게 된다면, 수신 프로세스로 데이터가 전달되기 전에 전송 TCP로부터 더 이상의 데이 터를 기다리지 않을 것이다.

    [RFC1122에 써있는 내용]

  4. 그러므로 protocol의 고려에 의해서 방해 되는 것을 제외 하고는 write()호출에 전달되는 데이터는 한정된 시간안에 peer 로 전달 되어야 한다.
  5. 데이터를 전달하는데의 약 11번의 테스트 지연시간이 있다.(앞에서의 Steven의 말에 의하면) 그러나 내가 봤을 때, 프로그 래머의 제어하에 있지 않고 한정된 시간에 해결되어야 하거나 연결이 끊기게 되는 재전송 포기와 같은 것으로 오직 2번의 의미있 는 테스트 지연이 있다.

첫 번째 재미있는 것은 "윈도우 닫힘:window closed"(예를 들어 수신자에게 버퍼가 없을 경우 이것은 데이터를 무한 정 지연 시킬 수 있다. 수신 프로세스가 이용할 수 있는 데이터를 실제 읽을 수 없을 때만)

Vic의 질문:

좋다. 만약 클라이언트가 읽지 않는다면 또 데이터는 연결된 것을 통하여만 성공 할 경우에 의미가 있다. 이것은 전송자가 하 여금 수신큐가 차있을 경우 블록된다는 것으로 받아 들이면 되는가?

소켓 전송버퍼가 차 있을 때 송신자는 블록한다. 그래서 버퍼는 양쪽으로 차게 될 것이다.
윈도우가 닫혀진 동안에 전송 TCP는 윈도우 시험 패킷을 보낸다. 이것은 윈도우가 마침내 다시 열리게 되었을 때 그 전송 TCP 가 그 사실을 알도록 해준다.

두 번째 흥미있는 것은 "Nagle알고리즘"(키 입력 같은 작은 세그먼트, 예를 들어 만약 peer로 부터 ACK가 올거 같 으면 키 입력같은 것은 큰 세그먼트를 만들기 위해 지연된다. ;이것은 TCP_NODELAY로 무력하게 만든 것이다)

Vic 질문:

나의 tcpclient예제가 TCP_NODELAY로 하여금 EOL코드가 네트웍을 통해 전달 되었다는 것을 보증 해야함을 의미합니까?

아니다. tcpclient.c는 옳게 작동하고 있다. 가능한 많은 데이터를 write()하기 위한 가능한 적은 호출로 써라. 많은 양의 데 이터는 소켓 전송버퍼에 비해 상대적으로 작기 때문에 모든 요청은 오직 한 번의 write()로 충분하며, TCP 레이어는 즉시 그 요 청을 하나의 세그먼트로 떼어낸다.(PSH flag을 이용해서, 위의 2.2를 보라)

Nagle 알고리즘은 두 번째 write()이 데이터가 여전이 ack되지 않은 동안에 호출될 때 영향을 준다. 일반적인 경우에, 이 데 이터는 ack된 데이터가 없거나 full-size세그먼트를 떼어내는 것이 가능한 충분한 데이터가 있을 때까지 버퍼에 남아 있게 된다. 이 지연은 무한정 계속 될 수는 없는데 왜냐면, 재전송 타임아웃이 생기거나 또는 연결이 끊게 되면 조건이 참이 되기 때문이다 .

이 지연이 어떤 응용프로그램에 대해 부정적인 결과를 나타낼 때, 일반적으로 작은 요청의 스트림은 response없이 전송되어 진다. 예를 들어 마우스 움직임의 표준은 마우스 움직임을 불가능하게 하기 위한 옵션이 존재해야 한다고 하고 있다. [RFC1122, ss 4.2.3.4]

추가적인 기록: RFC1122 또한 이렇게 되어 있다.:

[토론]:
PUSH flag가 SEND호출에서 구현되지 않았을 때, 예를 들어 어플리케이션/TCP인터페이스가 순수한 스트림 모델을 사용할 때 어 떤 작은 데이터 조각들을 합리적인 크기의 세그먼트로 모우는 것에 대한 책임은 부분적으로 어플리케이션 레이어에 있다.

그래서 프로그램은 작은 데이터 길이(MSS에 상대적으로 작다는 의미)로 write()를 호출하는 것을 피해야 한다. 버퍼 내에서 요청을 형성 시키고 그런후에 sock_write()등과 같은 호출을 하는게 더 좋겠다.

다른 TCP에서 지연에 대한 원인은 프로그램에서 제어 할 수 있는 것이 아니며, 그들은 데이터를 임시 지연시킬 뿐이다.

Vic 질문:

임시적으로, 당신은 데이터가 갈수 있는 만큼 간다는 것을 의미하는가? 한쪽은 대답을 기다리고 한쪽은 요청을 받지도 않은 상태에서 이런 시점에 내가 더 이상 가만히 있을 수만은 없다. 적어도 이러고 영원히 기다릴 수는 없지 않은가?

당신은 어떻게든지 양쪽 방향의 모든 버퍼를 채우도록 하기위해 관리한다면.. 쉽지 않다. 데드락(deadlock)에 걸릴 수 밖에 없다.

다음은 가능하다.(좋은 예는 아니지만)
해결책은 비블록킹 모드를 사용하는 것이다. 특히 write를 위해. 그러면 필요에 따라 프로그램에서 버퍼를 넘치게 할 수 있다 .

2.12. 어디스 소켓에 관한 라이브러리를 구할 수 있는가?

Charles E. Campbell, Jr. PhD. 와 Terry McRoberts에 의해 만들어진 간단한 소켓 라이브러리가 있다. 이 파일은 ssl.tar.gz 이라고 불린다. 그리고 그것은 faq홈페이지에서 구할 수 있다. c++를 위해 socket++라이브러리가 있다. (ftp://ftp.virginia.edu /pub/socket++-1.10.tar.gz.) 또한 C++ Wrapper도 있다. ftp://ftp.huji.ac.il/pub/languages/C++/C++_wrappers.tar.gz.
이걸 알려준 Bill McKinnon에게 고맙다. http://www.cs.wustl.edu/~schmidt에 가면 여러분은 ACE 툴킷를 구할 수 있다.
PING소프트웨어그룹은 몇몇 소켓 라이브러리를 가지고 있다.
http://love.geology.yale.edu/~markl/ping.

이 라이브러리를 써본 경험은 없다. 그래서 뭐 다른 추천해줄만한 내용은 없다.

2.13. select는 데이터가 있다고 하는데 읽으면 0을 리턴한다?

select가 리턴하는 데이터는 EOF이다. 왜냐면 반대편이 연결을 끊었기 때문이다. 이러면 read는 0을 읽는다. 자세한 내용은 2 .1을 읽어봐라.

2.14. select()와 poll()의 차이점은?

Richard Stevens (rstevens@noao.edu):

기본적인 차이는 select()의 fd_set은 bit mast이고 그러므로 어떤 고정된 크기라는 점이다. 그것은 커널이 컴파일될 때 어플 리케이션이 원하는 크기로 FD_SETSIZE를 정해줄 수 있도록 이 크기를 제한하지 않는 것이 가능하다. 그러나 그렇게 하면 더 많을 일을 하게 된다. 4.4BSD의 커널과 솔라리스 라이브러리 함수둘은 이 한계를 가지고 있다. 그러나 나는 BSD/OS 2.1 이 이 한계를 피하고 있어서 프로그래밍에서는 약간의 수고만 하면 된다. 어떤 사람은 솔라리스의 버그리포트에 이것을 넣고 언제 이게 고쳐 지나~ 보는 경우도 있다.

그러나 poll()의 경우에 사용자는 pollfd 구조체 배열을 할당해야한다. 그리고 이 배열의 개수를 넘겨줘야 한다. 그래서 기본 적인 한계는 없다. Casper가 말하는 바에 의하면, select보다 poll()을 가지고 있는 시스템은 거의 없다고 한다. 그래서 후자(se lect())가 좀더 호환성이 있다라고 한다. 또한 원래 시스템(SVR3)에서는 pollfd구조체에있는 한 엔트리를 커널이 무시 하도록 하 기위해 디스크립터를 -1로 설정하는 것이 불가능 했었다. SVR4에서 이것이 해결 되었다. 개인적으로 나는 항상 select()를 사용 하며 poll()은 거의 사용하지 않는다. 왜냐하면 나는 나의 코드를 BSD환경에도 포팅하기 때문이다. 이러한 이유로 select()를 사 용하는 poll()을 구현할 수도 있겠다. 그러나 난 이런 사람은 한 번도 본적이 없다. 어쨋든 POSIX 1003.1g에서 이 둘은 표준으로 채택되었다.

2.15. 어떻게 [this]를 네트웍을 통해 보낼 수 있는가?

주의하지 않으면 1바이트의 데이터가 아니면 아마 산산조각날 수 있다. 정수형 숫자의 경우 htons()를 사용할 수 있고 1바이 트의 묶음들은 잘 전송된다. 포인터형 데이터를 문자열처럼 보내지 않도록 주의하라. 왜냐면 포인터는 다른 기계에서는 아무 의 미가 없기 때문이다. 만약 구조체를 보내는게 필요하면 sendthisstruct()를 전송하는데, 그리고 readthisstruct()를 수신하는함 수로 사용하라. 만약 실수를 보내야 한다면 많은 작업을 해야한다. RFC1014는 호환되는 방법으로 다른 기계에 데이터를 전송하는 방법에 대해 기술하고 있다.(이걸 알려준 Andrew Gabriel에게 감사를 표한다.)

2.16. TCP_NODELAY를 사용하는 방법은?

우선, 처음부터 이것을 써야 하는지에 대해 먼저 생각해 봐라. 이것은 Nagle알고리즘을 못쓰게 만들어 버릴 것이다.(2.11의 소켓 버퍼에 있는 내용을 강제로 전달 할 수 있는 방법은 없나? 부분을 읽어봐라) 이것은 작은 패킷들을 양산하여 네트웍 트래픽 을 높일 수 있다. 또한 내가 말해줄 수 있는 것은 속도 증가는 미미하다는 것이다. 그러므로 일단은 TCP_NODELAY를 먼저 고려해 보고 그래도 안되면 써야 한다.

코드 예제가 여기 있다. Andrew Gierth의 이것의 사용에 대한 경고 메시지와 함께
Gierth:

int flag = 1;
int result = setsockopt(sock,       /* 영향받을 소켓 */
                       IPPROTO_TCP, /* TCP레벨에 옵션은 선택 */
                       TCP_NODELAY, /* 옵션이름 */
                       (char *) &flag, /* 이 경우는 역사적인 cruft 이다 */

sizeof(int)); /* 옵션값의 길이 */
if (result < 0)
   ... handle the error ...

TCP_NODELAY는 Nagle 버퍼링 알고리즘을 중지시킬 특별한 목적으로 사용된다. 이것은 전송은 작은 단위로 자주 이루어 지지만 즉각적인 응답은 필요 없는 어플리케이션에서만 사용 되어야 한다.(마우스 움직임 같은)

2.17. Nagle 알고리즘이 도데체 뭐하는 건가?

접 속의 다른 끝으로부터 ACK사이의 가능한한 많은 데이터를 한데 묶는 것을 말한다. Andrew Gierth (andrew@erlenstar.demon .co.uk)가 아래의 그림을 통해서 설명한 것을 보면 쉽게 이해할 수 있을 것이다.
설명:

이 그림은 완벽한건 아니다. 단지 잘 설명하기 위한것일 뿐이다.

첫 번째 경우: 클라이언트는 한 번의 write()에 1바이트를 write한다. B 호스트에서 이 프로그램은 FAQ 예제의 tcpserver.c 이다.

 

        CLIENT                                  SERVER
  APP             TCP                     TCP             APP
                  [연결 설정의 제거됨]

   "h" --------->          [1 byte]
                      ------------------>
                                             -----------> "h"
                                     [ack 지연됨]
   "e" ---------> [Nagle alg.              .
                   효과를 나타냄]          .
   "l" ---------> [ditto]                  .
   "l" ---------> [ditto]                  .
   "o" ---------> [ditto]                  .
   "\n"---------> [ditto]                  .
                                           .
                                           .
                         [ack 1 byte]
                      <------------------
                  [큐에 쌓인 데이터를    
                  보냄]
                          [5 bytes]
                      ------------------>
                                            ------------> "ello\n"
                                            <------------ "HELLO\n"
                     [6 bytes, ack 5 bytes]
                      <------------------
   "HELLO\n" <----
                [ack 지연됨]
                   .
                   .
                   .   [ack 6 bytes]
                      ------------------>

총 세그먼트: 5. (만약 TCP_NODELAY 가 설정되었다면, 10까지 증가될 수 있다.)
응답시간: 2*RTT에 ack 지연시간을 더한 합.

두 번째 경우: 클라이언트는 한 번의 write()요청에 모든 데이터를 write한다.

             CLIENT                                  SERVER
       APP             TCP                     TCP             APP
                       [연결 설정의 제거됨]

        "hello\n" --->          [6 bytes]
                           ------------------>
                                                 ------------> "hello\n"
                                                 <------------ "HELLO\n"
                          [6 bytes, ack 6 bytes]
                           <------------------
        "HELLO\n" <----
                   [ack 지연됨]
                        .
                        .
                        .   [ack 6 bytes]
                           ------------------>

총 세그먼트: 3.

응답시간 = RTT (최소시간이 걸린다).

이 그림이 좀 쉽게 알아보는 계기가 됐음 한다.

두 번째 경우에서, 데이터를 전송하는데 필요없는 지연이 구현되길 바라지는 않을 것이다. 응답시간에 곧바로 더해진다면.

2.18. read()와 recv의 차이점은?

Andrew Gierth (andrew@erlenstar.demon.co.uk):

read()는 파라메터 flag로 0을 넘겨주면 recv()와 같게 동작한다. flag파라미터에 대한 수를 넣으면 recv()는 다르게 동작한 다. 마찬가지로 write()도 flag가 0이면 send()와 같게 동작한다.

이것은 send()와 recv()가 drop될 수 있다는 점에서 다르다. 아마 POSIX드래피트의 복사본을 가지고 있는 사람은 이것을 확인 해 볼 수 있을 것이다.

호환노트: unix가 아닌 시스템은 read()/write()가 소켓에서 동작하는 것을 허용하지 않을 것이다. 그러나 recv()/send()는 보통 허용된다. 보통 Window와 OS/2에서 가능하다.

2.19. send()/write()가 SIGPIPE 신호를 생성할 수 있던데. 이 신호를 무시하거나 EPIPE오류에 대한 검사를 하지 않고 다른 처리를 했을때의 어떤 잇점이 있는가? 시그널을 잡는 잡는 함수 에 넘겨지는 어떤 유용한 파라미터가 있는가?

Andrew Gierth (andrew@erlenstar.demon.co.uk)가 말하기를:

일반적으로 시그널 핸들러에 전달되는 유일한 파라메터는 그것이 구동되도록 만드는 시그널 번호번호이다. 몇몇 시스템은 추 가 선택사항 파라미터를 가지고 있지만 이 경우에 사용되지 않는다.

나의 충고는 단지 여러분이 제안한 것처럼 SIGPIPE를 무시하라는 것이다. 그것은 나의 소켓코드에서 할 수 있는 것이다. erro r값은 시그널보다 다루기 쉽다(사실, FAQ의 첫 번째 판은 이러한 전후 관계를 가지고 SIGPIPE를 설명하는데 실패했다. 그래서 뭐 난 그것을 잊기로 했다~)

SIGPIPE를 무시해서는 안되는 한가지 상황이 있다; 만약 여러분이 소켓으로 redirect되는 stdout를 가진 다른 프로그램을 exe c() 하려고 한다면, 이 경우 exec()하기 전에 SIGPIPE를 SIG_DFL로 설정하는게 현명할 것이다.

2.20. chroot()실행후에 socket() 호출이 실패했다. 왜 그런가?

Andrew Gierth (andrew@erlenstar.demon.co.uk):

소켓이 스트림의 최상위에서 구현된 시스템(예를 들어 시스템V기반의 시스템, 솔라리스등)에서 socket()함수는 실제로 /dev에 의 특수파일을 여는 것으로 socket()이 실행된다. 그러므로 chroot()를 실행하기 전에 그 root디렉토리에 /dev/디렉토리를 만들 어 줘야 하며, 필요한 디바이스 장치들도 만들어 줘야 한다(오직 필요한 것만).

시스템 매뉴얼에 그 필요한 디바이스 장치를 지정하고 있을 것이다. (편집자노트: Adrian Hall (adrian@waltham.harvard.net) 은 ftpd 매뉴얼 페이지에 있는 부분을 참조하라고 권한다. 거기에 있는 /dev 장치들을 사용할 수 있을 것이다.

좀 관련이 적은 얘기지만, 많은 daemon들이 그렇게 하듯이 syslog()함수를 호출 했을 경우 syslog()는 UDP와 소켓, FIFO또는 유닉스도메인 소켓을 열게 된다. 그래서 만약 여러분들이 chroot() 실행 후에 그것을 사용할 경우 chroot()전에 openlog()를 호 출하는 것을 잊지 말기 바란다.

2.21. 왜 소켓 호출로부터 EINTR가 오는 것을 계속 기다리고 있어야 하는가?

exit 조건에서 처럼 이건 그렇게 많은 문제가 있는건 아니다. 이것은 그 호출이 시그널에 의해 중지될 수 있음을 의미한다. 블록될수 있는 모든 호출은 EINTR를 검사하는 루틴으로 싸여 있어야 한다. 예제코드가 그것을 보여준다(6. 예제코드를 보라)

2.22. 언제 내 프로그램이 SIGPIP 시그널을 받게 되는가?

Richard Stevens (rstevens@noao.edu):

매우 간단하다. TCP를 사용할 경우 여러분의 연결(connection)이 다른 쪽으로부터 RST를 받게 되면 여러분은 SIGPIPE를 받게 된다. 이것은 RST가 여러분들이 읽도록 거기에 존재하고 있기 때문에 만약 write() 대신에 select를 사용한다면, 그 select는 읽 기 가능한 소켓을 지정하게 될 것이다.(read는 errno에 ECONNRESET을 설정한 오류코드를 리턴할 것이다.)

기본적으로 RST는 더 이상 다룰 수 없거나 그럴것으로 예상되는 어떤 패킷에 대한 TCP의 응답니다. 일반적인 경우는 peer가 연결(여러분에게 FIN을 보내는)을 끊을때이다. 그러나 여러분이 쓰고있고 읽고 있지는 않을 경우 여러분은 이것을 무시한다. (여 러분은select를 사용해야 한다.)
그러므로 여러분은 반대편에 의해 끊긴 연결과 다른쪽은 RST를 가진 TCP응답에 write 한다.

2.23. 소켓 예외(exception)는 있는가? out-of-band데이타란 무엇인가?

C++에서의 예외와는 다르게, 소켓 예외는 오류가 발생했다는 것을 지칭하지는 않는다. 소켓 예외는 보통 out-of-bind데이터가 도착 했다는 것을 알려 주는 것이다. out-of-band데이터( TCP에서 "긴급데이터(urgent data)"라고 불리우는)는 주 데 이터 스트림으로부터의 분리된 스트림과 같이 응용프로그램에 의지한다. 이것은 분리된 두가지 종류의 데이터의 경우에 유용하다 . "긴급데이타(urgent data)"라는 것은 in-band데이타 스트림보타 중요성이 더 크다거나, 더 빠른 전송이 요구된다는 것을 뜻하는 건 아니다라는 것을 알아두라. 그리고 또한 주 데이터 스트림과는 다르게 out-of-band데이터는 여러분의 어플리케이 션이 그것을 유지하지 않을 경우 상실되게 된다는 것을 알아둬야 한다.

2.24. 어떻게 완전한 내 시스템의 호스트이름인 (FQDN)을 얻을 수 있는가?

Richard Stevens (rstevens@noao.edu):

몇몇 시스템은 호스트 이름을 FQDN으로 설정하고 어떤 다른 시스템들은 단지 unqualified된 호스트 이름을 그냥 사용하기도 한다. 현재 BIND FAQ는 FQDN를 사용하도록 추천하고 있지만 예를 들어, 대부분의 솔라리스 시스템들은 unqualified된 호스트 이 름을 사용한다.

그럼에도 불구하고, 이 방법은 처음 호스트 이름을 알아내는 방법이다. 대부분의 시스템은 uname()이라는 posix의 방법을 지 원한다. 그러나 오래된 BSD시스템은 단지 gethostname()을 지원하기도 한다. 여러분의 IP 주소를 얻으려면 gethostbyname()을 사 용하라. 그리고 그 IP주소를 가지고 gethostbyaddr()를 사용하라. 그러면 hostent{}의 h_name에 여러분이 원하는 FQDN이 들어 있 을 것이다.