로컬 IP를 사용하여 TCP/IP 통신을 하는데 RECV() 함수 호출 시 데이터가 잘려서 들어 옵니다.

sunny2696의 이미지

현재 솔라리스9 장비에서 로컬IP를 사용하여
프로세스 간에 통신을 하도록 개발을 하고 있는데요
Client 어플에서는 700 바이트를 전송했다고 뜨는데
Server 어플에서는 700 바이트를 전부 수신 하지 못하고 있습니다.

Client 와 Server 간에는 tcp 연결이 send 와 recv로 각각 2중으로 구성되어 있고,
Server에서 Client가 전송하는 데이터를 수신하는 부분이 문제 입니다.

server에서는 recv() 함수를 사용하여 데이터를 소켓에서부터 가져오도록 되어 있는데요
이와 관련해서

KLDP 에서 자료를 검색해본 결과,
RECV() 함수로 데이터를 수신하는 경우,
한번에 원하는 만큼 수신할 수 없다는 것을 알았는데

왜 그런 현상이 발생되는지 나와 있지 않아서
이렇게 글을 올리게 되었습니다.

익명 사용자의 이미지

사용자가 지정한 데이터의 양만큼 읽어서 리턴하지 않도록 발전한 데는 그만한 이유가 있겠죠. 생물이 특정한 기능을 갖거나 형태로 진화하는 것처럼요.

linux 에서 read, write 함수들이 다 그렇게 되어 있습니다. design policy의 일부라고 생각합니다. Oreilly 의 Linux Device Driver 에 보면 그런 구절이 있죠. "디바이스 드라이버에서 policy를 결정하지 마라"라는. 그만큼 상위 라이브러리나 애플리케이션의 자유도가 높아집니다.

network program에 MTU 라는 용어가 있죠. 동일한 프로토콜을 사용해서 이종 네트워크를 통해 통신하는 경우를 가정해보면.

사용자가 900 바이트의 데이터를 한 번에 보내려고 하는데 한 네트워크에서 MTU가 500 이고 다른 네트워크는 1500 인 경우에... 님이 프로그램을 하는데 디바이스 드라이버에서 혹은 라이브러리(glibc 같은 범용 라이브러리)에서 사용자가 지정한 900 바이트를 지정해서 쓰거나 읽어야 리턴하는 경우를 말입니다. MTU 500인 네트워크를 통과할 수 없겠죠. 만약에 디바이스 드라이버나 범용 라이브러리에서 사용자가 지정한 양의 데이터만 쓰거나 읽는다면 애플리케이션을 개발할 때 어떤 네트워크에서 이 애플리케이션이 사용될지까지 고려해야 할 겁니다.

하지만 애플리케이션에서 데이터 송수신 양을 다룬다면 그런 문제는 발생하지 않겠죠. 그리고 애플리케이션에서 자신이 원하는 만큼의 데이터를 모으기는 매우 쉽습니다.

stevens 의 network programming 앞 부분에 예제도 있었던 것으로 기억합니다.

sunny2696의 이미지

먼저, 친절한 설명 감사드립니다.

한가지 더 궁금한게 있는데요,

현재 제가 개발하고 있는 프로세스(어플리케이션)들은
한 장비에서 루프백 IP 주소(127.0.0.1)를 사용하여 Client와 Server 로 통신하고 있습니다.
MTU 사이즈는 IFCONFIG 로 확인 해본 결과 8232로 되어 있구요
이러한 경우에는 왜 recv 함수로 한번에 데이터를 수신을 못하는 건가요?

그리고 제가 알고 있는 지식으로는 Blocking 모드의 소켓에서는 recv 함수를 호출 했을 때
어찌됐던 수신하려는 크기의 데이터가 다 수신되어야 리턴하는 걸로 아는데.
Blocking 모드의 소켓에서도 원하는 크기만큼 데이터를 수신하지 못해도 성공으로 리턴할 수 있는 건가요?

익명 사용자의 이미지

MTU는 한가지 예일 뿐입니다.

