자바로 된 클라이언트와 C로된 서버-클라이언트 모델에서요~~ 소켓데이타가 짤리는 현상에 대한 질문

suldogagi의 이미지

자바로 짠 클라이언트와 C로짠 서버-클라이언트 모델에서요

테스트를 하고있는데

약 10KB의 데이타를 클라이언트가 전송했어요

그런데

서버에서 읽어들이면 8192 바이트만 읽어들이네요

나머지는 한번더 읽으면 읽혀집니다.

즉 클라이언트가 8192 로 짤라서 두번 전송한다는 얘긴데요

(사정상 tcpdump 를 뜰수가 없어서 추측입니다.)

만약에 TCP/IP프로토콜 상에서 나눠 전송했으면 어짜피 서버단에서 재조립되서 읽혀지니까

프로토콜이 나눠서 보내는거 같지는 않구요

자바로된 클라이언트가 내부적으로 특정 바이트만큼 짤라서 나눠 보내는거 같아요.

음 암튼

서버에서 한방에 10KB의 데이타를 읽고싶거든요

방법이 있습니까??

자바의 특성때문에 어쩔수 없는겁니까??

고수님들의 조언을 부탁합니다.

추신 - 클라이언트를 C 로 짜면 한방에 전송하는걸 확인했습니다.

jos77의 이미지

자바 클라이언트 소스 쪽에서 뭔가 문제 있지 않을까요? flush를 안해준다던가 buffer를 close 안한다던가
예전에 java - c 통신 프로그램 만들었을때 잘 돌아갔었고, 언어상의 문제는 없었습니다.

-----
안녕하세요 소프트웨어 공학센터 장원석 책임입니다.
http://www.software.kr

stypr의 이미지

음... 원론적으로 이야기 해서 C 클라이언트로 한방에 읽어 진다고 해서 항상 (혹은 반드시) 다른 때에도 한방에 읽어질까요? 운영체제 상으로는 보장을 못하는데 말입니다...

우선 하시고자 하는 것에 대한 가정이 틀려보이네요.

oosap의 이미지

저는 하수입니다만, 저도 답변해볼게요.. 하수의 말이니 틀렸거나 잘못되었을 수 있다는 것 우선 밝히고요..

>> 자바의 특성때문에 어쩔수 없는겁니까??
TCP 소켓이 원래 데이타의 경계가 없다고 했습니다. 그러니까 TCP 소켓의 특성이라고 봐야 하지 않을까요?

클라이언트에서 보내는 만큼 서버에서 받길 원한다면 UDP 소켓은 가능할 것입니다. UDP 는 데이타의 경계가 있어서 보낸대로 받아지니까요(두번에 걸쳐 전송했으면 두번에 걸쳐 수신해야 합니다.)..

TCP 에서 데이타를 받을 때는 그래서 수신 횟수로 수신 완료를 판단할 수 없고 read 함수의 반환값이나 EOF 를 가지고 판단한다고 앏니다.

read() 함수의 반환값을 통한 EOF 와 버퍼가 빈 상태 구분하기.
- 상대측에서 Halfclose 를 하거나 close 를 통해 연결을 종료하면 read 함수를 통해
버퍼에 남겨진 데이타를 다 읽은 후에는 read 함수가 0을 반환한다.(EOF)
- Halfclose 나 close 가 된 상태가 아닌데 read 를 수행하면 버퍼에 남겨진 데이타를
다 읽고 난 후에는 read 함수가 -1을 반환한다.(error) 이때, 변수 errno 의 값은
EAGAIN 이다.
 "read 함수가 -1을 반환하고, 변수 errno에 저장된 값이 EAGAIN 이라면 더 이상
 읽어들일 데이타가 존재하지 않는 상황이다."

위와 같은 이유로 TCP에서는 read 호출을 데이타 수신이 완료(위의 조건으로 검사)될때까지 반복합니다.

Thanks for being one of those who care for people and mankind.
I'd like to be one of those as well.

hwiorb의 이미지

확인해본 사항은 아니지만 기본 자바 io 버퍼크기가 8192라서 그럴거에요. EOF나올때까지 읽는 방법외에는 딱히
방법이 없어보입니다.

nil.

gilgil의 이미지

이는 언어랑 관계가 없고 TCP의 특성에 기인하는 현상입니다.

[기술적 설명]
요즘 일반적으로 사용하는 네트워크는 대개가 ethernet 기반이고 MTU는 1500 octet인 경우가 많습니다(네트워크 환경에 따라 다를 수 있음). TCP data를 전송할 때 IP(20), TCP(대개 20) 헤더를 감안하면 하나의 packet에 전달할 수 있는 내용은 약 1460바이트입니다. 참고로 iPhone OS에서는 TCP header의 크기가 좀 더 커서 한번에 보낼 수 있는 TCP data의 크기가 조금 더 작아 지고, IPv6 통신의 경우에도 작아 지게 됩니다.

송신측 어플에서 send(10000)으로 한번에 1만 바이트를 송신한다 하더라도, 물리적으로 네트워크 환경에서는 하나의 packet에 1만 바이트가 한꺼번에 전송되는 것이 아니라 나누어서 보내어 지게 된다는 것입니다. 쉽게 말해서 도로에 다닐 수 있는 차량이 1톤 트럭이라면, 10톤짜리 화물을 보낼 때에는 1톤 트럭에 10번 나누어 보내야 한다는 거죠.

MTU의 문제뿐만 아니라 packet loss에 의해서도 이러한 현상은 충분히 발생할 수 있습니다. 예를 들어 설명해 보겠습니다.

[송신]
어플에서 send(5000) 명령어로 한꺼번에 5천 바이트를 보냄.
아래 레이어에서 1천바이트씩 잘라서 보냄(인덱스를 0~4라 가정하겠음).

