listen()대한 질문입니다.

chakan01의 이미지

간단한 서버프로그램에서 소켓생성하고 bind한후에 listen(sock_fd, 1) 로 한다음에 accept로 기다리고 있습니다.

int listen(int s, int bakclog); 로 정의가 되어있는데,

bakclog는 대기큐의 최대갯수로 알고있습니다.

그러면, 위와같이 backlog를 1로 세팅을 하고,

클라이언트프로그램에서 소켓생성후 connect를 시도하면 당연히 서버에서는 accept를 합니다.

이후 클라이언트에서 계속 같은포트로 connect를 시도해도 connect함수가 에러를 리턴하질 않고 계속 connect가 성공합니다.

listen server가 없으므로, connect가 에러를 리턴해야 하지 않나요?

thepianis2의 이미지

accept() 함수가 접속한 클라이언트에 대한 소켓을 새로 생성해 리턴해 줍니다. 결국, 그 클라이언트에 대한 포트가 새로 할당되고...리슨닝 중인 포트는 계속해서 리스닝을 할 수 있습니다.

익명 사용자의 이미지

보통 다음과 같은 구성이겠죠?

handle = socket( ... );
if(handle >= 0)
{
  bind( ... );
  listen( handle, blog );
  do
  {
    AcceptHandle = accept( handle, ...);
    if(AcceptHandle >= 0)
    {
      /* 뭐 하는겨... */      
      close(AcceptHandle);
    }
  }while( ! IsExit );

 close(handle);
}

그런데 여기서 "뭐하는겨" 하는 부분에서 엄청난 지연이 발생한다면
그동안은 listen에서 지정된 백로그만큼의 연결은 성공합니다.
하지만 그 이상을 지연되면 연결은 실패합니다.

그래서 "뭐하는겨" 하는 부분은 쓰레드나 포크로 재빨리 넘겨버리고
다시 accept로 가는 시간을 줄입니다.

그래서 대부분 listen의 백로그는 5정도면 충분하다는 예기가 있고요.....

만약 백로그가 1개로 되어 있고
accept한후 다음 accept하기까지의 시간
즉, accept를 하지 않고 대기하는 시간동안의 연결은 받지 못하는 사태가 발생합니다.

질문하신분의 결과와 다른듯 하지만 소스보면 증거를 잡아드리죠.

chakan01의 이미지

코드는 다음과 같이 간단합니다.

server쪽 코드.. (클라이언트에서 오는 데이타읽어서 echo해줌)

sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

memset(&sock_in, 0, sizeof(sock_in));
sock_in.sin_family = AF_INET;
sock_in.sin_addr.s_addr = inet_addr(ipaddr);
sock_in.sin_port = htons(port);	

bind(sock, (struct sockaddr*)&sock_in, sizeof(sock_in));

while(1) {
	if (listen(sock, 1) == -1) {
		printf ("listen Error\n");
	}
	
	sin_size = sizeof(struct sockaddr_in);  
		
	client = accept( sock, NULL, NULL );

	while( 1 )
	{
		int dlen;

		printf("wait recv....., %d\n", client);
		dlen = recv( client, packet, PACKET_LEN, 0 );

		if( dlen == 0 || dlen == SOCKET_ERROR )
		{
			printf("disconnect client\n");
			break; 
		}

		packet[dlen] = '\0';
		printf("Success some packet; %s, %d\n\n", packet, dlen);

		printf("try to send....., %d\n", client);
		dlen = send( client, packet, dlen, 0 );
		if( dlen == 0 || dlen == SOCKET_ERROR )
		{
			printf("disconnect client\n");
			break;
		}
		printf("complete to send....., %d\n", client);
	}
		
	if (close(client) == -1)
	{
		printf("error to disconnect client; %d,");
		break;
	}
}


------------------------------------------------------------

client쪽 코드..


if ((sockfd = socket (AF_INET, SOCK_STREAM,0)) == -1)
{
        perror( "socket" );
        exit(1);
}

address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_port = htons(50000);

len = sizeof (address);

rc = connect( sockfd, (struct sockaddr *)&address, len);
if(rc != 0)
{
   perror( "accept" );
}
else
{
    printf ("conn success = rc = %d\n", rc);
}


여기서에서 connect를 연속으로 호출을 한다면 서버의 listen의 backlog가 1로 세팅되어있고, 최초에 한번 클라이언트와 connect가 되어있으면,
두번째로 클라이언트가 connect를 하면 서버측의 listen이 없으므로 connect함수가 에러를 리턴해야 하지 않을까 입니다.
사실 backlog의 의미가 좀 와닿지가 않습니다. 클라이언트 대기큐갯수라는데, 1로했음에도 물론 accept는 한번만 수행되지만. connect를 에러없이 계속 성공하는것이 궁금합니다.

