rein's world

TCP 기반의 소켓 통신 101

좀 더 명확히 하자면 production level code 101 이라고 해야겠지만.1

  • TCP가 믿을 수 있다고(reliable)하지만 이건 “연결이 살아있으면 언젠가는 전송될 수도 있다” 란 의미다.2 허술한 추상화에 속지 말 것
  • TCP 소켓에 send를 했을 때, 해당 함수 호출로만 전부다 전송될 거라고 절대로 믿지 말아라 — 반환값을 확인하고 적절한 처리를 해줘라3
  • TCP 소켓에서 recv를 했을 때는 반드시 버퍼링을 해야한다 — 절대로 특정 길이만큼은 올거라고 가정하지 말아라. 특정 길이보다 길게는 안 올거라고 믿어서도 안된다

얼마 전에 세번째 항목이 고려되지 않은 코드를 봐야했는데, 참 보고있기 괴로웠다.

recv를 하면 크게 세 가지 경우가 나온다

  • (즐겁게도) 정확히 응용 프로그램에서 사용하는 단위로 메시지가 왔을 때 — 단순히 상위단에서 처리하게 해주면 끝
  • (불행히도) 응용 프로그램에서 필요한 길이보다 짧게 왔을 때 — 적당히 보관해주고 다음 TCP 세그먼트4가 오는 것을 기다려야한다
  • (역시 불행스럽게도) 응용 프로그램에서 사용하는 것보다 긴 길이가 왔다면, 메시지를 하나 처리하고 다음 메시지 처리를 이 세 가지 경우에 맞춰서 다시 처리해야한다

길이의 가정을 하지 말아야하는 이유가 여기서도 마지막 항목 때문. 응용 프로그램에서 사용하는 메시지 길이가 짧다고 — 특히나 path MTU보다 작다고 — 그 메시지가 한꺼번에 올 수 있는 것은 아니다. Path MTU가 1000 bytes 메시지가 300 bytes 고라고 해도 메시지 4개가 사이좋게 뭉쳐서 오면, 4번째 메시지는 100 bytes만 버퍼에 남게 된다.

결국 최소한으로 잡아도 이런 메시지 처리 코드가 나오게 된다.

  1. recv() 로 TCP 소켓에서 데이터를 받는다
  2. 데이터 길이가 응용 프로그램 메시지 길이와 비교.
  3. 필요한 것보다 짧으면 남은 데이터를 적당한 버퍼에 보관하고 다시 1로
  4. (응용 프로그램에서 메시지 처)
  5. 4에서 처리 후 남은 부분의 길이를 받아온다.
  6. 다시 2로

이렇게 하면 위에서 설명한 세 가지 경우가 대부분5 처리 된다.

결론적으로 말하면,

TCP가 보장하는 부분은 매우 적다. TCP 세그먼트를 받아서 필요한 크기로 재조립할 생각을 하자

ps. 101 이긴 하지만 좀 추상적인 내용이다. 그래도 이게 네트웍 프로그래밍으로 toy program이 아닌 물건을 만드려면 필요한 “최소한"의 지식이라고 생각해서 끄적여본다.


  1. 여기에서 101은 어떤 주제에 대한 기초적이거나 개괄적으로 설명하는 것을 의미한다. 그래서 흔히 신입생을 위한 강좌를 과목명 101 처럼 쓰기도 한다 — from wikipdia 101 ↩︎

  2. 물론 전송 순서, 약한 수준의(하위 레이어까지 합쳐지면 충분히 안정적이지만) 데이터 일관성 보장, 그리고 중복 전송이 없기는 하다. ↩︎

  3. 물론 특정 OS에서 성공 or 실패만 있는 형태로 포장해서 제공해주기도 한다. eg. Win32의 IOCP의 Send 라거나… ↩︎

  4. 혼란스럽게도 네트웍의 각 레이어에서 사용하는 데이터 전송단위에 대한 용어는 모두 다르다. 데이터링크 레이어에선 흔히 “frame” 이라고 부르고, IP에선 “packet”, UDP에선 “datagram” 이라고 부른다. 물론 몽땅 뭉뚱그려 부르는 PDU;Payload Data Unit 같은 용어도 있긴하지만. ↩︎

  5. 전부가 아닌 이유는 recv() 자체가 system-call에 의해 중단되거나, 연결 자체가 끊기거나하는 것 정도는 일단 시작이고, 생각할 수 없던 별별 일이 다 생기는게 네트웍이라 예외 경우를 다 쓰게되면 101이 아닐 것이다. ↩︎