pthread의 작업 할당(?) + select() ...

버려진의 이미지

리눅스에서 프로그래밍 시작한지 얼마 안됐는데요
이것저것 짜면서 공부중입니다.
지금은 간단한 서버를 짜고 있는데요
fork는 윈도에 없는거다 보니까 익숙하지가 않아서
pthread로 만들었습니다.
1 thread per 1 work식으로 만드려고 했거든요.
accept처리해 주는 쓰레드가 있고
(논블로킹이나 셀렉트 같은 대안을 모르는것은 아닙니다만
공부중이라 이것저것 해보는 중입니다)
패킷을 받아주는 쓰레드가 있습니다.
받은걸 처리하는 쓰레드, 패킷을 보내는 쓰레드를 더 만들
예정입니다만 어쨌든..
접속 처리는 잘 되는데, 이상하게도 날아오는 패킷을 못받더군요.
음..
테스트로 공유 변수(?)를 하나 선언해 놓고
쓰레드 두개를 만들어서 하나는 ++, 하나는 -- 연산을 하게 해서
출력을 시켜 봤는데요
처음엔 업치락 뒤치락 하는거 같다가,
잠시후에는 무한정 커지기만 하더군요.
pthread는 하나가 죽으면 나머지도 죽는다고 들었는데
--연산을 하는 쓰레드만 죽었을리도 없고 말이죠.
도스에서 쓰레드 짜보면 쓰레드 두개가 하나씩 차례차례
양보해가면서 돌아가던데 ^^;
소스 수준의 문제인지, pthread의 동작에 유의할 점이 있는지
궁금합니다.

choissi의 이미지

--, ++ 테스트에서는
아마 커널에서 무한루프 때문에, 그런 일이 생긴것 같은데
작업중에 sleep을 줘보세요.

그리고 첫번째 질문은 올리신 내용으로는 문제점을 발견하기가
어렵겠네요.. 배우시는 중이라니까 소스를 올려주시면
어디가 문제가 될지 봐드릴께요..

울랄라~ 호기심 천국~!!
http://www.ezdoum.com

버려진의 이미지

음.. 보다 확실해 보이는 select()를 써서 짜봤는데,
이번에도 역시 recv()가 안되네요 ㅜ.ㅜ...
문제는 다르겠지만..
저번엔 recv()를 담당하는 쓰레드가 제대로 안돌아갔고,
이번에는 FD_ISSET()이 제대로 동작하지 않는것 같습니다.
뭐가 문제인지 좀 알려주셨으면 합니다.

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#define CLIENT_COUNT 100
#define BUFFER_SIZE 2048

char buffer[BUFFER_SIZE];
int socks[CLIENT_COUNT] = { '\0' };
int num_client = 0;

int main()
{
	int sock_listen = 0;
	int len = 0;
	int i = 0; // for() loop
	int on = 1; // setsockopt()
	struct sockaddr_in my_addr, their_addr;
	fd_set read_fdset;

	if ((sock_listen = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		printf("%s\n", "sock create err");
		return 1;
	}

	setsockopt(sock_listen, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));

	memset(&my_addr, 0x00, sizeof(struct sockaddr_in));
	len = sizeof(struct sockaddr_in);
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(2593);
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*inet_addr("127.0.0.1")*/
	bzero(&(my_addr.sin_zero), 8);

	if (bind(sock_listen, (struct sockaddr *)&my_addr, len) < 0)
	{
		printf("%s\n", "bind err");
		return 1;
	}

	listen(sock_listen, 5);

	while(1)
	{
		FD_ZERO(&read_fdset);
		FD_SET(sock_listen, &read_fdset);

		for (i=0; i<num_client; i++)
			FD_SET(socks[i], &read_fdset);

		if (select(10, &read_fdset, 0, 0, 0) < 0)
			
		{
			printf("%s\n", "select failed");
			return 1;
		}

		if (FD_ISSET(sock_listen, &read_fdset))
		{
			if (socks[num_client++] = accept(sock_listen, (struct sockaddr *)&their_addr, &len) < 0)
			{
				printf("%s\n", "accept err");
				return 1;
			}
			if (num_client > CLIENT_COUNT)
			{
				printf("%s\n", "sock list full");
				close(socks[--num_client]);
			}
		}
		else
		{
			for (i=0; i<num_client; i++)
			{
				if (FD_ISSET(socks[i], &read_fdset))
				{
					memset(buffer, 0x00, BUFFER_SIZE);
					if (recv(socks[i], buffer, BUFFER_SIZE, 0) <= 0)
					{
						close(socks[i]);
						socks[i] = socks[--num_client];
						continue;
					}
					printf("recv [%d] :%s\n", i, buffer);
				}
			}
		}
	}
	close(sock_listen);

	printf("%s\n", "server is terminated");

	return 0;
}
choissi의 이미지

      if (select(10, &read_fdset, 0, 0, 0) < 0) 
          
      { 
         printf("%s\n", "select failed"); 
         return 1; 
      }

이런식으로 선언하시면 fd가 10번 미만일때는 잘 동작을 하나,
10개을 넘어가면 문제가 생깁니다.