[수신]
recv(5000) 하면 5000이 반환되는 것이 보통의 경우임. 하지만 그러지 않을 수도 있음.

[가정] - 인덱스 3번 패킷이 네떡 중간에 손실이 되었다고 가정.

A. 수신측은 0, 1, 2 번 패킷 받았음.
B. 수신측은 4번 패킷 받았음. 3번 패킷을 못받아서 송신측에게 3번 패킷의 재전송을 요구함.
C. 송신측은 수신측으로부터 재전송 요구를 받고 3번 패킷을 재전송함.
D. 수신측은 3번 패킷 잘 받았음.

수신측에서 언제 recv를 호출했는지에 따른 함수의 return값
~A : block
A~B : 3000(0, 1, 2 패킷의 합)
B~C : 3000(3번 패킷 아직 못 받았음)
C~D : 3000(3번 패킷 아직 못 받았음)
D~ : 5000(수신된 3번 패킷과 이전에 받은 4번 패킷을 조합해서)

[결론]
송신측에서 send(N) 바이트를 호출했다 하더라도 TCP의 기본 성격상 수신측에서 recv의 return값이 반드시 N이 되어야 한다는 보장은 없습니다. 따라서, 대개의 경우에는 Application Layer에서 처리를 하게 됩니다. TCP data의 header에 length 정보를 넣거나 header와 body의 delimiter를 넣어서 처리하는 방식등을 사용하는 것이 일반적입니다.

suldogagi의 이미지

tcp/ip 프로토콜에 대한 간략한 설명입니다.

TCP/IP는 2개의 계층으로 이루어진 프로그램이다. 상위계층인 TCP는 메시지나 파일들을 좀더 작은 패킷으로 나누어 인터넷을 통해 전송하는 일과, 수신된 패킷들을 원래의 메시지로 재조립하는 일을 담당한다. 하위계층, 즉 IP는 각 패킷의 주소 부분을 처리함으로써, 패킷들이 목적지에 정확하게 도달할 수 있게 한다. 네트웍 상의 각 게이트웨이는 메시지를 어느 곳으로 전달해야 할지를 알기 위해, 메시지의 주소를 확인한다. 한 메시지가 여러 개의 패킷으로 나뉘어진 경우 각 패킷들은 서로 다른 경로를 통해 전달될 수 있으며, 그것들은 최종 목적지에서 재조립된다.

위에서 봐도 알수있듯이 어플리케이션단에서는 패킷의 재조립을 신경쓸필요가 없습니다.

포로토콜단에서 이것을 처리하니까요(전송계층에서 담당하겠죠)

그래서 프로토콜이 문제가 아니구요

자바로 만들어진 클라이언트가 8192 바이트로 데이타를 지맘대로 잘라서 보낸다는거에 있습니다.

저는 이것을 한방에 보내고싶구요

여기에 착안해서 답변을 부탁드립니다.

감사합니다^^

ymir의 이미지

위에서 이미 설명한대로 TCP 는 stream 입니다.
잘라서 보냈건 나눠서 보냈건, 읽는 쪽에서는 원하는 크기만큼 한꺼번에 읽을 수 있습니다.
물론 원하는 크기만큼 읽을 수 있다 하더라도, 그게 보장된 것이 아니기 때문에..
원하는 크기만큼 읽어들일 수 있도록 코드를 작성해야 합니다.
애초에 한 번의 read() 호출로 원하는 만큼 데이터를 읽어들일 수 있도록 보장되어 있지 않다는 뜻입니다.

문제만 봤을 때, 읽는 쪽에서 일단 8192 bytes 를 읽은 후에 리턴을 했다는 것만 알 수 있지..
이걸로는 클라이언트가 나눠서 보냈다는 근거가 될 수 없습니다.

다른분들 의견대로 정확하게 데이터를 주고 받을 수 있도록, application protocol 을 정의해서 구현하시는게 바람직합니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

gilgil의 이미지

음.... 직접 해 보셨는지요? 현업에서 몇번 네트워크 프로그램 짜 보면 금방 알 수 있는 일인데...

// 자바로 만들어진 클라이언트가 8192 바이트로 데이타를 지맘대로 잘라서 보낸다는거에 있습니다.

비단 자바만의 문제일까요? 다음 코드를 봅시다. FTP Server가 FTP Client에게 파일을 보낼 때 쓰이는 코드가 가정하겠습니다.

static const int BUF_SIZE = 1024;
whle (!file.eof())
{
readLen = file.read(buf, BUF_SIZE);
send(buf, readLen);
}

tcpdump로 패킷 잡아 보면 하나의 패킷에 전송되는 TCP data 패킷의 크기는 몇바이트일까요? 1024? 처음에는 그럴지 몰라도 조금만 지나면 MTU 꾹꾹 채워서 1460바이트가 하나의 packet에 전달이 됩니다. 용량 좀 큰거 보내 보면서 패킷 잡아 보면 금방 알 수 있습니다.

Application Layer에서 send 함수를 호출했다 하더라도, 이는 TCP Task에게 명령을 내일 뿐이지 실제로 packe이 송신되는 것은 아닙니다. 그냥 thread scheduling만 될 뿐입니다. Application에서 TCP N 바이트를 송신했다 하더라도(자바에서 8192 크기로 보냈다 하더라도), TCP layer에 가면 TCP layer 마음대로 자르고 붙입니다. 수신측 TCP layer도 그렇구요.

TCP Stack은 데이터의 제대로 된 순서는 보장하지만, 송신측에서 보낸 단위대로 수신측에서 수신되는 것까지 보장하지는 않습니다.

stypr의 이미지

우선 내공을 좀 더......

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.