fork 로 채팅서버를 만들고 있는데요..

blueball81의 이미지

클라이언트가 접속하면 accept 하고, 공유메모리 배열을 잡아서 소켓을 저장해 둔 다음 프로세스를 포크해서(A) 클라이언트를 담당하게 하고
부모 프로세스는 다음 클라이언트를 대기했습니다. 다음 클라이언트가 접속하면 accept 하고 아까 공유메모리 배열에 소켓 저장하고도 프로세스 포크(B) 했습니다.

그리고는 A 클라이언트에서 메세지가 오면 A 프로세스가 각 소켓으로 메세지를 뿌리구요. B 클라이언트에서 메세지가 오면 B 프로세스가 각 소켓으로 메세지를 뿌렸습니다. ㅋ~ 근데 문제가 생기더군요 B 프로세스는 A,B 클라이언트의 소켓을 다 아는데 A는 B 를 모르더군요..

좀 봐 주시겠습니까?

fork.c -----------------------------------------------------------------------------

#include "TCPEchoServer.h"
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define MAXLINE 1024

int readline(int , char* ,int);
int main(int argc, char *argv[])
{
	int servSock;
	int clntSock;
	unsigned short echoServPort;
	pid_t processID;
	unsigned int childProcCount =0;
	char rlinebuf[MAXLINE];
	int k,i,j,n;
	
	void *shared_memory = (void *)0;
	int shmid;
	int *client_socks;
	int *num_chat; 
	
	if(argc != 2)
	{
		fprintf(stderr,"Usge : %s<server port>\n",argv[0]);
		exit(0);
	}
	echoServPort = atoi(argv[1]);

	///////////////////////////////////////////////////////////////////////
	// 공유메모리 공간을 만든다.
	shmid = shmget((key_t)6666, sizeof(int)*11, 0666|IPC_CREAT);
    	if (shmid == -1)
    	{
        	perror("shmget failed : ");
        	exit(0);
    	}
	// 공유메모리를 사용하기 위해 프로세스메모리에 붙인다. 
    	shared_memory = shmat(shmid, (void *)0, 0);
    	if ( shared_memory == (void *)-1)
    	{
        	perror("shmat failed : ");
        	exit(0);
    	}

    	num_chat = (int *)shared_memory;
    	client_socks = num_chat+1;
    	*num_chat = 0;
	////////////////////////////////////////////////////////////////////
	
	servSock = CreateTCPServerSocket(echoServPort);
	
	while( 1 )
	{
		clntSock = AcceptTCPConnection(servSock);
		client_socks[*num_chat] = clntSock;
		(*num_chat)++;
		
		processID = fork();
		
		if(processID == -1 ) { //fork failed
			DieWithError("fork failed");
		}
		else if (processID == 0) { //child proc.
			///////////////////////////////////////////////////////////////////////
			// 공유메모리 공간을 만든다.
			shmid = shmget((key_t)6666, sizeof(int)*11, 0666|IPC_CREAT);
    			if (shmid == -1)
	    		{
	        		perror("shmget failed : ");
        			exit(0);
    			}
			// 공유메모리를 사용하기 위해 프로세스메모리에 붙인다. 
    			shared_memory = shmat(shmid, (void *)0, 0);
    			if (shared_memory == (void *)-1)
    			{
		        	perror("shmat failed : ");
        			exit(0);
    			}
	
		    	num_chat = (int *)shared_memory;
		    	client_socks = num_chat+1;
			////////////////////////////////////////////////////////////////////////
			close(servSock);
			
			send(clntSock, "Welcome~!!", strlen("Welcome~!!"), 0);
			printf("Welcome~!!\n");
			
			do {
				//printf("1 rlinebuf : %s\n", rlinebuf);
				if( ( n=recv(clntSock, rlinebuf, MAXLINE, 0) ) > 0 ) {
					rlinebuf[n]='\0';
					printf("SERVER(%d) : '%s' received from client %d\n", *num_chat, rlinebuf, clntSock);
					for(j=0; j < *num_chat; j++) {
						printf(" SERVER : server sent message '%s' to client %d\n",rlinebuf, client_socks[j]);
						send(client_socks[j],rlinebuf,n,0);
					}
				}
				else //when client isn't connected
					break;
			} while( strcmp( rlinebuf, "quit") );
			
			(*num_chat)--;
			printf("SERVER : client %d quited, %d are connected\n", clntSock, *num_chat);
			close(clntSock);
			exit(0);
			
		}
		else if (processID > 0) { //parent proc.
			//close(clntSock);
			printf("%d번째 사용자 ",*num_chat);
			printf("with child process : %d\n" , (int)processID);
			childProcCount = *num_chat;
			while( childProcCount )
			{
				processID = waitpid((pid_t) -1,NULL,WNOHANG);
				if(processID < 0)
					DieWithError("waitpid() failed");
				else if(processID == 0){ // no zombie process exist.
					printf("SERVER : waitpid = 0, child_count = %d\n",childProcCount);
					break;
				}
				else {  //zombie process existed
					printf("SERVER : waitpid > 0\n");
					childProcCount--;
				}
			}
		}
	}
}

