wirte(120k 정도데이터) 로 보내면 속도가 느릴때 .. 어떻게 해?

shean0의 이미지

안녕하세요..
프로그램을 하나 개발했는데.. 속도 때문에 최적화에 애를 먹고 있습니다.

일반적으로 보내는 데이터가 작을때는 문제가없는데.. 120k 정도되는 양을 보낼때는 tcp프래그멘테이션때문인지... 넘 느립니다.
이거 해결하려면.. 어떻게 해야하죠?
write(real-data)를 1024만큼 잘라서..
write(real-1);
write(real-2);
write(real-3);
이렇게 호출해야하는지... 이거참.. 난감하군요..

조언 부탁드립니다.

먼저 전체 소스구조와 시간을 표현하자면.
전체로직은 Recv_DATA에서 데이터를 받고.
           Make_Send를 호출 
                parse_data()에서 받은데이터 파싱하고.
                make_data()에서  real-data만들고
                write_header();        헤더 만들어서 보내고.
                write_data(real-data); 보낸다. 
이상입니다

소스는

int Magic10Main(int soknum)
{
	int pid = ((getpid()) % 100000);
	                  Trace_log(start);    //로그
                Recv_DATA();
	//	shutdown(sok,SHIT_RD); //다시read - block대기하므로..
	Make_Send();
		Trace_log(sendAll);    //로그
		shutdown(sok,SHUT_WR);
	read(sok,buf,1);//클라이언트 특징에의해서::client가 먼저 끝을때까지 기다린다
		Trace_log(All-END);    //		
}

Make_Send()
{
	parse_data();
		Trace_log(cmd);        //로그
	make_data();
		Trace_log(send-start); //로그
	write_header(data-header);     
	write_len(real-data);          //==>여기부터문제가 됩니다.
}
write_len(data)
{
	while(1) 
	{
		write(sok,data[pos],nleft);
		.../* 다 보낸다. */
	}
}

[start:20305]:[16:08:21]       //시작시각 현재 21초 [=>데이터 read하고]
[20305:cmd:13]:[16:08:21]   //cmd:13번 추출 :현재 21초 [=>데이터 모두 만들고]
[send-start]:[16:08:21]         //데이터보내기 시작              //즉 데이터 다 만들때가지는 금방인데..... 
//만들어지는 헤더는 고정길이 20바이트이고, 만들어서 보냅니다.
[sendAll:20305]:[16:08:36]   // 15초라니.. 데이터크기는 12k입니다. 
[ALL-END:20305 ]:[16:08:36]
 

이렇게 느리게 서버에서 : 데이터 write가 이루어지고.

클라이언트단에서는 테스트해보면
헤더는 금방 받아들여지는데..
즉. 요청->헤더 recv 까지는 금방이지만.
데이터 recv가 너무 느립니다...

이 현상을 어디서 부터 접근해서..해결할수 있을까요?

실제 write_len소스는

unsigned int write_len(int sok,char *buf,unsigned int len)
{
   unsigned int nleft, w_len;
   char *p;
   nleft=len;
   p=buf;
   while(nleft>0)
   {
      w_len=write(sok,p,nleft);
      if(w_len<0)
      {
          return ERROR;
      }
      else if(w_len==0)
      {
            ;
      }
      else ;
      nleft -=w_len;
      p +=w_len;

   }
   return (len-nleft);
}
liongo의 이미지

안녕하세요..

지나가다 얼핏 답글이 없어서.. 참고하시라고 올립니다..

참고만 하시길( 언제나 오답의 봉변을 회피하기위한 )

제가보기엔 Write시에 블러킹 되는것같습니다..

상대편이 Recv를 다해주지 않은것같네요..

보낸만큼 recv를 해주어야합니다.. 블러킹 상태라면..

send에서 상대방이 다 받을때까지 또는 타임아웃 상태까지

대기하게 됩니다.. 물론 이것은 소켓옵션에 따라 send옵션에

따라서 결과가 달라질수 있습니다.. 그럼 ^^

' 형식이 내용을 규정한다. '

mach의 이미지

