MFC -> 안드로이드 간 소켓 통신 시 송/수신 크기가 다릅니다.

doutor0303의 이미지

안녕하세요.

소켓 통신 시 송/수신 크기가 다른 문제로 인해 문의 드립니다.

MFC는 Server이고(Desktop) Android는 Client(Phone) 입니다.

1. Android에서 접속
2. Android UI에서 버튼을 누르면 커맨드를 서버에 전달하고
3. 커맨드를 받은 서버는 커맨드를 구분하여 HEADER + 파일(이미지)을 전송하는 구조 입니다.

문제는 Server(MFC)에서 전달하는 사이즈는 정상적으로 전달되는 것 같은데,(return 값 sum해서 확인)
Android에서 스트림을 받을 때 값이 약 2048 정도 적게 들어와서 read 함수내에서 무한 블록되는 상태입니다.
(HEADER를 양쪽다 적용하지 않고 단순 파일 전송만 하면 아주 정상적으로 동작됩니다.)

HEADER는 32byte로
파일명[구분자 쉼표]파일크기 \r\n형태로 구성을 하고

char header[32];
memset(header, 0x00, 32);
sprintf(header, "%s,%d\r\n", (CStringA)filename, pFile->GetLength());
header[30] = 0x0D; // 혹시 몰라 CR 추가
header[31] = 0x0A; // 혹시 몰라 LF 추가
send(hClient, header, 32, 0); // 32 크기만큼 전달

파일 CFile을 이용해서 GetLength()만큼 while문 돌려서 전달하고 있습니다.

int sum=0;
while((send_cnt = pFile->Read(buf, 1024)) > 0) {
    int ret = send(hClient, buf, send_cnt, 0);
    sum += ret;
    if(errno == EINTR || errno == EWOULDBLOCK || ret <= 0) {
        AfxMessageBox(_T("전송 에러"));
    }  
}
if(sum != pFile->GetLength()) {
  AfxMessageBox(_T("전송 에러"));
}

Client(Android)에서는 BufferedReader를 통해 readLine()으로 HEADER를 읽으려고 했고
HEADER를 Parsing하여 저장할 파일명(FileOutputStream) 및 파일크기(소켓 상태는 유지하기 위해)를 얻게끔 작업했습니다.

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String header = in.readLine();

실제 데이터는 while문을 통해 남은크기만큼 가져오도록 하였고 파일크기 만큼 받으면 종료되도록 작업하였습니다.

while((read_length = socket.getInputStream().read(buf)) != -1) {
    sum += read_length;
    Log.d(TAG, "downloaded length : " + read_length + ", sum : " + sum);
 
    output.write(buf, 0, read_length);
    output.flush();
 
    if((fileSize -= read_length) <= 0) {
        Log.d(TAG, "download complete");
        break;
    }
}

되다 안되는 형태가 아닌 계속 데이터가 수신쪽에 적게 들어와 10초 동안 Sleep을 준 후 available()로 들어온 Buffer 상태를 체크해보니
readLine()을 사용했을 때 실제 크기보다 2048 정도 적게 들어오는 상황이 발생하고 있습니다.

수신중인 파일 이름 : harvest.png, 파일 크기 : 951122, available : 951122 // 정상적인 경우
수신중인 파일 이름 : harvest.png, 파일 크기 : 951122, available : 949074 // readLine()을 수행한 후 10초 뒤 Buffer를 확인한 경우

아예 read를 수행하지 않고 일정 시간 후 버퍼를 확인해보면 사이즈는 같았습니다.

readLine()이라는 함수에 문제가 있는가 싶어 read(buf, 0, 32); 이런 형태로도 읽어봤습니다만 read의 경우는 제대로 올라올 때도 있고 2048로 올라올때도 있고... 뒤죽박죽입니다.
제대로 올라왔을 때에도 다시 시도하면 적게 들어와서 블록되고 그러네요.

뭘 잘못하면 저런 상태가 되는지 아니면 다른쪽에 근본적인 문제가 있는데 이런 형태로 문제가 발생되는 건지 궁금합니다.

여기서 "송수신" "소켓" "크기" 등등 글 많이 찾아보고 읽어봤는데 뚜렸하게 답변이 되지 않아 문의 드립니다.

알고 있는 부분 있으면 도움 부탁드립니다.

감사합니다.

shint의 이미지

1. 같은 크기에 데이터를 보내보세요.
2. 함수에 인자값. 리턴값. 오류값을 MSDN이나 API문서와 디버그모드에서 확인해보세요.
3. 소켓 전송이 잘 되는 책 예제를 참고해보세요.

