TCP에서는 연결과정보다 중요한 것이 중료과정이다. 연결과정에서는 큰 변수가 발생하지 않지만 종료과정에서는 예상치 못한 일이 발생할 수 있기 때문이다. 따라서 종료과정은 명확해야 한다.
리눅스의 close 함수호출과 윈도우의 closesocket 함수호출은 완전종료를 의미한다. 완전종료라는 것은 데이터를 전송하는 것은 물론이거니와 수신하는 것 조차 더 이상 불가능한 상황을 의미한다.
때문에 한쪽에서의 일방적인 close 또는 closesocket 함수호출은 경우에 따라서 우아해 보이지 못할 수 있다.

위 그림은 양방향을 통신하고 있는 두 호스트의 상황을 묘사한 것이다.
호스트 A가 마지막 데이터를 전송하고 나서 close 함수의 호출을 통해서 연결을 종료하였다. 때문에 그 이후부터 호스트 A는 호스트 B가 전송하는 데이터를 수신하지 못한다. 아니! 데이터 수신과 관련돤 함수의 호출 자체가 불가능하다. 때문에 결국엔 호스트 B가 전송한, 호스트 A가 반드시 수신해야 할 데이터라 할지라도 그냥 소멸되고 만다. 이러한 상황을 해결하기 위해서 Half-close 가 제공된다.
소켓을 통해서 두 호스트가 연결되면, 그 다음부터는 상호간에 데이터의 송수신이 가능한 상태가 된다. 그리고 바로 이러한 상태를 가리켜 '스트림이 형성된 상태'라 한다. 즉, 두 소켓이 연결되어서 데이터의 송수신이 가능한 상태를 일종의 스트림으로 보는 것이다.
스트림은 물의 흐름을 의미한다. 그런데 물의 흐름은 한 쪽 방향으로만 형성된다. 마찬가지로 소켓의 스트림 역시 한쪽 방향으로만 데이터의 이동이 가능하기 때문에 양방향 통신을 위해서는 두 개의 스트림이 필요하다.

때문에 두 호스트간에 소켓이 연결되면, 각 호스트 별로 입력 스트림과 출력 스트림이 형성된다.
우아한 종료라는 것은 이 두 스트림을 모두 끊는 것이 아니라, 이 중 하나의 스트림만 끊는 것이다. 물론 리눅스의 close 함수와 윈도우의 closesocket 함수는 두 가지 스트림을 동시에 끊어버리기 때문에 우아한 연결종료와는 거리가 멀다.
소켓이 종료되면, 입력 버퍼에 있는 내용은 그대로 남아있고, 출력 버퍼에 있는 내용은 마저 상대에게 전송된다.
그럼 이제 우아한 종료 즉, Half-close에 사용되는 함수를 소개하겠다. 다음 shutdown 함수가 스트림의 일부를 종료하는데 사용되는 함수이다.
#include <sys/socket.h>
int shutdown(int sock, int howto);
// 성공 시 0, 실패 시 -1 반환
sock: 종료할 소켓의 파일 디스크립터 전달howto: 종료방법에 대한 정보 전달위의 함수호출 시 두 번째 매개변수에 전달되는 인자에 따라서 종료의 방법이 결정된다. 두 번째 매개변수에 전달될 수 있는 인자의 종류는 다음과 같다.
| howto 매개변수 | 의미 |
|---|---|
| SHUT_RD | 입력 스트림 종료 |
| SHUT_WR | 출력 스트림 종료 |
| SHUT_RDWR | 입출력 스트림 종료 |
Half-close가 필요한 이유
출력 스트림을 종료하여 상대에게 EOF를 보내고, 받을 데이터가 남아있을 떄 유용하다.