저는 질문하신 분의 문제를 버퍼 문제로 찍어보겠습니다! :roll:

tcp kernel buffer크기를 조절해 보시기 바랍니다.
tcp는 자체적으로(커널내부의 프로토콜스택에서) reliability를 제공하고,
flow control과 congestion control을 제공합니다. 이때, 양단간에
통신버퍼사이즈가 잘~ 맞지 않으면 느린경우가 발생하기도 합니다.
특히, ftp와 같이 대량의 데이터를 전송하는 경우에 더 심합니다.
보통 튜닝하게 되지요.

부연하자면, 통신이란게, 혼자하는게 아니라 tcp통신에서는 적어도 둘(2)이 하지요.
보내기만 열심히 보낸다고 되는게 아니라, 받는측의 수준도 생각해 주어야 합니다.
받는 컴퓨터가 무지 빠르고 메모리 많다고 좋은게 아니고 통신버퍼세팅이
성능을 좌우하는 경우가 많습니다. 즉, 파라메터를 잘 맞춰야 하는데요.
보내고, 받는측의 파라메터를 맞출때, 가장 중요한 것은 네트워크
밴드위쓰(10메가랜 또는 100메가랜등등)도 있지만, 버퍼가 아주 중요한
요인이 되기도 합니다.
송신측, 수신측 둘다 소켓을 생성할때, 기본 소켓버퍼사이즈가 있는데,
(getsockopt()로 알아볼수있습니다)
이를 늘려보세요.

물론 실험적으로 늘리는 방법이 있고, 자동으로 이를 측정해서 늘리는 방법도
있을수있습니다.
일단은 실험적으로 늘려가면서 테스트를 해보세요.
당연히 클라이언트+서버 둘다요.

int bufSize = 64*1024; // 64KB로,... 이 사이즈를 가변적으로 실험해 보세요.
...
sock = socket(.....); // 소켓생성 직후에 세팅함
// 소켓의 송신버퍼 세팅
setsockopt(sock, SOL_SOCKET, SO_SNDBUF,  (char *) &bufSize , sizeof(bufSize ));

// 소켓의 수신버퍼 세팅
setsockopt(sock, SOL_SOCKET, SO_RCVBUF,  (char *) &bufSize , sizeof(bufSize ));
....
connet()또는 accept()가 이곳정도에 위치해야 함

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

최종호의 이미지

Snader의 Effective TCP/IP Programming 책
Tip 24. When Possible, Use One Large Write Instead of Multiple Small Write
에 보면 비슷한 현상에 대해서 기술하고 있습니다.

delayed ack의 timeout을 일반적인 200 ms로 잡고,
MSS를 1440으로 잡고 말씀하신대로 120K 의 데이터를 보낸다면
(본문에는 120K 라고 하신 것과 12K라고 하신 것이 있는데, 120K죠?)
Nagle 알고리즘과 Delayed ACK이 작용을 하면
(120K / 1440) * 200 ms = 약 16.7 초로
말씀하신 것과 비슷한 시간이 걸립니다.

책에는 Nagle 알고리즘을 disable 시켜서 이 문제를 *피해갈* 수 있다고 되어있습니다.
(Nagle 알고리즘을 disable시켰을 때의 부작용에 대해서도 언급하고 있습니다.)

const int on = 1;
setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));

의 코드로 Nagle 알고리즘을 disable시킬 수 있습니다.

(이 옵션에 대한 설명은 Stevens의 UNP vol.1 2판의 7.9절에도 나와있습니다.)

이상이 책에 언급된 내용이고요,,

책과 올리신 글을 읽고 의문이 생겼습니다.

말씀하신 것과 같은 문제가 발생할 수 있다는 것은 알고있었는데,
실제로 그와 같은 일을 제가 겪어보지는 못했었거든요.
한 1M 되는 데이터를 한 번에 다른 장비로 전송하는 테스트 코드에서도
delay없이 그대로 전송이 되었었거든요.

