Skip to content

C:send

Send Buffer Information

send()는 정상적인 상황이라면 데이터 전송 여부에 상관없이 커널의 send buffer로 데이터를 다 복사하면 리턴한다(http://stackoverflow.com/questions/5407182/blocking-sockets-when-exactly-does-send-return). Send buffer는 ACK을 받지 못한 데이터들이 쌓이는 커널 내의 버퍼이다. 일반적인 상황에서라면 이 send buffer는 윈도우즈의 경우 8K, 리눅스의 경우 16K이다. 이 값은 SO_SNDBUF로 조정할 수 있다(http://brucekim.egloos.com/1459382).

Blocking mode socket이라면 send()도 블럭되는 경우가 있는데, man page에서는 아래과 같이 설명하고 있다.

When the message does not fit into the send buffer of the socket, send normally blocks, unless the socket has been placed in non-blocking I/O mode.

이 말은 blocking 소켓에다가 send buffer를 넘어가는 데이터를 전송하라고 요청하면 블록킹 된다는 것이다(http://kukuta.tistory.com/119). 이 상황을 테스트하려면 위 파이썬 코드로 큰 데이터를 계속해서 send()해보면 된다. 얼마안가서 send()가 블럭되는 것을 볼 수 있다.

send()가 블럭되는 성질을 이용해 센더측에서 데이터가 정상적으로 보내졌는지 확인할 수 있을 줄 알았다. 하지만 위에서 이야기한 것과 같이 send()의 리턴시점과 send()가 에러를 리턴하는 시점이 다르기 때문에 application 입장에서는 어떤 데이터까지 성공적으로 전송되었는지 알 길이 없다. 어떤 상황에서 에러가 발생하는지는 http://www.stratus.com/blog/openvos/?p=749 에 잘 나와 있다. 저 링크의 결론은 TCP는 데이터 전송을 보장해주지 못하기 때문에 application level ACK을 사용해야만 한다는 것이다.

이 시점에서 send()의 리턴값과 errno, 소켓 옵션에 대해서 정리해봐야겠다.

  • SO_SNDTIMEO로 send()의 타임아웃을 지정할 수 있으며, 타임아웃이 발생한 경우 send()는 -1를 리턴함과 동시에 errno를 EAGAIN(=EWOULDBLOCK)으로 지정한다.
  • TCP send buffer가 꽉 차서 send()가 타임아웃된 경우, 다음번 send() 호출시에 errno는 ETIMEOUT이 된다.
  • send()했는데 reset이 돌아오는 경우, 다음번 send() 호출시에 errno는 ECONNRESET이 된다.
  • send()도중에 인터럽트가 걸리는 경우, errno는 EINTR가 된다.

만약 TCP를 이용해 length field로 구획이 나눠지는 구조체를 전송하고자 할 때, 아래와 같은 것들을 생각해 보았다.

  • 네트워크 단절로 인한 전송실패를 송신측이 알아차릴 수 있는가?

송신측이 send buffer를 다 채울 때까지 수신측이 데이터를 못 받으면, 송신측 send()가 블럭되었다가 타임아웃 될 것이다. 이 타임아웃을 수신측 이상으로 판단할 수 있다.

  • 네트워크 단절로 인한 전송실패를 수신측이 알아차릴 수 있는가?

송신측이 주기적으로 메시지를 보내주다가, 이 메시지를 못 받으면 네트워크 단절로 간주한다.

  • 송신측에서 성공적으로 보낸 구조체와 실패한 구조체를 구별할 수 있는가?

송신측은 일단 다 보냈다고 생각하므로 구별할 수 없다.

  • 수신측에서 구조체 단위로 성공과 실패를 구별하는 것이 가능한가?

제일 문제가 되는 것이, 한 구조체가 반쯤 전송되다 실패해서 나머지 반쪽이 다른 구조체로 채워져 수신되는 경우이다. 이렇게 되면 수신측에서는 구조체의 구획을 나눌 방법이 사라지게 되므로(이상한 필드를 length field로 인식할 수 있음) 망한다. 하지만 수신측에서 성공적으로 구조체를 다 못받고 타임아웃이 되면(여기서 타임아웃은 TCP 타임아웃과는 별개다) 아얘 연결을 끊어버린다면 또 가능할 것 같다. Application level ACK을 사용할 수도 있지만 이러면 쓰레드를 하나 더 생성해야하고, 전송 함수의 응답성을 위해 별도의 큐 구조를 가져가야만 한다.

Favorite site