if (socks[num_client++] = accept(sock_listen, (struct sockaddr *)&their_addr, &len) < 0) 
         { 
            printf("%s\n", "accept err"); 
            return 1; 
         }

접속을 수락하는 부분도 문제가 있습니다.
연산자 우선순위가 =보다는 대소비교(<,>)가 높기 때문에
socks[]에는 1아니면 0만 들어가게 됩니다.
즉.. 님이 의도한 socks[]에 값을 주고 그 값을 체크하는
것으로 동작을 안하죠..

        sock_accepted = accept(sock_listen, (struct sockaddr *)&their_addr, &len);
         if (sock_accepted < 0)
         {
            printf("%s\n", "accept err");
            return 1;
         }
         else{
                socks[num_client++] = sock_accepted;
                printf("accept fd[%d] [%d]\n",socks[num_client], sock_accepted);
        }

이런식으로 별도의 변수로 분리하는게 맞겠죠..

         for (i=0; i<num_client; i++) 
         { 
            if (FD_ISSET(socks[i], &read_fdset)) 
            { 
               memset(buffer, 0x00, BUFFER_SIZE); 
               if (recv(socks[i], buffer, BUFFER_SIZE, 0) <= 0) 
               { 
                  close(socks[i]); 
                  socks[i] = socks[--num_client]; 
                  continue; 
               } 
               printf("recv [%d] :%s\n", i, buffer); 
            } 
         } 

이 부분도 종료처리 하는 부분이 문제가 있습니다.
비게되는 socks 배열에 마지막 배열 값을 넣는데,
continue로 올라가면, i가 이미 증가되어
옮겨진 마지막 fd는 검사를 안하겠죠.

ps.
디버깅을 하실려면 gdb를 사용해서 값을 추적하시거나.,
printf나 로그파일로 주요 값을 찍어보는게 좋습니다.

         for (i=0; i<num_client; i++)
         {
                printf("select i[%d] fd[%d]\n",i, socks[i]);

            if (FD_ISSET(socks[i], &read_fdset))
            {
                printf("select isset i[%d] fd[%d]\n",i, socks[i]);
               memset(buffer, 0x00, BUFFER_SIZE);
               if (recv(socks[i], buffer, BUFFER_SIZE, 0) <= 0)
               {
                printf("select err i[%d] fd[%d]\n",i, socks[i]);
                  close(socks[i]);
                  socks[i] = socks[--num_client];
                  continue;
               }
               printf("recv [%d] :%s\n", i, buffer);
            }
         }
      }

울랄라~ 호기심 천국~!!
http://www.ezdoum.com

버려진의 이미지

select()에 인자로 10넣은건 소스를 이리저리 바꾸다가
원래대로 복구 안해놓고 올려버렸네요 ^^;
num_client를 넣던지, CLIENT_COUNT를 넣던지 똑같이 보이는데 맞는지요?

accept()는 괄호 하나 묶어서 해결했네요 ^^;
결국 저번에 pthread에서 발생했던 문제도 같은 문제였습니다. 흑흑 ㅜ.ㅜ

음.. client쪽에서 접속됐는지 메세지를 출력하게 해놔서
클라이언트가 접속 됐다고 하길래 그쪽은 생각도 못했네요 ^^;

vi는.. 다 좋은데 한 화면에 글씨가 얼마 안들어가니
들여쓰기를 얼마 못하고, 소스가 눈에 많이 안들어오는 점이
좀 불편하네요.

gdb는 오늘 처음 깔았습니다 ^^;
이거좀 손에 익히고.. 진도를 나가야 되겠네요.
와 갈길이 멀지만 ^^;
도움 주셔서 거듭 감사드립니다 ^^

jj의 이미지

pyj200 wrote:
select()에 인자로 10넣은건 소스를 이리저리 바꾸다가
원래대로 복구 안해놓고 올려버렸네요 ^^;
num_client를 넣던지, CLIENT_COUNT를 넣던지 똑같이 보이는데 맞는지요?

첫번째 변수는 file descriptor중에 가장 큰값 + 1입니다. man page를 참고하세용...

--
Life is short. damn short...

고도리의 이미지

select()함수의 제일 처음인자로는 일반적으로 그냥
숫자를 쓰는 것보다는 검사하고 싶은 fd의 값들중 가장 큰값보다 1이
큰 값이 들어갑니다.

즉, 몇개까지 검사하는게 아니라, fd의 값들중 어느것보다 작은것들까지
검사를 하겠다는 겁니다.

만일 fd가 2, 3, 4, 9일 경우 select()함수의 첫번째 인자로는 10이
들어가야 겠지요...

그래서 보통 프로그램을 작성하는 중간에 for문을 돌면서

해당 fd의 값들을 서칭해서 가장 큰 fd의 값을 maxfd란 변수로 넣고

select( maxfd+1, &readset, &writeset, NULL, NULL);

이런식으로 해줘야 합니다.

그럼...

서명.....음, 서명이라...

아싸!!! Three Go!

댓글 달기

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