추가로 적어봅니다.
트래픽 점검(측정) 유틸리티 | ShellScript http://blog.daum.net/shlee45/13744635
Network Monitor (netmon) 네트워크 트래픽 수집 및 분석 http://laigo.kr/69
리눅스 네트워크 트래픽 모니터링 유틸리티 | 리눅스 / Linux http://blog.daum.net/inpl/192
NetTraffic v1.25.5 한글지원 (네트워크 트래픽 실시간 모니터링) http://www.kbench.com/software/?q=node/53046
과도한 네트워크 트래픽 발생 범인을 찾는 좋은 방법 http://kinlife.tistory.com/entry/%EA%B3%BC%EB%8F%84%ED%95%9C-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%8A%B8%EB%9E%98%ED%94%BD-%EB%B0%9C%EC%83%9D-%EB%B2%94%EC%9D%B8%EC%9D%84-%EC%B0%BE%EB%8A%94-%EC%A2%8B%EC%9D%80-%EB%B0%A9%EB%B2%95
쉡 스크립트 예제 - 트래픽 점검(측정) 유틸리티
http://moyaria.tistory.com/entry/%EC%89%A1-%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8-%EC%98%88%EC%A0%9C-%ED%8A%B8%EB%9E%98%ED%94%BD-%EC%A0%90%EA%B2%80%EC%B8%A1%EC%A0%95-%EC%9C%A0%ED%8B%B8%EB%A6%AC%ED%8B%B0
트래픽 발생 프로그램 TfGen
http://www.softpia.co.kr/tt/board/ttboard.cgi?act=read&db=pds_util&page=2&idx=240

----------------------------------------------------------------------------
젊음'은 모든것을 가능하게 만든다.

매일 1억명이 사용하는 프로그램을 함께 만들어보고 싶습니다.
정규 근로 시간을 지키는. 야근 없는 회사와 거래합니다.

각 분야별. 좋은 책'이나 사이트' 블로그' 링크 소개 받습니다. shintx@naver.com

doutor0303의 이미지

우선 답변 감사합니다.

회사라 말씀해주신 부분은직접 가서 확인해보도록 할께요.

1번에 대해서는 32byte를 한번 더 보내서 제대로 받는지를 본적이 있는데 이건 문제없이 받았습니다.
(좀더 데이터를 키워야 할까요?)

2번에 대해서는 동작 시 error 자체가 발생하지 않아서 return 값이 음수나 0으로 들어오는 경우도 없었습니다. ㅠ

감사합니다.

m4170의 이미지

질문하신 분의 말씀으로만 보면 오류가 날 확률이 있어 보입니다.
만약 수신부에서 readline()으로 header를 읽게 되면 처음 보낸 32Byte헤더의 일부분만 읽게되는 문제가 생길 것 같습니다. 즉 헤더크기를 32byte로 정의하셨으면 무조건 32byte를 처음에 수신하여 헤더를 확인하시는 것이 맞다고 생각이 듭니다.
(CR/LF가 header의 18byte정도에 기록이 되어 있다고 가정을 한다면 총 서버가 송신한 byte는 32 + filesize인데 반해 수신 byte는 18byte + filesize만 수신하게 되므로 소켓버퍼에 데이터가 남아 있는 현상이 발생할 것으로 보입니다.)

doutor0303의 이미지

readLine()은 여러모로 문제가될 수 있는 것 같아서,
read(), read(buf, 0, 32)로 대체하여 확인해보고 있습니다.
이렇게 하면 header를 읽는 루틴은 의도대로 32byte를 읽을 수 있고 header에서 읽은 filesize만큼 while문을 돌려 실제 파일을 받을 수 있다고 보고 있습니다.

근데 이상하게 이렇게 read()와 read(,,)를 사용해도 정상적으로 들어오지 않네요.

Anti-Lock의 이미지

BufferedReader 개체와 while 문 코드가 socket.getInputStream() 을 사용하고 있습니다.
의도한대로 동작하지 않을것 같습니다.
가령 BufferedReader.readLine 메서드가 소켓스트림에서 32 바이트보다 많이 읽어버릴 수 있습니다.

doutor0303의 이미지

readLine을 사용하지 않아도 문제인 상태라 완전 난감합니다.

함수를 찾아보니 setReceiveBufferSize 이런 것들이 보입니다.

이런 것들이 도움이 될까요?

doutor0303의 이미지

MFC(Server)쪽 파일 전송하는 부분에서

기존 : HEADER(32byte) + 이미지파일(가변)
변경 ; HEADER(32byte) + 더미(2048byte) + 이미지파일(가변)

이렇게 수정하면 Android App에서 한번은 정상적으로 받아 Bitmap 출력까지 됩니다.

Receiver(Android Application)쪽은 read() 함수로 32byte만 읽었습니다.

char header[32];
memset(header, 0x00, 32);
sprintf(header, "%s,%d\r\n", (CStringA)filename, pFile->GetLength());
header[30] = 0x0D; // 혹시 몰라 CR 추가
header[31] = 0x0A; // 혹시 몰라 LF 추가
send(hClient, header, 32, 0); // 32 크기만큼 전달
 