저도 제 lo interface 확인해보니 mtu가 65536으로 되어 있네요. 근데... TCP/UDP에 데이터 길이 필드가 지정할 수 있는 길이가 최대 1500 바이트 언저리 아닌가요(책 있으시면 확인해 보시길)? lo 는 loop back interface 일 뿐입니다. 그 위에 TCP/UDP 가 올라간다면 lo, IP, TCP/UDP 가 주는 길이 제한이 모두 적용되어야 합니다. 제한 조건 중에서 제일 작은 길이가 사용되겠지요. loopback 이나 로컬이라면 1450 바이트 정도 데이터는 한꺼번에 주고받을 수 있을 걸로 보여집니다.

'man 2 recv' 일부입니다.

If no messages are available at the socket, the receive calls wait for
a message to arrive, unless the socket is nonblocking (see fcntl(2)),
in which case the value -1 is returned and the external variable errno
is set to EAGAIN or EWOULDBLOCK. The receive calls normally return any
data available, up to the requested amount, rather than waiting for
receipt of the full amount requested.

"The receive calls normally return any data available"

sunny2696의 이미지

정말 감사합니다.
덕분에 많은 배움을 얻었습니다.

익명 사용자의 이미지

TCP로 데이터를 보낼때,

"내가 보낸 데이터를 상대가 그만큼 받는다는 보장은 없다" 고 생각하시면 됩니다.

그렇다면 메시지의 시작과 끝을 어떤식으로 구분할까라는 질문이 떠오르죠.

그러면 자연스럽게 xml이나jason 같은 메타 데이터로 data level protocol을 설계하고

처리하게 되는 것을 생각할 수 있을 테고요. 지금 우리가 사용하는 http나 xml 처럼요

내가 보낸대로 메시지 시작과 끝을 구분하고 싶다면, UDP 를 사용하면 되지만,

UDP에도 MTU에 대한 기준이 있고 그 크기를 넘어서면 잘 작동한다는 보장은 없습니다.

그래서 UPnP Discovery 메시지등에도 UDP 최대 길이가 정해져 있기도 합니다.

일반적으로 LAN 에서는 동작하겠지만요.

참고로 내 PC 에서 작동한다고 그 socket 프로그램이 정상이라는 보장은 업습니다.

DCode의 이미지

저는 보통 그래서 패킷 디자인하며 어플리케이션 헤더를 만들고 거기에 패킷 길이를 집어넣거나

아예 고정사이즈로 만든 다음 나머지 길이만큼을 루프문 돌립니다.

전체 받을 바이트수에서 읽은 바이트수만큼을 빼서 모자라면 더 읽는거죠.

zalhae의 이미지

쉽게 생각해서 send()로 1000bytes를 전송할 경우 recv()가 소켓버퍼에서 읽어가는 시점 무조건 전송된 1000bytes가 모두 읽히는 것이 아닙니다.
1000bytes는 MTU나 MSS값보다 작다 하더라도 소켓 버퍼에는 상황에 따라 잘게 분할되어 들어올 수도 있습니다.
특히 네트워크 회선의 품질이 나쁘거나, 서버 자체의 CPU 과부하 상태가 발생되는 생황에서는 더욱 빈번하게 일어납니다.
따라서 이러한 상황에서 질의 하신 상황을 회피하기 위해서는 위 DCode 분께서 말씀하신 대로 수신하고자 하는 바이트를 미리 약속 받고 해당 바이트만큼 수신이 완료될때까지 반속하는 구조가 일반적입니다.
또는 recv() 옵션에 MSS_PEEK와 같은 옵션을 사용하여 소켓 버퍼는 비우지 않고 일단 체크를하여 유효한 데이터가 들어온 다음 옵션 0을 쓰다거나, MSS_WAITALL 등을 사용하여 수신하고자 하는 데이터를 모두 받는 방법도 여러 방법중에 하나일 수 있습니다.
recv_block() 과 같은 함수를 직접 작성하시던지 아니면 공개된 소스를 쓰시는 것도 좋습니다.

익명 사용자의 이미지

참고로 사이즈 한번에 다 들어올거라는 보장은 없습니다.

예를 들어 데이터 길이를 처음 4바이트로 설정 한다면

그 4바이트도 짤려 들어올 수 있습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.