각본대로라면 client에서 계속 MSS씩 전송을 하면 서버쪽에서
delayed ACK에 걸려서 ACK을 보내주지 않는 경우
윈도우가 꽉차서 클라이언트에서는 더 이상 데이터를 못 보내고
서버쪽이 timeout 걸려서 ACK을 보낼때까지 기다려야 하는 상황이
발생해야 할텐데요.

이런 경우에는 {(보낼데이터크기 / MSS) / (서버윈도우크기 / MSS)} * 서버 delayed ACK timeout
만큼의 시간이 적어도 걸릴 것일텐데 그렇지가 않더라고요.

플랫폼을 윈도우-윈도우, 윈도우-Solaris, Solaris-윈도우로 해도 문제없이 잘 수행되고요.

그래서 뭔가 있나 ndd로 Solaris쪽 TCP tunable 을 살펴봤더니
tcp_deferred_ack_interval과 tcp_deferred_acks_max
라는 것이 있었습니다.
(윈도우쪽 TCP tunable 볼 수 있는 방법이 있나요?)

tcp_deferred_ack_interval은 delayed ack의 timeout 값을 지정하는 것이고,
값은 default로 100 ms 로 되어 있었습니다.

Solaris 메뉴얼에 tcp_deferred_acks_max 는

Quote:
tcp_deferred_acks_max

Description

The maximum number of TCP segments (in units of maximum segment size MSS for individual connections) received from remote destinations (not directly connected) before an acknowledgment (ACK) is generated. If set to 0 or 1, it means no delayed ACKs, assuming all segments are 1 MSS long. The actual number is dynamically calculated for each connection. The value is the default maximum.


와 같이 설명되어 있습니다.
(Solaris 10의 메뉴얼에서 가져와서 remote라는 용어가 있습니다.
Solaris 7에는 remote와 directly connected 호스트를 구별하지 않습니다.)

중요한 것은 delayed ack의 max값이 있어서
그 이상으로 데이터(MSS갯수)가 들어오면
piggyback할 서버쪽의 응답이없고 delayed ACK의 timeout이 발생하지 않더라도
ACK이 전송된다는 것입니다.
그리고 이 값은 최대값을 지정하며, connection별로 동적으로 계산됩니다.
(실제로 패킷을 잡아보았더니 초기에는 2였는데 3으로 올라갔습니다.
넷웍상황이 좋을수록 어느 정도까지는 값이 커지는 것이 유리하겠죠?)

아마 이런 이유로 제가 테스트했던 환경에서는 전송시간 지연이 나타나지 않은 것 같습니다.

제 테스트환경은 Solaris 7 (Sparc), Win 2000, Win XP, 100M Ethernet Lan 이었습니다.

아, 그리고 비슷한 문제를 다른 분이 겪은 적이 있는데(AIX 5L),
그 경우에는 버퍼 크기를 조정(줄여서)해서 문제가 사라졌던 적이 있습니다.
반대의 경우로는 Snader책 Tip 32: Understand the Effects of Buffer Size 에서 send buffer를 최소 3 * MSS로 하는 것을 추천하고 있습니다.
(일반적으로 기본값이 그 정도는 됩니다. -_-)
참고로 버퍼크기 조정은 listen이나 connect 하기 전에 해 줘야 한다는군요.

monac의 이미지

저도 비슷한 경험을 한 적이 있습니다.

Client-Server 프로그램에서 데이터를 주고 받는데,
1. 받는측이 windows 2000 (보내는측하고 관계없음)
2. 보내는 data size가 1463 인가 보다 작을때
200ms 의 delay가 생기는 것이었습니다.

처음에 이게 왜생기는지 별의별 테스트를 다 해보다가 (device driver 에서 생기는 문제여서..결국 문제는 device driver의 문제가 아니라 windows tdi 구현이 그렇더군요..) 위의 조합을 알아냈습니다.

그때 찾아보다가 이것이 MS Windows 2000의 ack timeout 이라고 하던데, 그때 당시에는 아주 data size를 조정할수도 없고, delay가 성능에 심각한 영향을 미치는 것이었습니다.

