둘 이상이 접속하면 select에서 에러가 납니다.

wizmusa의 이미지

안녕하세요? 처음으로 질문을 올립니다.

핸드폰으로 접속하여 그림을 다운받는 프로그램을 짜고 있습니다. 그런데 둘 이상이 접속을 하면 select에서 에러가 나버립니다. 물론 혼자 접속하면 종료되지 않고 몇 번을 접속해서 다운을 받아도 잘 됩니다. (-_-) 넷에서 돌아다니는 채팅 서버 소스를 고쳐서 만들고 있는데요... 뭐가 문제인지 잘 모르겠습니다. 그저 select에서 에러가 난다는 것만을 알 뿐입니다. 어떻게 해야 에러가 없어질까요?

int main(int argc, char *argv[])  {
   char rline[MAXLINE], my_msg[MAXLINE];
   char *start = "Connected to chat_server \n";
   int i, j, n;
   int s, client_fd, clilen;
 //
 	int		threadFd ;
 	int     ret;
	BYTE	readline[SBUF_LEN];
	int		length, len ;
	int     addrLen;
	uint	magic, block ;
	int		mode, imageKey ;
	pthread_t	thread_id ;
//
 
 
   fd_set  read_fds;     /* 읽기를 감지할 소켓번호 구조체 */
   struct sockaddr_in  client_addr, server_addr;
   
   if(argc != 2)  {
      printf("사용법 :%s port\n", argv[0]); 
      exit(0);
   }
   
   /* 초기소켓 생성 */
   if((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)  {
      printf("Server: Can't open stream socket.");   
      exit(0);
   }

//
	umask(0);
	setDemon();
	if ( fork() ) {
			exit(0);
	}
	setpgrp();
//

   
   /* server_addr 구조체의 내용 세팅 */
   bzero((char *)&server_addr, sizeof(server_addr));  
   server_addr.sin_family = AF_INET;              
   server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
   server_addr.sin_port = htons(atoi(argv[1]));     
   
   if (bind(s,(struct sockaddr *)&server_addr,sizeof(server_addr)) < 0) {
      printf("Server: Can't bind local address.\n");
      exit(0);
   }
   /* 클라이언트로부터 연결요청을 기다림 */
   listen(s, 50); // 원랜 5였습니다.
   maxfdp1 = s + 1;    /* 최대 소켓번호 +1 */
   
   while(1) {
 		FD_ZERO(&read_fds);
     FD_SET(s, &read_fds);
     for(i=0; i<num_chat; i++)  FD_SET(client_s[i], &read_fds);
     maxfdp1 = getmax(s) + 1;     /* maxfdp1 재 계산 */
     if (select(maxfdp1, &read_fds, (fd_set *)0, (fd_set *)0,(struct timeval *)0) < 0) {
     //if (select(maxfdp1, &read_fds, (fd_set *)0, (fd_set *)0,NULL) < 0) {
     		printf("select error <= 0 \n");
        	exit(0); // 여기에서 에러가 나면서 종료됩니다.
		} 
     if(FD_ISSET(s, &read_fds)) {
			clilen = sizeof(client_addr);
        	client_fd = accept(s, (struct sockaddr *)&client_addr, &clilen);
        	if(client_fd == -1)  {
        		printf("accept error\n");
        		exit(0);
			}
     		/* 채팅 클라이언트 목록에 추가 */
        	client_s[num_chat] = client_fd; 
        	num_chat++;
        	//send(client_fd, start, strlen(start), 0);
        	printf("%d번째 사용자 추가.\n", num_chat);
		}
          
     /* 클라이언트가 보낸 메시지를 모든 클라이언트에게 방송 */
     	for(i = 0; i < num_chat; i++)  {
     		if(FD_ISSET(client_s[i], &read_fds)) {


				threadFd = client_s[i];
				memset( &thread_id, 0, sizeof(thread_id) );

				if(pthread_create(&thread_id, NULL, (void *)doDownLoad, (void *)&threadFd)) 
/* doDownLoad는 쓰레드로 실행시킵니다.(?) 실제로 서버에서 다운받는 부분입니다. 혹시 쓰레드를 만들기 때문에 셀렉트에서 에러가 날까요?*/
				{
					logDebug("dlServer startServer(), FAIL_CREATE_THREAD, errno = %d \n", errno);
				}
 			}
		} 	
  }  		
}

혹시 쓰레드를 잘못 쓰면 select에서 착오를 일으키는걸까요?
아니면 select 이전에 제가 뭔가를 잘못 썼기 때문일까요?
답변 부탁드립니다.

stoneshim의 이미지

일단 오류의 내용을 보세요.

     if (select(maxfdp1, &read_fds, (fd_set *)0, (fd_set *)0,(struct timeval *)0) < 0) { 
     //if (select(maxfdp1, &read_fds, (fd_set *)0, (fd_set *)0,NULL) < 0) { 
           /* printf("select error <= 0 \n"); */
           /* printf 를 perror 로 바꾸셔서 오류내용을 출력하세요 */
           perror("select");
           exit(0); // 여기에서 에러가 나면서 종료됩니다. 
      } 

우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자

wizmusa의 이미지

select: Bad file descriptor 라는 메시지가 나오면서 종료됩니다.

세 개의 클라이언트끼리 서로 싸워가면서(?) - 계속 재접속시도를 하면서 이미지를 다운로드받다가는 위의 메시지가 나오면서 종료되었습니다.

평화가 함께 하기를...

faye의 이미지

추축입니다만.. maxfdp1 값이 잘못되서 그런것이 아닐까요??

client가 접속하면.. client sock fd 값은 s값 보다 클수 있는데..

maxfdp1값은 s에서 최대 값을 찾고 있으니....

getmax()라는 함수에서 어떻게 처리해주는지 모르겠내요..

maxfdp1쪽을 확인보시는 것도 좋을듯 하내요..

그럼..

stoneshim의 이미지

이 부분이 문제입니다.

           /* 채팅 클라이언트 목록에 추가 */ 
           client_s[num_chat] = client_fd; 
           num_chat++; 
           //send(client_fd, start, strlen(start), 0); 
           printf("%d번째 사용자 추가.\n", num_chat); 

다음과 같이 처리해 주셔야 합니다.

         /*초기화 */
         for( i=0; i<최대값; i++ )
            client_s[i] = -1

...
          /* 채팅 클라이언트 목록에 추가 */ 
           for(i=0; i<최대값; i++) {
             if( client_s[i] < 0 )
                client_s = client_fd; 
             if( i > num_chat ) num_chat++; 
           }
           printf("%d번째 사용자 추가.\n", num_chat);
...
           /* socket이 close 된 경우 */
           client_s[?] = -1;

우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자

wizmusa의 이미지

maxfdp1 = getmax(s) + 1; /* maxfdp1 재 계산 */

일단 s 중에서 최대값을 찾고 거기에 1을 더하는 것으로 했는데요...

getmax()는 다음과 같습니다.

/* clien_s[] 내의 최대 소켓번호 얻기(초기치는 k) */
int getmax(int k) {
	int max = k;    
	int r;
  for (r=0; r < num_chat; r++) {
  		if (client_s[r] > max ) max = client_s[r];
	}
  return max;
}

원래 소스에 있었던 것인데 그냥
maxfdp1 = s + 1;
로 해봤더니 다운로드가 실행되지않더라구요...

평화가 함께 하기를...

wizmusa의 이미지

stoneshim님 말씀대로 해봤습니다. 이제는 동시 접속이 일어났다고 해서 서버프로그램이 종료되지는 않습니다. 그런데 아무래도 원래 주신대로 하면 에러가 나서요... 제가 우선 다음과 같이 바꿔서 했습니다.

for(i=0; i<MAX_SOCK; i++) { 
	if( client_s[i] < 0 ) {
      		client_s[i] = client_fd;
             		if( i > num_chat ) { num_chat++; }
             	}
}

아무래도 좀 이상하게 고쳤지만 일단 돌려봤습니다. :?
그랬더니 앞서 말씀드린대로 서버프로그램이 종료되지는 않는데 첫번째 접속자도 63번째(MAX_SOCK이 63입니다.)로 나오면서 클라이언트들이 접속만 하고 다운로드를 진행하지 않습니다. 재접속을 시키면 받기는 합니다만 제대로 돌아가는 건 아니겠지요. 일단 다른 방식으로 다시 시도해보겠습니다만 어렵네요. 시간도 별로 없는데... :cry:

stoneshim wrote:
이 부분이 문제입니다.
           /* 채팅 클라이언트 목록에 추가 */ 
           client_s[num_chat] = client_fd; 
           num_chat++; 
           //send(client_fd, start, strlen(start), 0); 
           printf("%d번째 사용자 추가.\n", num_chat); 

다음과 같이 처리해 주셔야 합니다.

         /*초기화 */
         for( i=0; i<최대값; i++ )
            client_s[i] = -1

...
          /* 채팅 클라이언트 목록에 추가 */ 
           for(i=0; i<최대값; i++) {
             if( client_s[i] < 0 )
                client_s = client_fd; 
             if( i > num_chat ) num_chat++; 
           }
           printf("%d번째 사용자 추가.\n", num_chat);
...
           /* socket이 close 된 경우 */
           client_s[?] = -1;

평화가 함께 하기를...

stoneshim의 이미지

제가 코딩 중에 잘못된 부분이 있었군요.

          /* 채팅 클라이언트 목록에 추가 */ 
           for(i=0; i<최대값; i++) { 
             if( client_s[i] < 0 ) {
//              client_s = client_fd; 
                client_s[i] = client_fd;
             }
             if( i > num_chat ) {
//               num_chat++; 
                  // num_chat++보다 아래 코드가 정확할것 같습니다.
                  num_chat = i; // i+1일수 있으니 문맥상 결정하세요.
             }
           } 

Quote:
서버프로그램이 종료되지는 않는데 첫번째 접속자도 63번째(MAX_SOCK이 63입니다.)로 나오면서

말씀하신 현상은 초기화를 안해주신것 같습니다.

         /*초기화 */ 
         for( i=0; i<최대값; i++ ) 
            client_s[i] = -1 

우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자

wizmusa의 이미지

stoneshim wrote:
제가 코딩 중에 잘못된 부분이 있었군요.

          /* 채팅 클라이언트 목록에 추가 */ 
           for(i=0; i<최대값; i++) { 
             if( client_s[i] < 0 ) {
//              client_s = client_fd; 
                client_s[i] = client_fd;
             }
             if( i > num_chat ) {
//               num_chat++; 
                  // num_chat++보다 아래 코드가 정확할것 같습니다.
                  num_chat = i; // i+1일수 있으니 문맥상 결정하세요.
             }
           } 

Quote:
서버프로그램이 종료되지는 않는데 첫번째 접속자도 63번째(MAX_SOCK이 63입니다.)로 나오면서

말씀하신 현상은 초기화를 안해주신것 같습니다.

         /*초기화 */ 
         for( i=0; i<최대값; i++ ) 
            client_s[i] = -1 

초기화는 해주었는데요... 아무래도

          for(i=0; i<최대값; i++) { 
             if( client_s[i] < 0 ) {
                client_s[i] = client_fd;
             }

여기에서 client_s[i]에 무조건 client_fd 값이 들어가는 것 같습니다. 그러니까 num_chat에는 무조건 최대값 63이 들어가게 되구요... 여러 클라이언트가 접속하면 select: Bad file descriptor 에러도 여전히 발생하구요... 어떻게 된건지 도무지 모르겠네요...

평화가 함께 하기를...

stoneshim의 이미지

          /* 채팅 클라이언트 목록에 추가 */ 
           for(i=0; i<최대값; i++) { 
             if( client_s[i] < 0 ) { 
                client_s[i] = client_fd; 
                break; // 죄송합니다. 추가해주세요.
             } 
           }
           if( i > num_chat ) { //수정하면서 이부분은 for loop밖으로 뺐습니다.
                num_chat = i;  
           } 

죄송합니다. 대충 로직만 말씀드린다는 생각으로... 정확한 코딩이 아니었습니다.

우리 모두 리얼리스트가 되자. 그러나 가슴에 이룰 수 없는 꿈을 가지자

댓글 달기

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