소켓 프로그래밍 중인데 데이타가 계속 짤리네요
글쓴이: a287848 / 작성시간: 금, 2008/04/18 - 12:47오후
2410 ARM 타겟보드에서 2개의 프로그램이 소켓 통신을 합니다.
하나는 Java로 짠 프로그램이고
다른 하나는 C로 통신을 하고요.
C는 서버 역활을 하고
Java 는 클라이언트 역활이 되어서 약 30kbytes 정도 데이터를
C 서버에게 데이터를 송신하는데
보내는 녀석은 30,000 바이트를 다 보낸다고 보내는데
받는 쪽에서 확인해보면
데이터가 16384바이트만 수신합니다.
버퍼 크기를 늘려보기도 했고요, 물론 소스코드에서는 setsockopt 와 자바함수로 버퍼를 충분히 크게 해 놨습니다. 왜 짤리는걸까요?
C 서버 코드입니다.
socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) ... 중략 do { memset(buf, '\0', sizeof(buf)); //add nbyte = recv(s, buf, MAX_LINE,MSG_DONTWAIT); if(nbyte <= 0) { szCmdMsg = (char*)realloc(szCmdMsg, iCmdMsgLen+1); break; } else if(nbyte > 0) { szCmdMsg = (char*)realloc(szCmdMsg, iCmdMsgLen+nbyte); memcpy((szCmdMsg+iCmdMsgLen), buf, nbyte); iCmdMsgLen += nbyte; continue; } }while(nbyte != 0);
다음은 자바 코드이고요
DataOutputStream dos = null; dos = new DataOutputStream(skt.getOutputStream()); System.out.println("resXML : " + resXML.length()); System.out.println("Sending buffer size: " + this.socket.getSendBufferSize()); System.out.println("Receving buffer size: " + this.socket.getReceiveBufferSize()); dos.write(resXML.getBytes()); dos.flush();
다음은 버퍼 크기를 조정 하기 위해서 내린 명령어들입니다.
[root@host internal]# echo "174760 174760 174760" > /proc/sys/net/ipv4/tcp_mem [root@host internal]# echo "174760 174760 174760" > /proc/sys/net/ipv4/tcp_rmem [root@host internal]# echo "174760 174760 174760" > /proc/sys/net/ipv4/tcp_wmem [root@host internal]# echo "65536" /proc/sys/net/core/wmem_max [root@host internal]# echo "65536" /proc/sys/net/core/rmem_max [root@host internal]# echo "65536" /proc/sys/net/core/wmem_max_default [root@host internal]# echo "65536" /proc/sys/net/core/rmem_max_default
Forums:
방법 1: 매 루프에서
방법 1: 매 루프에서 recv의 리턴값을 체크해서 -1 인 경우 errno 값을 확인해보세요.
방법 2: strace 로 c로 작성한 서버의 호출을 추적, java 로 실행하는 클라이언트
호출도 추적해 보세요.
방법 3: tcpdump 을 사용할 줄 안다면 데이타 전송을 추적해 보세요.
---------
간디가 말한 우리를 파괴시키는 7가지 요소
첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스
이익추구를 위해서라면..
다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치
---------
간디가 말한 우리를 파괴시키는 7가지 요소
첫째, 노동 없는 부(富)/둘째, 양심 없는 쾌락
셋째, 인격 없는 지! 식/넷째, 윤리 없는 비지니스
이익추구를 위해서라면..
다섯째, 인성(人性)없는 과학
여섯째, 희생 없는 종교/일곱째, 신념 없는 정치
패킷이 나눠져서 가니까 그런 것 같습니다.
보여주신 코드를 봐서는 recv에서 0을 돌려주면 루프를 빠져나오는 것 같은데, 제가 아는 한 30K를 하나의 패킷으로 보내는 경우는 없으므로 패킷이 여러 개로 나눠져서 가고, 이중 앞부분만 서버쪽 OS가 받아들인 상태에서 application이 이를 다 읽으면 recv는 0을 돌려줍니다. (음 아니면 -1인지도 모르겠네요. man page를 확인해 보심이...)
recv 입장에서야 앞으로 패킷이 더 올지 말지 알 턱이 없으니 버퍼가 비었으면 0을 돌려줘야죠.
제대로 하시려면 미리 정해진 사이즈만큼 무조건 읽거나, 데이터 시작부분에 "이 데이터는 몇 바이트짜리임"이라고 적어놓고 그만큼이 될 때까지 읽으셔야 합니다. (패킷이 작아도 마찬가지입니다. 아마 패킷이 작다고 해서 무조건 잘라지지 않고 간다는 보장 같은 건 안 될 걸요... 시작점에서 종점까지 모든 네트웍을 관리하는 상태라면 또 모르겠습니다만.)
Data를 한번에 많은
Data를 한번에 많은 양을 읽거나 쓰여질 것이라는 보장을 하지 않는 것이 좋습니다.
30000Byte를 전송시키면... 몇번에 걸쳐 나누어가지 않을까 싶습니다.
Data를 보냈을 때 확실하게 해당 Byte만큼 전송했는지 확인을 거쳐야하고.
Data를 읽을 때 recv() 호출 한번으로 30,000Byte를 읽을 수 있다고 가정하진 마십시요.
이러한 가정을 세웠다면 위험한 발상입니다.
위와 같은 방법을 응용해보시면 답이 나올 것이라 생각됩니다.
(절대로 위와 같이 recv함수를 두번 넣진 마시고 효율적인 코드를 만드시기 바랍니다.)
그리고 단순하게 while문장을 반복하실 것이 아니라.
해당 select()함수 등을 이용하여 해당 Socket에 Event가 있는지
검사 후 recv()를 호출하는 것도 좋은 방법입니다.
각 함수에 대한 설명은 man페이지를 꼭 참고해보십시요.
nonblock io
MSG_DONTWAIT로 nonblock io로 받으시기 때문에
error case시에 errno가 EAGAIN 일 경우 계속 recv를 하셔야 합니다.
그리고 recv의 return이 0인 경우는 error 이거나, 읽을 data가 없을때가 아니라
socket이 close 되었을 경우를 가리키니 따로 처리를 해주시는 것이 좋을 것 같습니다.
server쪽에서 memcpy를 하는 부분도 buffer에 직접 offset을 조정하는 방법으로 받으시는 것이
성능상 좋습니다. memory copy는 server 성능 장애의 요인이 될 수 있습니다.
다른 non block 이후에 특별한 일을 처리하시지 않는 것 같아보이니,
위의 구조에서는 MSG_DONTWAIT을 설정하실 필요가 없어보입니다.
I thought what I'd do was,
I'd pretend I was one of those deaf-mutes.. or should I?
댓글 달기