그래서 결국 client/server단에서 서로의 os를 알아내서 보내도록 protocol을 수정해서 (프로그램이 여러 플랫폼에서 돌아가기 때문에...) 위의 조건이 만족되면 data 크기가 1463이 되도록 쓰레기값을 집어넣어 수정했습니다.

참고 바랍니다.

PS: socket option 수정으로 해결 가능한지는 저도 모르겠습니다. 그때 당시에 문제는 device driver에서도 같은 network 문제가 발생하고, device driver에서는 socket option을 수정하는 interface가 없었거든요. 그래서 해결방안으로 고려되지 않았죠.

mach의 이미지

테스트를 해보셨는지 궁금하군요.

tcp 커널 버퍼크기의 중요성은 tcp의 ARQ(Automatic repeat ReQuest )에서
사용한 방법론에서 기인합니다. tcp의 구현에 따라 차이가 있을 수 있지만,
ACK가 수신되기 전에 송신할수 있는 데이터의 크기를 나타내는 window크기(sliding window algorithm)가
바로 그것입니다.(쉽게 쓰자면, 링버퍼, 환형버퍼-like한 ...거지요. ARQ만 가지고도 오랜 얘기가 됩니다.)
다시 말하자면, "10개를 보내는데, 1개보내고 응답받는게 아니라, 연속적으로 6개를 받고, ACK를 받는다면
성능에 보다 유리하다"는 얘기지요.
이 윈도우 크기는 초기 연결을 위한 3 way-handshaking에서 결정됩니다.
그래서, 윈도우크기 세팅(버퍼크기세팅)이 connect()또는 accept()보다는 빨리 이뤄져야합니다.

테스트의 편의를 위해서 간단한 도구를 하나 소개하겠습니다.
물론 제가짠거 아니고( 관리소홀로 현재 없음)... :oops:

Quote:
http://dast.nlanr.net/Projects/Iperf/
1) 가서 자신이 테스트할 환경에 타당한 소스(or executable)를 다운로드 받는다.
2) 테스트 대상이 되는 2대의 컴퓨터에 해당 프로그램을 설치
유닉스라면
make
make install
윈도우라면
자동압축해제 프로그램이므로 적절한 디렉터리에 압축을 푼다.

3) 테스트
(1) 서버가 되는 컴퓨터에서 다음과 같이 실행
iperf -s -w 128k
128KB의 커널 버퍼세팅(8k, 64k등으로 바꿔가며 밴드위쓰가 어떤게 가장 나은지를 필기한다.)
(2) 클라이언트가 되는 컴퓨터에서 다음과 같이 실행
perf -c 서버주소 -w 64k
64KB의 커널 버퍼세팅(8k, ...,128k등으로 바꿔가며 밴드위쓰가 어떤게 가장 나은지를 필기한다.)

(3) 테스트 결과 가장 나은 조합을 해당 컴퓨터간의 연결의 윈도우 크기로 세팅한다.
만일 다른 서버들과 통신이 혼용된다면, 이에 대한 고려가 필요함은 당연하다. 즉, 필요에 따라 파라메터를 달리 할 수 있다.
**(다시 부연하면 세팅은 connect()나 accept()가 되기 전에 해야한다. 방법? 앞의 글 .....위에 있다.)


보탬이 되었기를......
* 용어의 혼란이 있을까봐..
커널버퍼 = 커널 내부의 tcp /ip 프로토콜 스택의 송신 또는 수신버퍼

** 참고
또한 응용수준에서의 플로우콘트롤, 즉, 응용수준에서 프로토콜을 만들어서
1024정도로 나누어 전송하고, 응용수준에서 이에 대한 ack를 받게끔 프로그램을(응용 프로토콜) 만드는 것도 방법입니다.
저도 이를 많이 애용하기도 했습니다.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

shean0의 이미지

** 참고
또한 응용수준에서의 플로우콘트롤, 즉, 응용수준에서 프로토콜을 만들어서
1024정도로 나누어 전송하고, 응용수준에서 이에 대한 ack를 받게끔 프로그램을(응용 프로토콜) 만드는 것도 방법입니다.
저도 이를 많이 애용하기도 했습니다.

