kakasoo

[TCP/IP] TCP 기반의 Half-close 본문

프로그래밍/네트워크

[TCP/IP] TCP 기반의 Half-close

카카수(kakasoo) 2020. 7. 17. 17:34
반응형

음, 해보다 안 것인데, server 하나에 client 여러 개를 연결할 수가 없다. 이게 아마, 프로세스와 스레드가 필요한 영역.

문제가 된 요소를 몇 개 발견했는데,

client 2개가 있다고 하자, 하나는 A, 하나는 B라고 명명할 때 A를 서버에 연결했다.

이 상태로 연결이 잘 되고 있다고 할 때, B도 서버에 연결해보았다, 그런데 B는 전혀 동작하지 않는다.

이 서버는 동시성이 없기 때문이다.

그런데 B에서 메세지를 보낸다, 당연히 처리되지 않는다.

A에서 서버와 연결을 종료한다, 그럼 B에서 동작해야 할 거 같지만, 앞서 B가 보낸 메세지는 씹혔다, 서버는 계속 B의 메세지를 기다리고 있는데 B는 이미 다음 단계로 넘어가서 메세지를 보낼 수가 없다.

동시성만이 이 문제를 해결할 수 있는 듯 하다.

그러나 이런 내용을 배우기 전에, 일단 다른 내용부터 정리하고 넘어가자.

 

"TCP에서는 연결과정보다 중요한 것이 종료과정이다. 연결과정에서는 큰 변수가 발생하지 않지만, 종료 과정에서는 예상치 못한 일이 발생할 수 있기 때문이다. 따라서 종료과정은 명확해야 한다. 이번에 설명하는 Half-close는 명확한 종료를 위해 반드시 알아야 할 내용이다." - 윤성우의 열혈 TCP/IP 소켓 프로그래밍 170p

(살려주세요, 이건 뭐 라이브러리도 하나 없고 하나 하나 전부 구현해야 하는 수준 아닙니까, 선생님.)

 

소켓과 스트림

스트림이란 두 소켓 간에 존재하는 데이터의 흐름이다.

스트림이 있는 경우, 두 소켓은 연결되었다고 말하고, 데이터 송수신이 가능한 상태라고 말한다.

그런데 이 흐름은 한쪽으로만 가능하기 때문에, 두 소켓 사이에는 반드시 두 개의 스트림이 존재하게 된다.

가령 A의 출력 버퍼는 입출력 스트림을 타고 상대(B)의 입력 버퍼로 가게 될 것이고,

상대 측의 출력 버퍼는 다시 A의 입력 버퍼로 가는 꼴이다.

문제는 다음과 같은 상황에서 시작된다.

 

양방향으로 데이터를 송수신하는 두 호스트가 존재한다고 가정한다.

이 호스트들은, 데이터를 주고 받고 있는 와중인데, A가 전송을 모두 마친 후 close() (== closesocket) 해버렸다.

자기가 할 말 하고 딱 카톡방 나간 상태인데, 만약 A가 꼭 봐야 하는 메세지를 B가 보냈다면 어떡할 것인가?

B가 전송한 메세지는 영영 소실된다.

이런 상황을 해결하기 위해서 소켓을 닫을 때, 전송만 닫거나 또는 수신만 닫는 식으로, 스트림을 반만 닫는 게 있다.

 

이것이 필요한 까닭을 이해하기 위해서 다음의 논리를 따라 가보도록 하자.

 

  • Q1) half-close가 있을 필요가 있는가?
  • A1) 그렇다, 위에서 말한 것처럼 B의 데이터가 영영 소실되는 경우를 방지하기 위해서라도 half-close가 필요하다.
  • Q2) 그냥 충분히 시간을 둔 다음에 종료하면 되는 것 아닌가?
  • A2) 안 된다, 충분한 시간이라는 말이 모호한데, 연결 종료 직전에 데이터를 보낼 일이 생길 수도 있지 않은가.
  • (* 데이터의 전송보다 close() 가 빠를 경우 문제가 생긴다.)
  • Q3) 그렇다면, 서로 주고받을 메세지가 더 있는지 확인한 다음, 없는 경우에 종료하면 되지 않은가?
  • A3) 서로 주고받을 메세지가 남아있는지 확인하려면 특정한 값을 신호로 주고 받아야 하는데, 그 문자는 어쩔건가?
  • Q4) 그렇다면 이 문자를 해결하기 위해서, 문자 대신에 EOF를 반환하면 어떻겠는가?
  • A4) 하지만 한 쪽(서버)에서 종료하고 EOF를 준다고 치자, 받은 쪽에서도 EOF(또는 EOF에 대한 반환값, 지정된 상수)를 줘야 하는데, 이미 한쪽(서버)는 문을 닫아버렸다, 그럼 결국 처음 문제랑 같지 않은가?

결국에는 위와 같은 흐름을 따라가서 Half-close가 필요할 수 밖에 없게 된다.

 

  • int shutdown(SOCKET sock, int howto);
  • 두번째 매개변수로, SD_RECEIVE, SD_SEND, SD_BOTH를 넣어 입력, 출력, 입출력 중 종료할 스트림을 고를 수 있다.

이 함수가 바로, 스트림을 반만 닫게 해주는 함수이다.  (둘 다 가능하기도 하지만, 그럴 거면 그냥 closesocket()과 같다.)

코드는 생략하고 다음으로 넘어가겠다.

 

 

반응형