파일 전송시 버퍼 질문입니다,

익명 사용자의 이미지

프로토콜중 파일전송 프로토콜이 있습니다.

sync등을 하여 파일전송 준비를 하고 보내고 받고 하는 부분인데

파일 전송은 정상적으로 됩니다.(파일전송 -> 서버 파일받는곳 -> 클라이언트 )

파일 전송 write 다하게 되면 서버는 바로 다 보냈다는 end프로토콜을 날립니다.

근데 클라이언트 측에서는 파일전송의 마지막 버퍼와 end 프로토콜이 합쳐진 값이 들어와서 파일이 깨지게 됩니다.

하지만 파일 전송 완료 후 sleep(1)을 주게 되면 문제가 없습니다.

이를 미루어 서버측에서는 순차적으로 잘 보내나 클라이언트 측에서 받을때 버퍼가 밀려있을경우 합쳐서 받는것 같은데..

제가 생각하고 있는게 맞나요? 또한 이 문제를 해결하려면 어떻게 처리를 해야되나요?

함수는 write와 read를 사용하였습니다.

bugiii의 이미지

파일 전송전에 파일의 크기를 따로 전송한 후 내용을 전송하고, 받는 쪽에서 그 크기만큼 받는 것으로 완료되었다고 약속하는 것이 낫습니다.

익명 사용자의 이미지

그렇게 처리 하였습니다.

제 질문을 자세히 읽어보시면 받는쪽에서는 순차적으로 패킷을 보내는데 파일 끝까지

보내는 측에서는 그걸 순차적으로 차례차례 받는건 아닌것 같습니다.

마지막에도 마지막 파일 크기 까지만 보내주지만 받는측에서 찍어보면 마지막 파일 크기 + 파일 전송 종료 패킷을 합한 것이

한번의 read에 들어옵니다..

wariua의 이미지

TCP가 원래 그렇습니다. 파일 길이에 맞게 받은 데이터를 잘 나눠서 처리해주시면 됩니다.

TCP를 사용하고 계시겠지요? TCP는 데이터 스트림 서비스를 제공한다고들 합니다. 반면 UDP는 데이터그램 서비스를 제공한다고 하구요.

그래서 UDP에서는 데이터를 send 하면 그 덩어리 그대로 상대에게 전달됩니다. 받는 쪽에서 recv 하면 그 덩어리를 통째로 받거나 아무 것도 못 받거나 합니다.

반면 TCP에서는 그런 전달 단위가 없습니다. 바이트와 바이트가 줄을 지어서 상대에게 전달될 뿐입니다. (즉 전달 단위가 바이트입니다.) 따라서 보내는 쪽에서 10바이트씩 두 번 send를 했더라도 받는 쪽에서는 한 번의 recv로 20바이트 모두를 받을 수도 있고 두 번의 recv로 10바이트씩 받을 수도, 두 번의 recv로 15바이트와 5바이트씩 받을 수도, 스무 번의 recv로 1바이트씩 받을 수도 있습니다.

그럼 주고받는 항목의 경계를 (지금 경우라면 파일 데이터의 끝을) 어떻게 구별할까요? 그래서 TCP를 사용할 때는 bugiii님 말씀처럼 데이터의 길이를 미리 보내는 경우가 많습니다. 받는 쪽에서는 그 길이만큼만 recv 하고서 그 데이터를 마무리 합니다. recv로 뭔가를 더 수신했다면 그건 다음 항목의 데이터(지금 경우라면 end 메시지)입니다.

그런데 지금 익명 사용자님은 받는 길이(read()에서 세 번째 인자)를 딱히 제한하고 계신 것 같지 않네요. 그러면 파일 데이터의 끝과 end 메시지를 한 번에 읽어들이게 될 수도 있습니다. 그럼 어떡해야 할까요? 앞서 파일의 총 길이(file_len)를 받아뒀으니까 지금까지 수신한 파일 데이터의 총 길이(recv_tot_len)를 계산해뒀다면 남은 파일 데이터의 길이를 알 수 있을 겁니다. 그러면 read()로 읽은 데이터 가운데 앞쪽의 (file_len - recv_tot_len) 바이트만 파일 데이터이고 뒷쪽은 다른 데이터(end 메시지)가 되겠지요.

$PWD `date`

익명 사용자의 이미지

read의 세번째 인자는 넘겨주는 write쪽에서 의 크기와 같은 인자로 적용하였습니다.

그렇다면 read의 세번째 인자 크기(읽어올 최대크기) 를 total-현재받은size 로 해야된다는 건가요?

그리고 보내는측에서 순차적으로 각각 보내주는건데 받는측에서는 receive 버퍼가 밀려있어서 합쳐져서 들어오기도 한다는건가요?

wariua의 이미지

네, 합쳐져서 들어올 수 있습니다.

말씀드린 것처럼 TCP에서는 바이트 덩어리가 아니라 바이트의 흐름으로 데이터가 전달됩니다. 10B write 하고 2B write 했다고 받는 쪽에서도 10B read 하고 2B read 하게 되는 것이 아닙니다. 패킷 전달 타이밍이나 네트워크 상태, 커널 네트워킹 동작 옵션, 수신 호스트의 동작 상태 등에 따라 한 번에 12B를 읽을 수도 있고 다양한 방식으로 나눠서 읽게 될 수도 있습니다. 더 받아야 할 길이를 계산해서 그만큼만 받거나, 나눠 받은 걸 적당히 합치거나, 한꺼번에 받은 걸 적당히 나누거나 하는 일을 TCP를 이용하는 프로그램에서 해줘야 합니다.

$PWD `date`

익명 사용자의 이미지

음 그런데...

보내는측에서 파일을 읽어와서 받는측으로 보내줍니다 여러패킷 나누어서

그런데 보내는측에서 말씀하신 바와 같이 read( , , 토탈사이즈 - 여지껏 받은사이즈 ) 로 읽게되면

read 처음 하자마자 -1 뱉으면서 죽습니다.

아마 저장하는 버퍼 사이즈 보다 세번째 인자값이 커서 그런거 같은데요

이를 버퍼 사이즈보다 작을때만 저렇게 처리하자니 코드가 너무 지저분하네요

즉 , 말씀하신 방법처럼 하면 안되는데 제가 잘못 구현한건가요

wariua의 이미지

코드가 지저분하게 느껴지더라도 필요한 로직은 넣어주셔야 합니다. 더 이상 뺄 코드가 없을 때 프로그램 구현이 끝나다고도 하지만 일단 필요한 코드는 다 있을 때의 얘기입니다. :-)

개념적인 부분에 대해선 제가 아는 만큼 말씀드린 것 같고, 구체적인 코딩 수준까지 들어가는 건 폐를 끼치는 게 될 것 같아 도망갑니다~

$PWD `date`

planetarium의 이미지

read용 버퍼를 만들어서, while 루프를 돌면서 버퍼 사이즈만큼 계속 읽으세요.
데이터가 많으면 여러번에 나뉘어 읽을테고, 데이터가 적으면 알아서 데이터만큼 읽게 됩니다.
read 함수 반환값 확인해보시면 됩니다.

댓글 달기

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