먼저 많은 조언해 주신 분들께.. 고개 숙여 감사드리며....

처음 소스는 write_len(sok,real-data[120k바이트]);
[write_len 소스는 위에 있죠^^* ] 하여서 system에 의존하다가..
1024로 짤라서

send-bigdata(sok,real-data)
{  ..
    count = real-data-size/1024;
    left     = real-data-size%1024;
    for(.. count ..) 
    {  .. 
        ret=write_len(sok,&real-data[position]);
        pos +=ret;
    }
    ret=write_len(sok,&real-data[position]);
    return pos+ret;
}
이렇게 수정해 보았는데도..별로 효과가 없더군요.
Quote:
즉, 응용수준에서 프로토콜을 만들어서
1024정도로 나누어 전송하고,
이것을 구현한것이라고 감히 생각되어... 문의드리는데..
Quote:
응용수준에서 이에 대한 ack를 받게끔 프로그램을(응용 프로토콜) 만드는 것도 방법입니다.
저도 이를 많이 애용하기도 했습니다.

이 말씀이 이해가 안되어서 이렇게 다시 조언을 부탁드립니다.

아..그리고 당연이 setsockopt이 걸린 프로세스만 영향을 주겠죠? ^^*

언제나 즐프를 꿈꾸며~

liongo의 이미지

recv 하는부분도 볼수 없을까요?

write부분만 가지고 유추하니까 애매한 문제들이 있어서..

몬가 놓치고 있는게 있지않을까 싶습니다..

' 형식이 내용을 규정한다. '

shean0의 이미지

[제목: 튜닝중.. 그런데 accept타임이 긴 이유는??]

말씀해 주신 부분들을 테스트 하고 있구요.
일단 아래 소스처럼 소켓 버퍼 사이즈를 조절하니..눈에 띄게 성능향상이 나왔습니다. 감사드리며.. 이왕에 시작한거.. 조금 더 공부하고자 합니다.. 귀찮아도 ^^*

에궁. ..
$uname -a
SunOS netra02 5.7 Generic_106541-10 sun4u sparc SUNW,UltraSPARC-IIi-cEngine
컴파일러 : gcc version 2.95.2 19991024

소켓-사이즈 튜닝분 소스 발췌입니다.
main()
{
    optval = 1;
    setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(optval));
	{/* test turnning 20040421 sean */
		int recvBufferSize=0;
		int sockOptSize=0;
		int setbufSize =64*1024; /* 현재 8192 */
//걸리는 시간은 ?? 
//8*1024[default], 
//16* 
//32* 
//64* 
/124 

		sockOptSize = sizeof(recvBufferSize);
		if(getsockopt(server_sockfd,SOL_SOCKET, SO_RCVBUF, &recvBufferSize, &sockOptSize) < 0 )
		{
			printf("getsockopt fail\n");exit(0);
		}
		printf("getsockopt recvBufferSize[%d]: sockOptSize[%d]\n",recvBufferSize,sockOptSize); 
		recvBufferSize *= 2;
		if(setsockopt(server_sockfd, SOL_SOCKET, SO_SNDBUF, &setbufSize, sizeof(setbufSize))<0)
		{
			printf("setsockopt fail\n");exit(0);
		}
		/*double buffer size setting : 20040421 */
	}/* <==turnning */

}

bind();
listen(sok, 5 ); 
while(1)
{
       accept();      //여기 fprintf(stderr, "accep time\n"); 
       // 요청이 많은경우 이 accept 가 나오는 것이 느린것은 .. ??
        pid=fork(); 
       if(pid==0) Child_Main(child_sok);
       ..      
}

전체 소스는 이런 구조이구.. 현재 버퍼 크기 조절을 통해 테스트 중입니다.

요청하신 recv부분 소스 입니다.
말씀하신 read소스 역시 wirte와 같은 구조입니다.

if( (ret = read_len(sok,buff,call_len)) != call_len)
{
    return ERROR;/*요청길이만큼 못 읽었다 */
}

