select 에 대해.....

살자의 이미지


int main(int argc,char *argv[])
{
        int i,listenfd,connfd,sockfd;
        int maxfd,maxi;
        int nready,clilen,client[FD_SETSIZE];
        fd_set rset,allset;

        struct sockaddr_in cliaddr,servaddr;

        if((listenfd = socket(AF_INET,SOCK_STREAM,0)) < 0)
        {
                perror("Can not open socket");
                exit(1);
        }

        bzero((char *)&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(PORT);
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

        if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)
        {
                perror("error bind");
                exit(1);
        }

        if(listen(listenfd,WQUEUE) < 0)
        {
                perror("error listen");
                exit(1);
        }

        maxfd = listenfd;
        maxi = -1;
        for(i = 0;i < FD_SETSIZE;i++) client[i] = -1;
        FD_ZERO(&allset);
        FD_SET(listenfd,&allset);

 for(;;)
        {
                printf("for\n");
                rset = allset;          /*allset에서 설정된 bit를 rset으로 넘겨준다.
                printf("for1\n");
                if((nready = select(maxfd + 1,&rset,NULL,NULL,NULL)) < 0)
                {
                        perror("error select");
                        exit(1);
                }
                if(FD_ISSET(listenfd,&rset)) 
                {
                        clilen = sizeof(cliaddr);
                        if((connfd = accept(listenfd,(struct sockaddr *)&cliaddr,&clilen)) < 0)
                        {
                                perror("error accept");
                                exit(1);
                        }

                        printf("connection clientt.... \n");
                        for(i = 0;i < FD_SETSIZE;i++) 
                        {
                                if(client[i] < 0)
                                {
                                        client[i] = connfd;
                                        break;
                                }
                        }

                        if(i == FD_SETSIZE) perror("too many clients");
                        FD_SET(connfd,&allset);
                        if(connfd > maxfd) maxfd = connfd;
                        if(i > maxi) maxi = i;
                        if(--nready <= 0) continue;
                }

                /*----  client communication----*/

                for(i = 0;i <= maxi;i++)
                {
                        if((sockfd = client[i]) < 0)
                                continue;

                        if(FD_ISSET(sockfd,&rset))
                        {
                                close(sockfd);
                                FD_CLR(sockfd,&allset);
                                client[i] = -1;

                                if(--nready <= 0)
                                        break;
                        }
                }
        }
}

코드 중간에 디버깅을위해 for와 for1을 printf를 사용하여 체크해 봤는데..
for만 나오고 for1은 나오지 않습니다.
rset = allset 이 문장에서 문제가있는것 같은데...
어떤문제인지 모르겠습니다.
책보구 했는데 안돼네여...
몇시간째 보구 있는데 도저히 모르겠습니다.
제발 도와주세요...

xfmulder의 이미지

FD_ZERO(&allset);
FD_SET(listenfd,&allset);

이 문장은 for 문 안에 넣어야 할듯.

printf(); 다음에 꼭 flush(stdout); 하시고...
memcpy(&rset,&allset,sizeof(allset)); 으로 넣어보세요.

내 자식들도 나처럼 !!

purewell의 이미지

윗분... for 안에 FD... 넣을 필요는 없다고 생각합니다.

그리고 윗분 말씀처럼 printf 다음엔 꼭 flush 해주세요.

아니면 printf 대신 fprintf(stderr, "...."); 를 써보시던지요.

_____________________________
언제나 맑고픈 샘이가...
http://purewell.biz

cjy1126의 이미지

저도 전에 이런 비슷한 오류가 있었습니다.

위와 비슷하게 allset을 for문 밖에서 셋팅했는데,

select를 한번 실행하고 2번째부터 쓰레기가 들어가더군요.

select에서는 readset으로 받아서 allset이 변할 수가 없는데...

왜 for 안에서 선언을 해야하나요?

어차피 밖에서 값을 한번 셋팅하고 바꾸지않으면, 안에서 계속할 필요는 없지않나요?

hskim76의 이미지

fd_set은 구조체로 알고 있습니다만,

rset = allset;

이라는 문장이 C에서 성립하나요?

rset = allset; 문장에서 블락이 되어다기 보다는

rset에 잘못된값이 들어가서 select에서 블락이 된것 같습니다.

memcpy등을 이용해서 rset에 allset 비트를 카피해보세요.

MasterQ의 이미지

hskim76 wrote:
fd_set은 구조체로 알고 있습니다만,

rset = allset;

이라는 문장이 C에서 성립하나요?

구조체가 아니구 array로 되어있습니다.

고로 rset = allset은 성립합니다.

MasterQ의 이미지

살자 wrote:
                /*----  client communication----*/

                for(i = 0;i <= maxi;i++)
                {
                        if((sockfd = client[i]) < 0)
                                continue;

                        if(FD_ISSET(sockfd,&rset))
                        {
                                close(sockfd);
                                FD_CLR(sockfd,&allset);
                                client[i] = -1;

                                if(--nready <= 0)
                                        break;
                        }
                }

윗분 말씀대로 select에서 block된것 같군요.

client로 접속하신것 맞나요?

telnet <IP> <PORT> 로 테스트해보세요..

for1이 그때 나올것 같군요.. 강제로 나오게 하시려면 fflush를 해보세요.

근데.. 접속되두... 위의 코드처럼 클라이언트를 대접하면...

고객(client)들이 별로 안좋아할것 같은데요? :P

(접속만 하면 강제로 짤라버리니까요...FIN FIN FIN FIN....)

MasterQ의 이미지

cjy1126 wrote:
select에서는 readset으로 받아서 allset이 변할 수가 없는데...