------------------ 추가 --------------------------
char dummyBuf[2048];
send(hClient, dummyBuf, 2048, 0);
------------------ 추가 끝 -----------------------
 
int sum=0;
while((send_cnt = pFile->Read(buf, 1024)) > 0) {
    int ret = send(hClient, buf, send_cnt, 0);
    sum += ret;
    if(errno == EINTR || errno == EWOULDBLOCK || ret <= 0) {
        AfxMessageBox(_T("전송 에러"));
    }  
}
if(sum != pFile->GetLength()) {
  AfxMessageBox(_T("전송 에러"));
}

소켓을 유지한 상태에서 다시 파일을 받으려는 시도를 해보면 또 Blocking 되네요(못 받은 크기가 8kb 정도)

소켓 끊고 다시 연결해서 파일 받기를 시도하면 한번은 정상적으로 받아지고;;;

이렇게 되면 보내는 쪽보다 받는쪽 문제로 봐야 할까요?

m4170의 이미지

아래 내용을 보시면 Receiver(안드로이드)가 전체를 다 받기 전에 Send쪽에서 socket을 끊어버리면 readLine함수가 블록킹될 수 있다고 나와 있습니다. 그러므로 전송을 하는 측에서 Send를 하는 즉시 바로 소켓을 닫지 말고 기다렸다가 안드로이드 쪽에서 전체 작업을 마무리 한 다음 소켓을 닫는 것으로 우선 테스트 해보시면 좋을 것 같습니다.
http://stackoverflow.com/questions/6139834/sockets-bufferedreader-readline-blocks/6139962#6139962

doutor0303의 이미지

readLine()은 header 정보를 한번만 읽는 구조라 socket.getOutputStream()에서 Block되는 구조에요.

도음 주셔서 감사합니다.

doutor0303의 이미지

추측으로 HEADER(32byte) 정보를 전달하고 바로 데이터(약 1mb)를 전달하면 위에서 말씀해주신대로 readLine()에서 많이 읽거나 문제가 될 수 있지 않을까? 싶어.

HEADER 전달
 
Sleep(500); // 임의의 Delay 500ms
 
파일 전송

이와 같이 작업하니 정상적을 파일이 받아지고 2048같은 dummy도 전달하지 않아도 되고 합니다.

그런데 왜 이런걸로 동작이 되는지 모르겠어요.

readLine()이라면 그럴수도 있겠다 싶은데 read로 32byte를 꼭 찝어서 읽었는데도 왜 이러는지 모르겠네요.

HEADER 크기를 1024만큼 키워서 해볼까 싶기도 했는데.. 퇴근하면 집에 가서 해봐야겠네요.

혹시 이런 상황에 대해서 예측 가능한 부분이 있으면 코멘트 부탁드립니다.

doutor0303의 이미지

추측으로 HEADER(32byte) 정보를 전달하고 바로 데이터(약 1mb)를 전달하면 위에서 말씀해주신대로 readLine()에서 많이 읽거나 문제가 될 수 있지 않을까? 싶어.

HEADER 전달
 
Sleep(500); // 임의의 Delay 500ms
 
파일 전송

이와 같이 작업하니 정상적을 파일이 받아지고 2048같은 dummy도 전달하지 않아도 되고 합니다.

그런데 왜 이런걸로 동작이 되는지 모르겠어요.

readLine()이라면 그럴수도 있겠다 싶은데 read로 32byte를 꼭 찝어서 읽었는데도 왜 이러는지 모르겠네요.

HEADER 크기를 1024만큼 키워서 해볼까 싶기도 했는데.. 퇴근하면 집에 가서 해봐야겠네요.

혹시 이런 상황에 대해서 예측 가능한 부분이 있으면 코멘트 부탁드립니다.

Anti-Lock의 이미지

본문의 header 를 받을때, readLine 을 사용하는 코드로 해봐도, read 로 해봐도..
PC 에 설치된 자바에서는 잘되는군요.
(c 프로그램은 localhost 로 전송, java 프로그램은 localhost 에서 대기/수신,
테스트 파일은 628 MB 크기의 zip 파일 사용)

E:\UserData\Desktop\test_java_socket\java>javac -version
javac 1.8.0_91

E:\UserData\Desktop\test_java_socket\java>java -version
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b15)
Java HotSpot(TM) Client VM (build 25.91-b15, mixed mode)

안드로이드는 없어서 해보지는 못했습니다.
안드로이드 java api와 TCP 가 PC와 미묘하게 다른 부분이 있어서 발생하는 문제일까요?

그나저나, header 구조를 개선할 필요가 있을것 같습니다.
긴파일이름 + 큰파일사이즈 가 32 를 쉽게 넘어갈수도 있을거 같네요.

댓글 달기

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