FALINUX의 Q/A 게시판에 zerr님께서 thread 간의 데이터 공유에 대한 질문을 올려 주셔서 빈약한 지식이지만 알고 있는 내용에 한에서 정리해서 올린다라고 올린 것이 IPC를 위주로 설명을 드리다 보니 thread에서도 IPC 중에 UDS를 권하는 식으로 오해를 하기게끔 글이 올려 졌네요.

저의 설명 때문에 쓰레드에서 UDS를 이용하는 코드를 작성하시려고 하신 것 같아서, 또한 질문을 올려 주셔서 죄송한 마음에 쓰레드에서 UDS를 이용한 예제를 작성하게 되었습니다. 모쪼록 zerr님께서 저의 작문 실력이 짧음을 양해해 주시고 예제를 참고해 주신다면 감사하겠습니다.

쓰레드에서 UDS 사용

쓰레드는 전역 변수를 쓰레드끼리 공유할 수 있기 때문에 굳이 IPC 방법을 사용하지 않아도 간편하게 쓰레드끼리 자료를 주고 받을 수 있습니다. 물론 동기화를 맞추어야 겠지요.

그렇다고 쓰레드에서 IPC를 사용하지 못하는 것은 아닙니다. 세마포어나 뮤텍스를 이용하여 동기화 작업을 하는 것 보다 UDS를 이용하면 조금 복잡하더라도 UDS를 이용하는 장점을 그대로 이용할 수 있습니다.

zerr님께서도 질문을 올려 주셨습니다만 과연 전역변수를 이용하는 방법이 좋을까, 아니면 IPC를 이용하는 방법이 좋겠느냐는 각 개인의 경험에 따르지 않을까 합니다. 물론 대세라는 것이 있습니다만 제 생각에는 절대적이라는 것은 없다고 생각됩니다. 가급적이면 코드가 간단한 전역변수를 사용하겠습니다만 쓰레드 끼리 분리를 확실히 해야 겠다고 한다면 IPC를 사용하는 것이 더 좋다고 생각되며 IPC를 이용한다면 UDS 사용을 권합니다.

아래의 예제는 멀티 프로세스에서 UDS를 사용하는 것이 아니라 쓰레드에서 UDS를 이용하는 방법을 소개하며, TCP형식에 맞추어 클라이언트와 서버로 나누어 작성했습니다. 우선 프로그램을 보시겠습니다.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/un.h>
#include <pthread.h>

#define  BUFF_SIZE   1024
#define  FILE_SERVER "./test_server"

void  *thread_server( void *arg)
{
   int   server_socket;
   int   client_socket;
   int   client_addr_size;

   struct sockaddr_un   server_addr;
   struct sockaddr_un   client_addr;

   char   buff_rcv[BUFF_SIZE+5];
   char   buff_snd[BUFF_SIZE+5];

   printf( "[server]start!!n");

   if ( 0 == access( FILE_SERVER, F_OK))
      unlink( FILE_SERVER);

   server_socket  = socket( PF_FILE, SOCK_STREAM, 0);

   if( -1 == server_socket)
   {
      printf( "[server]server socket 생성 실패n");
      exit( 1);
   }

   memset( &server_addr, 0, sizeof( server_addr));
   server_addr.sun_family  = AF_UNIX;
   strcpy( server_addr.sun_path, FILE_SERVER);

   if( -1 == bind( server_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
   {
      printf( "[server]bind() 실행 에러n");
      exit( 1);
   }

   while( 1)
   {
      printf( "[server]listen start!!n");
      if( -1 == listen(server_socket, 5))
      {
         printf( "[server]대기상태 모드 설정 실패n");
         exit( 1);
      }

      client_addr_size  = sizeof( client_addr);
      client_socket     = accept( server_socket, (struct sockaddr*)&client_addr, &client_addr_size);

      if ( -1 == client_socket)
      {
         printf( "[server]클라이언트 연결 수락 실패n");
         exit( 1);
      }
      while( 1)
      {
         read ( client_socket, buff_rcv, BUFF_SIZE);
         printf( "[server]receive: %sn", buff_rcv);

         sprintf( buff_snd, "%d bytes - %s", strlen( buff_rcv), buff_rcv);
         write( client_socket, buff_snd, strlen( buff_snd)+1); // +1: NULL까지 포함해서 전송
      }
      close( client_socket);
   }
   return 0;
}

void  *thread_client( void *arg)
{
   int      client_socket;
   struct   sockaddr_un   server_addr;
   char    *str_send = "http://forum.falinux.com";
   char     buff[BUFF_SIZE+5];

   printf( "[client] start!!n");
   client_socket  = socket( PF_FILE, SOCK_STREAM, 0);
   if( -1 == client_socket)
   {
      printf( "[client]socket 생성 실패n");
      exit( 1);
   }

   memset( &server_addr, 0, sizeof( server_addr));
   server_addr.sun_family  = AF_UNIX;
   strcpy( server_addr.sun_path, FILE_SERVER);

   if( -1 == connect( client_socket, (struct sockaddr*)&server_addr, sizeof( server_addr) ) )
   {
      printf( "접속 실패n");
   }
   else
   {
      while( 1)
      {
         write( client_socket, str_send, strlen( str_send)+1);      // +1: NULL까지 포함해서 전송
         read ( client_socket, buff, BUFF_SIZE);
         printf( "[client]receive:%sn", buff);
         sleep( 1);
      }
   }
   close( client_socket);
   return 0;
}

int   main( void)
{
   pthread_t   id_server;
   pthread_t   id_client;

   if ( 0 != pthread_create( &id_server, NULL, thread_server, NULL))
   {
      printf( "서버 쓰레드 생성 실패n");
      exit( 1);
   }
   if ( 0 != pthread_create( &id_client, NULL, thread_client, NULL))
   {
      printf( "클라이언트 쓰레드 생성 실패n");
      exit( 1);
   }

   while( 1)
      sleep(1);
   return 0;
}

그리고 Makefile입니다.

TARGET      = sample
OBJS        = sample.o
SRCS        = $(OBJS:.o=.c)
INCLUDEDIRS +=
LIVDIRS     +=
CFLAGS      = $(INCLUDEDIRS) -W -Wall -O2 -D_REENTRANT
LDFLAGS     = $(LIVDIRS)
CC          = gcc

$(TARGET): $(OBJS)
   $(CC) -lm -o $(LDFLAGS) $@ $^ -lpthread

.c.o:
   $(CC) -c $(CFLAGS) $<

clean:
   rm $(OBJS) $(TARGET)
   @echo "파일을 삭제했습니다."

dep :
   gccmakedep $(SRCS)

# DO NOT DELETE

sample를 실행하시면 client thread가 sever thread의 서버 소켓에 접속하고 문자열을 전송하면 서버는 문자열의 길이를 구하고 문자열 길이와 문자열을 다시 전송해 줍니다. 클라이언트는 수신한 문자열을 화면에 출력하고 1초가 쉰 다음 다시 문자열을 전송하게 됩니다.

우선 zerr님께 죄송해서 쓰레드에서 UDS 사용 예제를 먼저 올렸습니다만 앞으로 쓰레드와 멀티 프로세스에 대한 강좌도 준비해서 올리겠습니다.

 

태그: *네트워크 *IPC *UDS