삽질의 현장/- 네트워크 프로그래밍

#014_Window_Network_고정 길이 데이터 전송

shovelman 2015. 10. 5. 20:04


안녕하세요 삽잡이입니다.


이번 시간에는 다양한 데이터 전송 방식중에

'고정 길이 데이터 전송'에 대해서 알아보려고합니다.


클라이언트와 서버간에 데이터를 주고받을 때... 

즉, 통신할 때 어플리케이션 버퍼에 데이터를 입력받고 

send 함수를 통해 TCP send 버퍼에 복사해왔었습니다.

반대로, recv 함수를 통해 TCP recv 버퍼에서 데이터를 받아와

어플리케이션 버퍼에 저장을 받으며 통신을 진행했었습니다.


이렇게 3바이트를 보냈다면 3바이트를 전송받고 

7바이트를 보내면 7바이트를 받으며 통신을 진행할 것이라는 소리입니다.


이와 같이 데이터를 주고 받을 때 주의해야할 사항이 있습니다.

TCP 방식을 사용해야할 때인데,

TCP는 stream 방식을 사용합니다. UDP는 반대로 Datagram 방식을 사용하지요...

아래는 TCP와 UDP의 특징을 나타낸 표입니다.


 

우리가 TCP 통신을 하며 가장 밀접한 연관성을 가지고 있는 특징은

바로 데이터의 경계가 없고 연속적이라는 특징입니다.

이 특징들은 TCP 특징에 대한 기본 중요 사항입니다.

1 대 1 통신이라는 것은 연결되어있는 놈들 끼리만 데이터를 주고 받음을 통해 확인할 수 있습니다.


또한, TCP 통신 자체가 OS 레벨에서 데이터를 보내면

내 send 버퍼에 있는 내용물이 recv 버퍼에 들어가는 것 까지 OS에서 보장을 해줍니다.

그리고 OS레벨에서 데이터를 보내면 데이터를 받았다는 응답을 다시 보내게 됩니다.

보낸 측에서는 제대로 받았다는 패킷이 오기 전까지 send 버퍼의 내용물을 지우지 않다가

해당 패킷을 확인하고 OS가 버퍼의 내용을 지웁니다.

따라서 TCP 통신은 신뢰성이 있다고 하는 것입니다.


TCP 프로그램에서 오류를 가장 많이 내는 케이스가 

바로, '데이터 경계가 없다'는 곳 부터 나옵니다.


TCP 에서는 데이터를 보내는 방식이 크게 3가지입니다.

1. 고정 길이

2. 가변 길이

3. 끝을 표식하는 방법


UDP는 이와 같은 방식이 필요없습니다.

왜냐하면 데이터의 경계가 나눠져있기 때문입니다.


TCP는 데이터의 경계가 없다는말이 무슨 말이냐하면,

서버에 recv 버퍼와 클라이언트의 send 버퍼가 있다고 했을 때,

'1234'라는 데이터를 날린다고 해보겠습니다.

이 때에 '1234'라는 데이터가 어떠한 OS의 처리에 의해서 

send에서 '12'가 먼저 날라가고 '34'가 나중에 날라갈 수도 있다 이겁니다....


send함수의 역할은 데이터를 복사해오는 것입니다.

그런데, TCP 레벨에서는 데이터 '1234'가 꼭 똑같이 날라간다는 보장이 없지요...

recv 버퍼 수준에서는 '12'가 먼저 도착했으면

recv 함수가 복사를 하고 return 할 것 아닙니까...

'1234'를 send 에서 한꺼번에 보냈으나 recv 함수는 '12'만을 먼저 받고 복사할 수 있다 이겁니다.

그리고 다시 recv 함수가 recv 버퍼를 봤을 때 '34'가 있다면 해당 데이터를 복사하겠지요...


그렇다면 조금 더 생각해보겠습니다.

'1234'를 보냈는데 또 '56'이 send 버퍼측에서 날라왔다고 해보지요...

'56'도 recv 버퍼에 도착해버렸는데 recv 함수가 이때 복사를 하러 오면 어떻게 하겠느냐 이겁니다.

보낼 때에는 따로 따로 보냈지만, 도착이 같이 된 경우를 말한 것이지요...

recv 함수가 데이터를 한꺼번에 가져갈 수도 있습니다.

이것이 바로 데이터의 경계가 없다고 말하는 것입니다.


따라서, 데이터의 경계를 어플리케이션이 만들어줘야합니다.

즉, TCP 레벨에서는 데이터의 경계가 없기 때문에

내가 만들어줘야한다는 것입니다.


간단하게 만들 수 있는 방법은 아래의 코드같이 만들 수가 있습니다.



recv 함수는 블록 함수입니다. 

곧 recv 함수는 내가 데이터를 받을 때 까지 block 상태에 놓이게 된다 이겁니다.

그리고 데이터를 몇바이트를 받을지 모릅니다.

이때 위와 같은 함수를 구현하는 것입니다....

(recvn에서 n은 n바이트를 받을 때 까지 실행하라는 뜻입니다...)


위의 코드를 살펴보면, 

내가 원하는 바이트수를 채울 때 까지 recv를 반복적으로 처리합니다.


무조건 TCP 방식에서는 이렇게 함수를 만들어서 사용을 하라는 뜻은 아닙니다.

위의 함수는 고정 길이를 받을 수 있도록 만든 함수인데,

데이터를 주고 받는 방식이 고정 길이 방식을 사용할 수도 있다는 것을 보여드린 것입니다.


무조건 고정 길이 만큼을 받기 위해 recvn이라는 이름의 함수를 만든 것이지,

모든 모델 방식에서나 사용할 수 있는 함수가 아니라는 뜻입니다.


아무튼...

이와 같은 함수 방법은 '쓰레드 모델'에서만 쓸 수 있습니다.

다른 callback, I/O와 같은 메커니즘에서는 사용을 못합니다...

오직 쓰레드 모델에서만 쓰는 것이니 참고하시길 바랍니다...


몇 바이트가 들어오던지 원하는 바이트 수만큼(len) 받고 싶을 때,

내가 원하는 바이트 수만큼 받을 것이라고 '고정 길이'라고 하는 것입니다.


위와 같이 구현을 하게 되면

예를 들어 10 바이트씩 받고 싶다면 recv 버퍼에 몇 바이트가 있어도 

10 바이트 씩만 어플리케이션 버퍼에 보내게 됩니다.

즉, 내가 원하는 바이트 수 만큼 받고 싶다면 위 처럼 구현하면 되는 것입니다.


이와 같이 구현을 해놓으면 10 바이트보다 더 큰 바이트가 들어와도 정해진 크기만큼만 받고 

recvn 함수는 return 하게 될 것입니다.

남은 바이트는 recv 버퍼에 남아있다가 다음 데이터로 복사해 올라가겠지요...


여러 작업이 동시에 수행될 때에는 힘든 코드일 수 있습니다...

아무튼... 이번 시간은 여기까지...


이상 삽잡이였습니다!


<코드 출처 : TCP/IP 윈도우 소켓 프로그래밍>