ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 소켓의 수신 버퍼 크기를 0으로 할 때 주의할 점.
    분석과탐구 2021. 5. 30. 20:56

    IOCP 및 overlapped IO를 다루는 글에서 소켓의 수신 버퍼 사이즈를 0으로 만들어서, zero copy의 이점을 살리라고 되어 있다. setsockopt 함수와 SO_RCVBUF를 이용하여 소켓의 수신 버퍼 사이즈를 0으로 만들 수 있다. zero copy는 도착한 패킷이 커널의 소켓 수신 버퍼를 거치지 않고, 유저가 제공한 버퍼에 바로 복사되는 것을 의미한다. 그러나, SO_RCVBUF의 사이즈를 0으로 만든다고 zero copy가 되는 것은 아니며, 수신 버퍼 사이즈를 0으로 만드는 것은 주의해야할 점이 있다. zero copy와 SO_RCVBUF의 관계는 이 링크에 있다. https://crmerry.tistory.com/29?category=1031711

     

    아래 인용글에 커널 소켓의 수신 버퍼 사이즈를 0으로 만드는 것에 대한 주의사항이 있다.

     https://docs.microsoft.com/en-us/windows/win32/winsock/overlapped-i-o-and-event-objects-2

     

    Overlapped I/O and Event Objects - Win32 apps

    Windows Sockets 2 supports overlapped I/O and all transport providers support this capability.

    docs.microsoft.com

    If data arrives when no receive buffers have been posted by the application, the network resorts to the familiar synchronous style of operation. That is, the incoming data is buffered internally until the application issues a receive call and thereby supplies a buffer into which the data can be copied. An exception to this is when the application uses setsockopt to set the size of the receive buffer to zero. In this instance, reliable protocols would only allow data to be received when application buffers had been posted and data on unreliable protocols would be lost.

    어플리케이션 코드에서 WSARecv를 호출하여 유저 버퍼를 제공하지 않으면. 기존에 친숙한 스타일의 동기적 방식으로 네트워크 동작한다고 되어 있다. 일단 도착한 패킷을 내부적으로 저장하고, WSARecv가 호출되면 저장한 패킷을 복사하도록 제공한다는 것이다.

     

    그런데, setsockopt 함수를 이용하여 수신 버퍼의 크기를 0으로 만들어 버리면 다르게 동작한다. 이런 경우에, TCP 프로토콜은 WSARecv가 호출되어야 내부적으로 패킷을 저장하고, UDP 프로토콜은 패킷이 버려진다고 한다. 즉, UDP에선 바로 패킷이 버려지고, TCP에서도 WSARecv가 호출하기 전까지의 패킷은 버려진다는 것이다.

     

    이것이 의미하는 바는 수신 버퍼의 크기를 0으로 만드는 것에 대한 주의가 필요하다는 것이다. Overlapped IO 프로그래밍을 할 때, WSARecv이후로 일련의 처리과정을 거친 후에 다시 WSARecv를 호출하게 되는데, 이 간격 동안 도착하는 패킷은 모두 버려지는 것이다. TCP가 이렇고, UDP는 무조건 버려진다고 나타내고 있다. 이 일을 막으려면 동일한 소켓에 대하여 WSARecv를 동시에 여러 번 post 해줘야 한다. 그런데, TCP의 경우 stream이므로 받은 패킷이 온전한 하나의 메시지를 의미한다고 볼 수 없다. 따라서, WSARecv로 등록한 여러 개의 버퍼를 다시 재조립하는 과정이 필요하다. 그런데, 이게 말이 재조립이지, 쉽지는 않을 것이다. 다행히, WSARecv를 등록한 순서대로 버퍼에 쌓이긴 하니까....(단, 완료 통지의 순서는 보장되지 않는다.....) 이 속성을 이용하면 되지 않을까? 싶지만 역시 그럴 바에 수신 버퍼의 크기를 건들지 않고 1회의 WSARecv를 post 하는 것이 나아 보인다.

     

    위 내용이 주로 주의할 점이고, 아래 내용은 수신 버퍼 크기와는 다르지만 알고 있어야 하는 내용이다.

     Both send and receive operations can be overlapped. The receive functions can be invoked several times to post receive buffers in preparation for incoming data, and the send functions can be invoked several times to queue multiple buffers to send. While the application can rely upon a series of overlapped send buffers being sent in the order supplied, the corresponding completion indications might occur in a different order. Likewise, on the receiving side, buffers can be filled in the order they are supplied, but the completion indications might occur in a different order.

    위 내용에 의하면, 똑같은 소켓에 대하여 여러 번의 WSARecv, WSASend 호출을 허용한다. 물론, 버퍼는 좀 달리 제공해야 할 것이다. 주의할 점은, 보내는 것도 받는 것도 순서대로지만, 보내기 완료했다는 이벤트와 받기 완료했다는 이벤트의 발생 순서는 다를 수 있다는 것이다. 

     

    추신1)

    IOCP 및 Overlapped IO를 적용하여 테스트해보니, TCP일 때 SO_RCVBUF로 소켓 수신 버퍼의 사이즈를 0으로 만들더라도, 패킷이 사라지지 않는다는 것을 확인했다. 문서랑 동작이 다르다. 그래도, 역시 공식 문서를 따라가는 쪽이 낫다고 보인다. 언제 구체적 동작이 바뀔지 매번 확인할 수 없으니까.

     

    댓글

Designed by Tistory.