윈도우에서 윈속으로 위의 서버프로그램을 조금 수정하여도 결과는 다르진 않겠죠?

열씨미!

lunarainbow의 이미지

어? 제 실력 부족탓인지...

제가 보기엔 하나의 client를 접속 받은후, 더이상 받지 못할것 같습니다만...

listen 함수에 적어주는 숫자는 "대기열"이라 알고 있습니다.

"동시 접속자 수"를 설정해 주는 것으로, 동일한 시간 또는 약간의 지연 탓으로 일시적으로 접속을 받아주지 못할경우 연이어 오는 접속 요청을 넣어두는 큐 정도로 생각하시면 될것 같습니다.

그리고.. 이것을 이용한 해킹방법으로... 윽.. 이름은 모르겠네요. ^^;;

암튼 connect 요청을 한번 하고, 그쪽에서 다시 확인을 위해 이쪽으로 메세지를 보내면.. 그것에 대한 응답을 하지 않도록 만듭니다.
이 작업을 while문으로 돌리면, 언젠간 listen 큐가 모두 차 버릴것이며, 그것은 더이상의 새로운 사용자의 접속 요청을 받아 들일 수 없을 것입니다.
이렇게 되면 각종 서버측의 리소스가 널널히 남는다 할지라도 마치 죽은것처럼 보이겠죠?

음... 왜 또 말이 샜을까..?

아무튼 그냥.. 그런것도 있다는 말이었습니다. ^^;;

아마 지금은 저런 방법 막히지 않았을까 싶은데.. 그렇겠죠?;;

에고.. 초보의 '나름대로' 답변 이었습니다.

chakan01의 이미지

답변 주신분들에게 감사드립니다.

스티븐아저씨가 쓴 책에서 보면,

listen에 backlog는 대기큐의 최개갯수라고하며, 이것은 imcomplete connection queue와 completed queue 두개 모두 합한것의 갯수라고 되어있는것 같습니다.

그럼 서버가 클라이언트로부터 SYN J을 받으면, 우선 imcomplete queue에 하나가 쌓이고, 이어서 클라이언트에게 SYN K, ack j+1를 줍니다.
클라이언트가 이것을 받으면 conncet함수가 리턴됩니다.
그리고 서버에게 ack K+1을 주고 서버가 이를 받으면 accept가 리턴이 된다고 나와있습니다.

그러면, listen(sock_fd, 1)로 세팅후에 이후에 클라이언트가 서버에게 connect를 요청하면, 서버의 imcomplete queue에 계속 쌓일것 같습니다.
현재 위의 코드로보면 fork()를 안하므로 recv()에 블록이 되어있을것이고,
서버의 대기큐의 갯수를 1로 했음에도 클라이언트의 connect는 에러를 내지 않습니다.

도저히 잘 이해가 안갑니다...

열씨미!

서지훈의 이미지

chakan01 wrote:
여기서에서 connect를 연속으로 호출을 한다면 서버의 listen의 backlog가 1로 세팅되어있고, 최초에 한번 클라이언트와 connect가 되어있으면,
두번째로 클라이언트가 connect를 하면 서버측의 listen이 없으므로 connect함수가 에러를 리턴해야 하지 않을까 입니다.

listenQ는 accepted 되기 전에 대기하는 곳이니깐...
아마 님의 말대로라면...
backlog를 0으로 하면은 원하는 결과를 얻을 수가 있을 겁니다.
그렇지 않고...
1로 한다고 한다면...
처음 접속
두번째 접속
세번째 접속 - 여기에서 에러를 뱃으면서 뻗을 겁니다.
확인을 해보세요...
물론 첫번째, 두번째 접속 중인 상태에서 세번째 접속을 시도...

<어떠한 역경에도 굴하지 않는 '하양 지훈'>

#include <com.h> <C2H5OH.h> <woman.h>
do { if (com) hacking(); if (money) drinking(); if (women) loving(); } while (1);

chakan01의 이미지

backlog값을 0으로 하면 처음 connection부터 connection refused error가 나네요.. --;

열씨미!

bugiii의 이미지

그 1이 하나를 뜻하는 것이 아니라 OS 마다 틀린 어떤 상수값이 곱해진다고 언뜻 본 것 같은데요... 맞나요?

댓글 달기

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