unsigned int read_len(int sok,char * buf,unsigned int len)
{
  unsigned int r_len;
  unsigned int nleft,nread;
  char *p;
  int count=0;
  nleft=len;
  p=buf;
  while(nleft>0)
  {
     nread=read(sok,p,nleft);
     if(nread<0)
     {
       return ERROR;
     }
     else if(nread==0)
     {
          //count++; NET-delay가 큰 경우를 예상  
          return ERROR;  /*   break; EOF*/
     }
     else
     { ;
     }
     nleft -=nread;
     p +=nread;
     count++;
#ifdef NoUse
     if(count>100)break; /* 길이가 틀리기 때문에 에러로 처리된다 */ 
    else usleep(10);
#endif
  }
  return (len-nleft);  /* return >= 0 */
}

지금은 많은 분들이 조언해 주신 부분에 대해 살펴보고 있습니다.
그리고 .. 여기에서 한가지.. 문제에 대해 또 다른 조언을 구하고자 합니다.

listen과 accept 타임에 대한 것인데요.
여러 테스트를 하다 보니.. printf("accept time\n"); 이라고
서버에서 accept가 얼마나 빨리 일어나는가에 대해 살펴 보고 있었습니다.
그런데 프로세스 요청이 많을 경우에는 이 시간이 꽤 긴것 같더군요.
책을 찿아보니
listen()인자의 대기수가 이것을 빠르게 하는것두 아닌것으로 나와 있는데.
제가 오해하고 있는것 인가요??
짐작은
tcp_deferred_acks_max ,tcp_deferred_ack_interval 과 어떤 연관이 있지 않을까? 생각하는데.. ack 리턴되고. accept리턴되자 않을까? [상상만....]
"Snader의 Effective TCP/IP Programming 책"저두 구입해서 지금 보고 있습니다.. 이런 부분은 어떻게 테스트 하셨는지?? 최적화 방법은 어떤것이 있는지..부탁드립니다.

결론적으로..
아직 알려주신 문서들을 모두 살펴 보지 못하고.. 또 다시 문의를 드리게 되었네요.
현재는
1 . buf 사이즈 조절해서 테스트
2 . tcp_deferred_acks_max 를 찿아 보았는데..시도하기가 만만찮군요.ㅠㅠ
ndd 역시..

Quote:

http://athena.vvsu.ru/docs/tcpip/solaris_tcpip/tune.html#tdam
tcp_deferred_acks_max
Since 2.6: default 8, recommended ?, maximum 16
This parameter features the maximum number of segments received after which an ACK just has to be sent. Previously I thought this parameter solely related to interactive data transfer, but I was mistaken. This parameter specifies the number of outstanding ACKs. You can give it a look when tuning for high speed traffic and bulk transfer, but the parameter is controversial. For instance, unless you employ selective acknowledgments (SACK) like Solaris 7, you can only ACK the number of segments correctly received. With the parameter at a larger value, statistically the amount of data to retransmit is larger.

Good values for retransmission tuning don't beam into existence from a white source. Rather you should carefully plan an experiment to get decent values. Intervals from another site can not be carried over to another Solaris system without change. But they might give you an idea where to start when choosing your own values.


3.
longio님의 조언말씀 ^^*
블러킹 상태라면.. 
send에서 상대방이 다 받을때까지 또는 타임아웃 상태까지 
대기하게 됩니다.. 물론 이것은 소켓옵션에 따라 send옵션에 
따라서 결과가 달라질수 있습니다.. 

타임아웃에 대한 그냥.. 확인사살?? ^^* 아래 것은 저는 아래와 같이 구현합니다.
   alarm(3);
   write_len(sok,read_buff,read_size);
   또는 read_len(sok,read_buff,wirte_size);
   alarm(0); 

Quote:

http://www.rt.com/man/socket.4.html
SO_RCVTIME0 and SO_SNDTIME0
Specify the sending or receiving timeouts until
reporting an error. They are fixed to a protocol
specific setting in Linux and cannot be read or
written. They can be easily emulated using
alarm(2).