AcceptTCPConnection.c -------------------------------------------------------

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>

void DieWithError(char * errorMessage);

int AcceptTCPConnection(int servSock)
{
	int clntSock;
	struct sockaddr_in echoClntAddr;
	unsigned int clntLen;

	clntLen = sizeof(echoClntAddr);

	if( (clntSock = accept(servSock,(struct sockaddr *)&echoClntAddr,&clntLen) ) < 0 )
		DieWithError("accept failed");

	printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));
	return clntSock;
}

CreateTCPServerSocket.c ----------------------------------------------------

#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>

#define MAXPENDING 5

void DieWithError(char * errorMessage);

int CreateTCPServerSocket(unsigned short port)
{
	int sock;
	struct sockaddr_in echoServAddr;

	if((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0)
		DieWithError("socket() failed");

	memset(&echoServAddr,0,sizeof(echoServAddr));
	echoServAddr.sin_family = AF_INET;
	echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	echoServAddr.sin_port = htons(port);

	if (bind(sock,(struct sockaddr *) &echoServAddr,sizeof(echoServAddr))<0)
		DieWithError("bind() failed");
	
	if(listen(sock,MAXPENDING) <0 )
		DieWithError("listen() failed");
	return sock;
}

p.s. 소스가 좀 지저분해도 적당히 좀 봐주세요 ^^;

pynoos의 이미지

채팅 서버를 만들때 클라이언트를 담당하는 fork 되어 나온 프로세스가 다른 프로세스의 socket에 쓰는 것은 좋아 보이지 않습니다.

파이프를 사용하여 모 프로세스가 중계를 하던지 따로 중계하는 프로세스를 두고 그것과 통신할 수 있도록 하는 것이 좋겠네요

blueball81의 이미지

아. 네 저도 만들고 보니 좀 그래 보이는데요.
그래서 중계하는 프로세스가 있어야 한다고 생각해서 모 프로세스를 생각했지만 모 프로세스는 다음 클라이언트를 받기위해 대기중이다 보니 좀 힘든게 아닌가 싶구요.

다른 프로세스를 만든다면 어떻게 해야하는지 조금만 더 구체적으로 설명해 주실순 없을까요?

그리고, ^^;; 프로세스간 통신 말인데요.. 어떤 내용을 공부해야 할지..
조금만 더 설명 부탁드립니다.

pynoos의 이미지

대략 생각하건데, 모프로세스에서 파이프를 하나 만듭니다. 이 놈은 중계 프로세스가 사용할 메시지 수신용입니다. 그리고, fork하여 메시지 중계용 프로세스를 띄웁니다.
모 프로세스는 클라이언트가 들어 올때마다 계속 fork해 댑니다. 이들은 먼저 열어 두었던 파이프를 가지고 생성됩니다.

또한 다른 사용자로부터 받을 메시지용 unix dgram socket하나를 만듭니다.

송신용 파이프를 통해서 unix dgram socket 주소를 등록합니다. 간단한 프로토콜이 있어야겠군요.
송신용 파이에다가 remote에서 보내는 메시지를 위 프로토콜로 전송합니다.

중계하는 놈은 자기한테 등록한 모든 DGRAM에 수신받은 메시지를 뿌려줍니다...

아... 어렵다..

leilei의 이미지

좀 무식하긴 해도.. 걍 부모 프로세스가 다시 fork를 하면 되지 않을까요?
물론 부모 자식간에는 파이프 같은 ipc로 연결을 하구요..

자식쪽에서 받은 채팅메세지를 부모쪽으로 보내면
부모가 fork를 해서 열려진 소켓에 주욱 보내면 될것 같은데요..
그럼 A, B를 모두 알 수 있으니까요..

아님...
각 자식 프로세스는 자신이 담당한 클라이언트의 소켓만 가지고 있고..
받으면 보모나 중계담당 프로세스쪽으로 ipc로 넘기고 다시 받으면
되지 않을까요?

걍 ~ 떠 오르는 생각이었습니다..
구현이 잘 될려나.. :?

전자의 경우는 구현을 해본 적이 있는데요.. 별로 효율적이란 생각은
안드는군요.. 단순히 채팅만 하신다면 쓰레드나 select/poll 이
좋지 않을까 합니다.. :)

댓글 달기

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