본문 바로가기
카테고리 없음

TCP는 Stream프로토콜이지 Message프로토콜이 아니다.

by Joseph.Lee 2017. 11. 14.

송신측

char data[2048] = "Some datas...";

send(fd, data, 2048, 0);


수신측

char data[2048];

recv(fd, data, 2048, 0);


이런 간단한 코드가 있을 때...

과연 데이터가 정상적으로 2048바이트의 데이터를 받을 수 있을까?


정답은 No(일 수 있다)이다.

fragment발생 시 한번의 recv에 모든 데이터를 받지 못할 수 있다.

recv(fd, data, 2048, 0); 에서 실제로는 1000바이트만 받을 수도 있다.


(1) 그래서 보통 패킷전송을 할 때 Header을 앞에 붙여서 전송한다.

제일 간단한 헤더라면 데이터의 길이를 앞에 붙이는 방식이다.

이런 식으로 전송하고 받는 쪽에서는 실제 데이터길이만큼 받을떄까지 반복해서 recv을 해야 한다.


적게 받는거 뿐만 아니라 많이 받을수도 있다.

만약 128바이트를 3번에 연속해서 보냈으면 받는 쪽에서 위와같이 recv(fd, data, 2048, 0)을 했으면

128 * 3 = 384바이트를 한번에 받을 수 있단 것이다.

이런 경우가 가장 많을거 같다.

무식하게는 의도적으로 전송시 sleep을 넣어서 커널에서 데이터를 보내는 시간을 벌고 전송하는 방법도 있지만..

무식하고 비효율적인 방법이다.


물론 이런 방법이 문제를 100%해결하는건 아니다.

(2) 만약 데이터가 3개로 fragment되었을 때 2번쨰 패킷이 유실된다면

(TCP가 신뢰성이 있는 통신이라고 하지만 OS구현 특성상 패킷 유실이 가능하다.

send 함수의 경우 실제로 데이터를 전송하고 확인하는 것이 아니라 send buffer에 데이터를 넣어놓고 함수를 리턴하며,

커널에서 데이터 전송을 담당한다. 따라서 데이터가 진짜로 정상적으로 전송되었는지는 알 수 없다.

이에 대한 해결방법으로는 MSG_DONTWAIT가 있는데 100% 모든 상황에 대처할 수 있는지는 아직 잘 모르겠다.)

정상적인 데이터 수신을 하지 못하고 구현방식에 따라서는 프로그램이 멈출 수 있다.

혹은 1번째 패킷의 전송이 실패하면 아에 데이터 size을 잘못 알아올 수도 있다.


때로는 이런걸 고려하지 않은 (예제는 뭐... 넘치고..) 프로그램들이 있다.

당장은 정상적으로 작동하는거 같아도 나중에 문제가 생기면 서버프로그램 자체가 죽을수도 있다..

(1)같은 상황만 있어도 심각한 문제는 발생하지 않을 것이다.

(2)같은 상황은 귀찮으면 걍 연결을 끊어버리는;; 방법이 있다.


Stream과 Message는 정말 큰 차이가 있다는 것을 유의해야 한다.

이러한 것은 libev같은걸로는 해결되지 않는 문제이니.. 잘 신경쓰거나 비싼 라이브러리를 써야 한다.

반응형

댓글