영원한 숙제.. 소켓 질문 입니다 -_-;;
안녕하세요..
후...
소켓..예.. 알만하다 싶으면 모르겠고..
일단, 제가 테스트로 알아보려고 하고 있는것은, 넌 블럭킹 TCP/IP에서
클라이언트 어플이 send로 메세지를 다량으로 보내는 동안, 서버 어플이 recv를 하기 전까지는, 클라이언트가 보내는 다량의 메세지는 서버 어플이 recv를 해 갈때까지 커널에서 버퍼링을 해 두고 있을텐데, 그 버퍼링의 크기가 어느정도인지를 알아보려고 하고 있습니다.
즉, recv로 실제 도착해 있는 데이터를 어플이 가져가기 전까지 정확히 얼마나 많은 데이터를 커널이 버퍼링을 해주는가가 궁금한건데..
테스트를 해본다고 했는데 잘 모르겠습니다. proc/sys/net/core/rmem_max
이 값이 그 버퍼 크기라고 하던데 이 값(131071) 보다 더 버퍼링이 되는것 같던데요.. -_-;;
그냥 평범한, 간단한 server/client 샘플로 테스트 중입니다.
아래는 클라이언트가 1초씩 루프를 둘며 202400 바이트를 보내고 있습니다.
unsigned long int nTcpLen, nSendMsgLen; char szSendMsg[202400]; memset(szSendMsg, 'c', sizeof(szSendMsg)); nSendMsgLen = send(tcpSockFd, szSendMsg, sizeof(szSendMsg), 0); if (nSendMsgLen == sizeof(szSendMsg)) printf("Tcp Client send length %d Msg\n", nSendMsgLen); else printf("nSendMsgLen = %d, sizeof(szSendMsg) =%d\n", nSendMsgLen, sizeof(szSendMsg));
일단 1번 질문입니다. send에서 한번에 전송되는 양이, 처음에는 65532, 즉 64K가 나오길래..음..한번에 보내지는 최대전송량은 64K구나 싶은 순간.. 다음에 131072바이트를 한꺼번에 보내버리던데.. 이건또 먼 조화인지..
nSendMsgLen = 65532, sizeof(szSendMsg) =202400
nSendMsgLen = 131072, sizeof(szSendMsg) =202400
nSendMsgLen = 16384, sizeof(szSendMsg) =202400
nSendMsgLen = -1, sizeof(szSendMsg) =202400
질문 2번... 여기 -1시점에서는 서버가 recv를 안하고 있으니 서버쪽 커널 소켓 버퍼링이 꽉 찼다는 신호일텐데..그렇다면 서버쪽 커널 소켓 버퍼크기는 65535+131072+16384 바이트라고 생각이 되는데...그래야 되는게 아닌가요?
그렇다면 아래의 서버쪽 소스에서 15초를 기다렸다가 recv를 하게 되어 있으므로, recv에서 위의 65535+131072+16384 바이트를 한꺼번에 긁어와야 될듯 한데..
이게 또 그렇지 않은걸로 봐..그게 아닌것 같다는게 -_-;;
int nTcpLen, nRecvMsgLen; char szRecvMsg[181072]; sleep(15); nRecvMsgLen = recv(newSockFd, szRecvMsg, sizeof(szRecvMsg), 0); if (nRecvMsgLen <= 0) { printf("Tcp Server : Connection closed, recv nRecvMsgLen = %d\n", nRecvMsgLen); printf(">> Tcp Server : wating new client connetion request... \n"); close(newSockFd); exit(0); } else printf("Tpc Server rcvd %d length Msg\n\n", nRecvMsgLen);
서버에서는 15초가 지난후, 그러니까 클라이언트는 위의 send에서 -1이 리턴되고도 한참 지난뒤에 한꺼번에 가져오는 값이 고작 77148입니다...
위에서 말대로라면 서버 커널 버퍼에는 65535+131072+16384 바이트가 버퍼링 되어 있을테니 이게 한꺼번에 받아져야 되는게 아닌가요...
클라이언트가 보낸 데이터가 아직 77148밖에 도착안했다고 보기도 무리인게, 로컬에서 테스트한데다가, 15초나 기다렸는데...
There is an message from client
Tpc Server rcvd 77148 length Msg
There is an message from client
Tpc Server rcvd 81592 length Msg
There is an message from client
Tpc Server rcvd 138832 length Msg
There is an message from client
Tpc Server rcvd 177768 length Msg
There is an message from client
Tpc Server rcvd 174604 length Msg
어ㅤㅉㅒㅅ든, 위의 서버 클라이언트 프로그램을 물려놓고 한참 돌리면 가끔 위에서 처럼 한번에 174604 바이트를 가져오기도 하고.. 그러는데요..
이런 현상을 좀 어떻게 명쾌하게 설명해 주실수 있는분 없으실까요?
이게 머 몰르고 넘어가도 소켓 프로그래밍 하는데는 아무 지장 없습니다.. 데이터는 손실 없이 잘 오니까요.. 근데 어ㅤㅉㅒㅤ 뭔가 앞뒤가 안맞는듯한게.. 가려워서 그러는데요..
좀 시원~하게 긁어주시면!!
무지 감사 드리겠습니다. ^^
에휴~ 제 글이 이해가 가셨는지나 모르겠네요..설명을 잘 드렸는지..
그럼, 여러 경험 많으신, 고수님들 부탁드리겠슴다~
꾸ㅤㅂㅓㄲ~
근거 없이 맘대로 상상해서 한번 써보겠습니다;;send()와 re
근거 없이 맘대로 상상해서 한번 써보겠습니다;;
send()와 recv()의 결과값이 202400이 아니라 오락가락 하는 건, 함수가 데이터를 보내는/받는 작업을 열심히 수행하다가 인터럽트를 받아서(timetick을 다 써버렸다거나, 등등) 지금까지 보낸/받은 양만을 반환하는 것 아닐까 싶습니다. 이만큼이나 로드를 주시는데 걔네들도 힘들지 않겠습니까:)
보내는 측에서 상대측이 수신을 시작하기 전까지 rmem_max값만큼이 아니라 그 이상으로 send()를 호출할 수 있는 건 송신 장비 측의 송신 버퍼 때문이 아닐까 하고 상상의 나래를 펼쳐 봅니다.
$PWD `date`
짧은 지식으로 말씀드리겠습니다.socket buffer의 크기는
짧은 지식으로 말씀드리겠습니다.
socket buffer의 크기는 네트워크의 혼잡도(congestion)에 따라 가변적입니다.
send()나 recv()는 프로그래머가 보내고자 하는 데이터를 한 번의 호출로 다 보내어 준다는 보장이 없습니다.
그래서 send()나 recv() 함수는 return값으로 프로그래머에게 지금 몇 바이트만큼 보내었고, 받았는지를 알려줍니다.
이 return값을 가지고 프로그래머는 나머지 데이터를 송,수신 하여야 합니다.
혼잡이 없으면, 이를 늘리고, 혼잡이 있으면 이를 줄인다고 알고 있습니다.
네트워크가 혼잡한데 사용자가 많은 양의 데이터를 보내지 못하게 막는 기능을 한다고 생각할 수 있습니다.
제 생각에는 그래서 아래와 같은 현상이 나타났다고 보여집니다.
64KB보내고 그 다음에는 더 많이 보냅니다.
즉, 보낸 64KB의 데이터가 원격 노드에 잘 전달되었고 원격노드로부터 ACK를 받았다는 얘기이겠죠.
그 다음에 128KB를 보내는데, 이건 제대로 다 전달되지 못했습니다. 왜냐면 다음 send시 더 적은 데이터만을 보낸걸로 알 수 있습니다.
마지막의 -1 에러값은 에러코드로 정확한 원인을 파악해 보시길 바랍니다.
더 말씀드리고 싶은데, 정확하지 않은 지식을 말씀드리기가 그렇네요.
질문에 답하면서 느끼는 거지만, 저부터 '공부 더 해야겠다'란 생각 많이 듭니다.
TCP/IP는 비동기입니다.-----------------------
TCP/IP는 비동기입니다.
--------------------------
송신측 프로그램 send()
-------------
송신측 커널의 송신버퍼가 일단 받는다. 그리고, 송신측 프로그램(응용)과 관계없이 커널이 보낸다. 핸드쉐이킹 플로우컨트롤등 하면서 ....
수신측 커널의 수신버퍼 : 버퍼에 빈공간이 있으면 일단 받는다. 당연히도, 핸드쉐이킹 플로우컨트롤등을 해야겠지...
-------------
수신측 프로그램 recv() <= 프로그램에서 호출하면 커널의 수신버퍼에서 받는다. 엄밀하게 따지면, 원격지에서 받는게 아니다라고 이해하는게 좋을 수도 있다.
-----------------------
* 따라서, 블록킹모드로 보낸다고 할지라도, 송신측에서 send()하면 원격지 커널이 받았을때(응용이 아니라...) send()에서 리턴하게 될 것이다.
이 중에서 수신측 프로그램을 정지(?)시켜 놓고 그 버퍼링크기를 검사하는 것은 별로 타당하지 않군요. 송신측 커널과 수신측 커널의 둘다의 버퍼를 고려해서 그 크기를 예측해 볼 수 있겠으나, 그냥 쉽게 getsockopt()에 파라메터 주어서, 현재의 버퍼 크기를 읽어와 보세요.
** 이런 경우에는 양단에 tcpdump와 같은 유틸리티로 해당 세션을 스니핑하면서, tcp헤더 부분에서 SEQ, ACK, Window Size등을 참고하면서 보세요.
....보다 명확해질 수 있을것으로 보입니다만.......
** dump는 역시 ethereal이 좋지요?
음.. 답변 감사드립니다.흠.. send() 리턴값은 수신측 커널이
음.. 답변 감사드립니다.
흠.. send() 리턴값은 수신측 커널이 받았을때다.. 예 그런것 같네요.
그런데 getsockopt는 저도 해봤었는데.. 제가 궁금한게 바로 그거라서 저런 삽질 테스트를 했던건데, getsockopt에서 나온 버퍼 크기가, 저 같은 경우는
87380 이 나오는데..
그렇다면 수신측 프로그램을 정지..그러니까 recv()를 호출 안하고 대기시켜놨다가 호출했을때는 최대 87380까지만 읽어와야 되는게 아닌가 했는데 위에서 보시는바와 같이 그게 아니라서 저를 혼란에 빠뜨리고 있는것 입니다..
그나저나 ethreal 저도 전부터.. 함 써볼라고 몇번 찝쩍거려 봤는데..
외적인 질문인데 제가 "message"라는 스트리을 보냈다면, ethreal로 "message"라는 스트링을 그대로 볼수 있나요? 제가 이것좀 봐볼라고 해봤는데.. 영 .. 쉽지가 안던데요.. -_-;;
간략하게라두 ethreal GUI에서 뭘 건드려야 되는지 대~충...이라도.. 알려주심..
감사하겠습니다 ^^
댓글 달기