4 . 지금 솔라리스 다시 깔고 있습니다.
아무래두 http://dast.nlanr.net/Projects/Iperf/ 이놈 테스트하려구 다운 받고 보니.. 현재 운영중인장비에서 하기는 조금 ..거시기 해서요 ..
테스트 하는데로.. 여기에 공개하겠습니다.

와.. 할거 많네요..
^^* 모든 분에게 감사드리며.. 이만..

언제나 즐프를 꿈꾸며~

mach의 이미지

일단 사용자 수준의 flow control은 응용프로토콜 설계로 부터 시작합니다.
tcp위에다가 응용 프로토콜을 만드는것입니다.
응용프로토콜에서의 전송단위를 packet이라고 부른다면,
보통 다음과 같은 형태가 되겠지요.
packet => header + data라고 정의하자.
header => length;type (고정사이즈로 하자)
예 ) packet length : 4 byte ; 4B = 32 bits 따라서, 최대 4G가 가능하겠지만 1024, 4096모 이런정도로 제한을 하자, 플로우 컨트롤을 위해서.또는 거대 (불법,이상한?)패킷을 걸러내는 방법도 되겠지.
packet type : 4바이트
data => header에서 기술한 length만큼이 전달된다고 하자.

대략 프로토콜 포맷의 설계가 끝나면, 플로우를 설계하자.
모든 송신자는 data/control packet을 보내고 ack packet을 수신한다.
모든 수신자는 data/control packet을 받으면 ack packet을 송신한다.

다음으로 패켓을 만드는 함수, 송신하는 함수 수신하는 함수를 제작한다.

이런 방식으로 하면 프로토콜처리에 따른 오버헤드는 있지만, 보다 안전한(?)
클라이언트/서버를 만들 수 있을것입니다.

그리고, setsockopt()로 설정한 것은 당연히 해당 프로그램에만 영향이 미치게 됩니다.

** 질문하신 분의 경우에는 클라이언트 서버를 모두 제작하시는 듯하니, setsockopt()로 시도하시는 것도 방법이고, 자체 플로우 콘트롤을 제작하시는
것도 방법이겠습니다.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

최종호의 이미지

mach wrote:
...
대략 프로토콜 포맷의 설계가 끝나면, 플로우를 설계하자.
모든 송신자는 data/control packet을 보내고 ack packet을 수신한다.
모든 수신자는 data/control packet을 받으면 ack packet을 송신한다.
...

mach님 말씀대로, 120K를 1K 씩 나눠보낼 때 1K 보내고 ack 패킷 받도록 바꿔보세요.
올려주신 소스는 1K 씩 나눠보내게는 되어있는데(정확히는 안 봤는데 이 의도로보여집니다.. ^^),
더 중요한 것은 서버쪽에서 1K를 받은 후 ack을 보내주는 것이죠.
이 응용 수준의 ack에 TCP 수준의 ack 이 piggyback되어서 ack이 delay되는 것을 방지하는것이죠.

osanha님 올려주신 링크보니까 재밌는게 많네요.
server쪽의 delayed ack 관련 파라메터 튜닝도 한번 해 보시면 좋을 것같은데요.
delayed ack이 정말 문제인지 확인차 delayed ack을 disable시킨다든지 해서 가능성들을 줄여나가는 것이 좋을 듯 한데요.

제 생각으로는 버퍼 사이즈 조정으로는 좀 한계가 있을 듯한데,
버퍼 사이즈만 조정한다면 해당 소켓으로 한번에 120K가 아닌 1M 쯤을 때리면
delay가 계속 발생하지 않을까 생각이 듭니다.
120K일때는 클라이언트의 sndbuf 에 던지기까지만 하고 작업이 끝나고,
다른 job이 들어올 때까지의 시간간격이 TCP단의 delay보다 더 길다면 문제가 안 생기겠지만,
sndbuf를 꽉 채우고도 수행할 작업(보낼 데이터)이 더 있다면 그때부터는
TCP쪽의 delay가 문제를 일으킬 수 있지 않을까 하는 생각이 들어서요.

부하증가에 따른 accept 시간 지연은 스레드를 따로 분리하는 게 좋을 듯 합니다.

댓글 달기

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