강의

멘토링

로드맵

Inflearn brand logo image

인프런 커뮤니티 질문&답변

SIsB님의 프로필 이미지
SIsB

작성한 질문수

[C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버

TCP vs UDP

TCP 프로토콜에 대해 더 궁금한 점이 있습니다.

작성

·

525

0

Send 2번을 보낸 패킷을 Recv 한번의 호출로 수신버퍼를 읽어 2개를 같이 읽을 수 있는 부분은 이해가 가는데요
이해되지 않는 부분이 패킷이 분할되어 올 수 있다는 부분입니다.

1. 송신자의 입장에서 수신 측의 윈도우 사이즈를 받아 하나의 큰 패킷을 분할하여 보내는 경우, 수신측의 어플리케이션 계층에서 Recv를 통해 분할된 패킷의 바이트를 받을 수 있기 때문인가요?

2. 위의 경우가 Yes라면 수신자에서 받은 패킷이 송신자에서 보낸 분할된 패킷중 순서상 뒤에 해당하는 분할패킷을 먼저 받게된 경우 어플레케이션 계층에서 수신버퍼를 읽을 수 있는 부분이 이해가 안됩니다. 수신측은 TCP 계층에서 분할된 패킷이라는 것을 알고 있고, 앞부분이 비어있는 상태라 다시 보내달라 요청하게될 것 같은데요 이 경우 TCP단에서 분할된 패킷이 오기까지 기다리면서 어플리케이션 계층의 수신버퍼에서 읽을 수 없도록 처리가 되나요?

3. 수신자 입장에서 발신자가 보낸 패킷의 일부분만을 받을 수 있다는 부분을 명확히 알고싶습니다. 윈도우의 사이즈로 인해 발신자가 패킷을 쪼게 보내기 때문에 수신자 입장에서 일부분의 패킷이 올 수 있다는 의미이실까요? 그렇다면 앞부분이 비어있는 패킷이 온 경우 어플리케이션에서 해당 부분을 수신버퍼에서 읽을 수 없는건가요? 수신측에서 분할된 패킷을 모두 받고 어플리케이션 계층에서 읽을 수 있도록 데이터를 올려주나요?

답변 1

4

Rookiss님의 프로필 이미지
Rookiss
지식공유자

여러가지 개념을 혼동하시는거 같은데
그냥 단순하게 생각하면 됩니다.
소켓통신을 하면 양쪽에 소켓을 만들게 되고,
그러면 내부적으로 송신/수신 커널 버퍼가 생깁니다.

Client          <->       Server
[Send]                   [Send]  << 커널
[Recv]                    [Recv]  << 커널

우리가 유저레벨에서 Send를 호출해서 성공했다는 것은,
사실은 상대방한테 데이터를 전달까지 완료했다는 것은 아니고
단순히 [우리의 커널 SendBuffer에 데이터를 복사한 것]을 의미합니다.
반대로 Recv를 호출해서 성공했다는 것은,
[커널 RecvBuffer에 있는 데이터를 1바이트라도 유저레벨로 복사한 것]을 의미합니다.
만약 우리가 Recv를 호출하지 않아 우리의 커널 RecvBuffer가 꽉 차면,
상대방은 더 이상 데이터를 보낼 수가 없습니다. (TCP 차원에서 조절)

그리고 무엇보다 TCP는 경계의 개념이 없습니다.
즉 유저레벨에서 '하나의 큰 패킷'을 보냈다고 해서
그것을 커널에서 정말 '하나'로 인식하지 않습니다.

송신자 쪽에서는 궁극적으로 자신의 커널 SendBuffer에 쌓인 데이터를
상대방의 RecvBuffer로 전송해야 합니다.
그런데 TCP 특성상 최대한 효율적으로 보내려고 노력하는데,
어차피 못받는 상황에서 데이터를 보내봤자 의미가 없으니
상대방의 커널 RecvBuffer 상황에 따라 보내는 데이터 양을 조절합니다.
따라서 유저레벨에서 우리가 4000바이트를 보내달라고 요청했다 하더라도,
실제로 상대방의 커널 RecvBuffer가 거의 꽉 찼으면,
4000을 다 보내지 않고 일부만 보낼 수 있다는 것입니다.

가령 상대방의 커널 RecvBuffer가 1000바이트만 받을 수 있다면,
4000바이트중 1000바이트만 보내게 되겠죠.
어찌됐건 이 1000바이트를 성공적으로 전송했다 가정하면,
상대방의 유저레벨 프로그램에서 이 1000바이트를 유저레벨로 복사해서 분석을 할텐데,
애석하게도 유저레벨에서는 4000바이트가 '완전하게' 와야 그것을 처리할 수 있습니다.
상대방의 유저레벨 프로그램에서 보낸 패킷이
내부적인 이유로 반쯤 짤려 오는 상황이 있다면,
나머지 부분이 도착하기를 기다렸다가 처리해야 합니다.

1.
상대방의 애플리케이션 계층과는 무관하고 순전히 커널 Recv 버퍼 문제입니다.

2.
분할된 패킷중 순서상 '뒤'에 해당하는 부분을 먼저 받을 수는 없습니다.
UDP라면 순서가 꼬일 수도 있지만
TCP에서는 경계가 없을뿐이지 보낸 순서는 그대로 유지됩니다.
그리고 위 예제 상황에서 1000바이트를 보냈는데
'일부' 데이터가 유실되어 1000바이트가 도착하지 않은 상황은
TCP보다도 더 및 단계인 IP 단계에서 처리를 해줍니다.

3.
'앞부분'을 먼저 받아 유저레벨 버퍼로 복사한 다음,
완전하지 않다고 판단되면 '뒷부분'을 기다려야 합니다.
'뒷부분'까지 성공적으로 와서 완성된 패킷이라 판단되면
이를 처리하는 것이죠.
'분할된' 데이터도 일단은 유저레벨로 복사해서
커널 레벨에 있는 데이터를 빼오긴 해야 합니다.
그렇지 않으면 우리의 커널 RecvBuffer가 꽉 차서
상대방이 데이터를 보내지 못하게 되겠죠.
분할된 패킷을 모두 받고 어플리케이션에 받는게 아니고
일단 복사는 하면서 완전체 패킷을 기다리는 것입니다.

강의 코드를 진행하다 보면 패킷 조립하는 내용이 PacketSession에 나옵니다.

SIsB님의 프로필 이미지
SIsB

작성한 질문수

질문하기