왜 for 안에서 선언을 해야하나요?

FD_SET(connfd,&allset);

cjy1126 wrote:
select를 한번 실행하고 2번째부터 쓰레기가 들어가더군요. <snip...> 어차피 밖에서 값을 한번 셋팅하고 바꾸지않으면, 안에서 계속할 필요는 없지않나요?

select는 return될때 읽을수 있는 descriptor set 을 업데이트하기 때문에 그런걸로 알고있습니다. i.e. 매번 바뀜.

그렇기때문에 allset은 우리가 관심있는 descriptor set(listenfd [+ connfd...]) 들을 bookkeeping해주는 역활을 하고 있습니다.. rset은 매번 업데이트가 되어서 우리가 원래 관심있는 descriptor에 대한 정보가 없어지기 때문입니다..

추신: UNP chapt. 6에 잘 나와있습니다. (원래 코드와 함께요)

cjy1126의 이미지

답변 감사합니다 ^^

그런데 제 질문은 allset은 값이 안변하니까 for문에서 allset을 계속 셋팅할 필요가 없지않냐는겁니다.

select문에서도 rset으로 읽기가능한 fd를 받아오면... 각 fd를 처리하고 다시 select문에 들어가기전에 rset = allset 으로 셋팅해주잔아요.

그럼 allset은 값이 안변하는데... for문에서 계속 값을 초기화할 필요는 없을것 같거든요.

문제는... 이 에러를 못잡아서 다른분께 맞겼는데... allset없이 rset을 for문안에서 계속 초기화하는걸로 해결하더군요.

제 생각으로는 실제사용하는 rset과 초기화용 allset을 선언하고 for문밖에서 한번 선언된 allset을 계속 사용하면 될거같은데... 이게 안되는 이유가 무엇인지 정말 이해가 안되네요.

MasterQ의 이미지

cjy1126 wrote:

<snip...>
그럼 allset은 값이 안변하는데... for문에서 계속 값을 초기화할 필요는 없을것 같거든요.
<snip...>
제 생각으로는 실제사용하는 rset과 초기화용 allset을 선언하고 for문밖에서 한번 선언된 allset을 계속 사용하면 될거같은데... 이게 안되는 이유가 무엇인지 정말 이해가 안되네요.

위에도 썼지만....
FD_SET(connfd,&allset);

allset은 변합니다!! 새로 접속한 녀석의 connfd를 allset에다가 FD_SET해주는겁니다... 연결된 녀석들하고 계속 통신을 해야하니까요...

UNP 6장에 자세하게 설명 되어있습니다~~ ^- ^

최종호의 이미지

o cjy1126 님의 동료분이 잡으셨다는 문제의 코드가 이 코드였나요?
제가 볼때는 코드를 칠 때 타이핑 실수인 듯 합니다. 아니었다면 죄송.
/*allset에서 설정된 bit를 rset으로 넘겨준다.
다음에 닫는 */ 이 없어서 쭈욱 아래에 있는
/*---- client communication----*/
이 나올 때까지가 모두 주석으로 처리된 것이죠. for1 을 찍는 문장은
당연히 주석 안에 들어가 있으니까 출력이 안되는 것이고요.

o rset = allset
이 문장은 가능합니다. array이기 때문에 가능한 것이 아니라 structure이기 때문에 가능합니다. 실제로 fd_set 의 타입 정의를 보시면 (Solaris 7 기준) structure안에 하나의 필드를 가지고 있고, 그 필드가 array입니다. 만일 structure를 없애고 그냥 array로만 되어 있다면 위 문장은 오류입니다.

o allset 을 for loop안에서 매번 초기화?
이 코드상에서 쓰인 allset은 for loop안에서 매번 초기화될 필요 없습니다. select()가 주어진 fdset 을 변경시키는 것 맞습니다. 그러나 코드상에서는 allset을 select의 인자로 주는것이 아니라 rset 에 할당한 후에 rset을 인자로 주기 때문에 문제되지 않습니다. rset은 매번 select할 때마다 값이 파괴될 수 있으므로 for loop 안에서 초기화해 주셔야 합니다. for loop 안에 가끔 allset이 보이는 것은 말씀하신 대로 client가 connect를 했을 때 해당 socket을 추가하고, 현재 코드에서는 socket에서 I/O가 발생하자마자 끊게 되어 있는데, 이 경우에 해당 socket을 fd_set에서 제외해 주기 위해서 입니다.

lgskydb의 이미지

:lol:

님께서 하신 부분에 약간의 착오로 인하여 문제가 발생되었네요.
아래의 source 를 참조하십시요.

printf("for\n");
rset = allset; /*allset에서 설정된 bit를 rset으로 넘겨준다.
printf("for1\n");
if((nready = select(maxfd + 1,&rset,NULL,NULL,NULL)) < 0)
{
---------- 생략 -----------
/*---- client communication----*/

rset = allset; 문장에서 comment를 막으셔야 하는데...
comment 처리를 안하셔서 밑에 있는 "client communication"의
comment 까지 모든 source 가 ... (^_^)

참고로 select 문의 5번째 parameter가 NULL 이면 폭주가 발생할수 있으니
"struct timeval" 을 이용하여 timer 처리 하시구요...

nready 값에 대한 대한 처리도 빠져 있네요...
nready값이 음수면 select error 처리/ 0이면 do nothing 처리
그리고 양수면 FD_ISSET 처리를 하심이 어떨까요 ?

글코, 제가 아는 범위에서는 FD_SET은 for 바깥쪽에 있어도 상관없습니다.

댓글 달기

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