Saturday, December 12, 2009

이클립스(eclipse)를 이용해서 C/C++ 프로그래밍 환경설정.

http://mudchobo.tomeii.com/tt/73


이클립스(eclipse)를 이용해서 C/C++ 프로그래밍 환경설정.2007/08/11 23:06 / C와 C플러스플러스
google_protectAndRun("render_ads.js::google_render_ad", google_handleError, google_render_ad);
C공부를 다시 해야할 것 같아서 IDE를 설치해야할 일이 생겼습니다. Visual Studio로만 C/C++을 해온 저는 유닉스 환경에서 해야해서 다른 IDE를 찾고 있었습니다. 이클립스를 다운 받을 때 C/C++용도 있었던 것 같았습니다.그래서 이클립스 홈페이지가니 있더군요. 여기저기 수소문 끝에 유닉스환경에서 할 수 있는 C프로그래밍을 윈도우에서도 구축할 수 있더군요. 이런게 있을 줄이야...우선 이클립스를 받습니다.http://www.eclipse.org/downloads/여기 사이트를 들어가시면 Eclipse IDE for C/C++ Developers 라고 있습니다.운영체제에 맞는 것을 받으시구요. Linux와 MacOS도 지원하는군요!받은다음에 적당한 폴더에 압축을 풉니다. 자 그다음 gcc, make 등을 구해야하는데 두가지 종류가 있더군요.(리눅스는 안해줘도 될꺼같군요)Cygwin랑 MinGW 가 있는데 MinGW로 해보겠습니다. (Cygwin은 이걸로 만든 실행파일을 실행할때 dll파일이 필요하다고 하더군요)이곳에서 MinGW를 받습니다.http://www.mingw.org/download.shtml받은 뒤 실행하면 다음신공하다보면 뭐 설치할 꺼냐고 묻는데 전 잘몰라서 c관련된 컴파일러는 다 설치했습니다. make랑 g++랑 objective c 컴파일러같은거 이거 3개 체크하고 설치했습니다.그런다음에 패스를 잡아줘야합니다. gcc랑 make 등의 파일이 어디서든 실행이 되어야합니다.패스잡는것은 비스타기준입니다.컴퓨터(오른쪽버튼누른뒤 속성) -> 고급시스템설정 -> 고급탭에서 환경변수 클릭!XP에서도 비슷하게 속성누르면 있을껍니다.집에 알집이 깔려있다면 위에 알집이 PATH로 잡혀있을껍니다. 요고이 지워줍니다. 그리고 이스트소프트를 막 욕합시다. 시스템변수에 Path를 찾습니다. 그다음 MinGW/bin폴더를 패스를 잡아줍니다.ex) ......;C:\MinGW\bin //c:\MinGW는 MinGW설치폴더패스가 제대로 잡혔는지 보려면 시작 -> cmd를 실행해서 gcc를 실행해봅시다. gcc: no input files라고 나오면 잘 잡힌겁니다.그리고 이클립스에서는 make.exe파일을 make할 때 쓰는데 MinGW는 mingw32-make.exe파일이 make파일입니다. 그래서 이름을 변경하던가 이클립스 설정을 변경하던가 합니다.첫번째 방법은 mingw32-make.exe파일을 똑같은 폴더에 복사해서 새이름으로 make로 저장합니다.두번째 방법은 이클립스에서 변경하는 방법입니다.Window -> Preferences -> C/C++ -> New CDT project wizard -> Makefile project를 보면
중간에 빨간상자부분이 있는데 저 부분을 mingw실행파일에 맞게 바꿔주면 됩니다.둘중에 하나만 하면 됩니다.이제 설정은 다 됐습니다. 언제나 그렇듯이 설정이 끝나면 헬로우 월드를 찍어봅시다-_-;
Toolchain은 MinGW로 선택을 합시다. 프로젝트이름은 HelloWorld로...-_-;HelloWorld프로젝트이름에 오른쪽버튼을 눌러서 New -> Source File 해서 HelloWorld.c 파일을 생성합시다.
HelloWorld.c (Language : c)
#include int main(void){ printf("Hello World!"); return 0;} 저렇게 적읍시다. 아 그리고 마지막 } 이거다음에 한칸 띄워주고 그다음칸을 한칸더 생성해줍시다. 그냥 }스페이스바하고 엔터 하고 저장합시다. 안그러면 no newline at end of file 라는 워링이 뜹니다-_-;검색해보니 뭐 무슨 어쩌구 설명을 하는데 잘모르겠습니다-_-;컴파일 단축키로 컴파일해봅시다. 저장하고 Ctrl + B를 누릅시다.컴파일이 완료되면 run단축키인 Ctrl + F11을 누릅시다.그러면 아래에 Console탭에 Hello World! 라고 뜰껍니다.컴파일할 때 object파일인 *.o도 만들고 윈도우실행파일인 *.exe도 만들어줍니다.
머드초보 이 작성.
TAGS , ,

Thursday, December 10, 2009

Unix Domain Socket - UDP

http://www.joinc.co.kr/modules/moniwiki/wiki.php/article/Unix_Domain_Socket_UDP

Unix Domain Socket - UDP

윤 상배

dreamyun@yahoo.co.kr



1절. 소개

우리는 이미 이전에 Unix Domain Socket를 이용한 IPC 에서 Unix Domain Socket 에 대한 기본적인 내용을 다루었었다.

그때는 SOCK_STREAM 을 이용한 연결지향의 Socket 를 사용했었는데, internet 소켓과 마찬가지로 UDP 특성 Socket 를 사용할수도 있다. 이번 글 에서는 UDP 를 이용한 Unix Domain Socket 에 대해서 알아보도록 하겠다.


2절. Unix Domain Socket (UDP)

2.1절. 특징

internet socket 상에서 UDP 를 다룰때, 관심을 가지는 특성이 비연결지향성이며, 그런이유로 데이타를 유실할수도 있으며, 데이타의 순서가 바뀔수 있다. 그러므로 중요 데이타 통신을 위한 프로토콜로는 UDP 가 적합하지 않다. 라는 점이 될것이다.

그러나 Unix Domain 영역의 UDP 는 internet 영역과는 특성이 좀 다르다고 할수 있다. Unix Domain 에서의 UDP 는 일종의 PIPE 와 같은 형태로 다루어진다. 게다가 동일한 같은 시스템 영역에서의 데이타 교환이므로 패킷이 유실되거나, 순서가 뒤바뀌는 등의 문제는 전혀 발생하지 않을거라고 장담할수 있다.


2.2절. 메시지 전달 방식

internet socket 에 전달되는 패킷들이 헤더 + 바디 로 이루어지는 것과 마찬가지로, Domain 영역에서의 데이타도 헤더 + 바디 로 이루어진다. 그러나 헤더의 구성요소는 매우 다르다.

Domain 영역에서의 데이타 통신은 알겠지만 "지정된 파일" 을 통해서 이루어지게 된다. 이 파일로 데이타를 쓰고/읽는 작업을 하며, 이것은 마치 FIFO 와 같은 pipe 형태로 전달된다.

그러므로 헤더영역에는 다른 기타의 정보가 들어가는게 아닌 통신을 위한 파일정보 가 들어가게 된다. /usr/include/sys/un.h 의 sockaddr_un 구조체를 보면 확실히 이해가 가능할 것이다.

struct sockaddr_un {     __SOCKADDR_COMMON (sun_);     char sun_path[108];     /* Path name.  */ }; 			
위의 구조체를 보면, sockaddr_in 과는 달리 단지 파일의 위치 지정을 위한 108바이트 크기의 배열변수만 있는것을 알수 있다.


2.3절. 서버/클라이언트 구성방식

internet socket 을 이용한 서버/클라이언트 구성과는 달리 Domain 영역에서 udp 를 이용한 서버/클라이언트의 구성은 주의해야 될 몇가지 점이 있다.

가장 기본적인 문제는 Domain 영역에서의 udp 는 Internet 영역에서의 udp 와는 달리 경로를 지정 할수 없다는 단점을 가진다. 이것은 다중의 클라이언트를 받아들이는 서버의 경우 문제가 될수 있다.

다음은 Internet 영역의 udp 서버/클라이언트 모델의 경우이다.

                   +----------+           +------- | Client 1 | address 1           |        +----------+  +------+ |        +----------+  | 서버 |-+------- | Client 2 | address 2   +------+ |        +----------+           |        +----------+           +------- | Client 3 | address 3                    +----------+ 			
Internet 영역에서의 서버는 단지 하나의 소켓지정번호(endpoint) 만을 가지고도 다중의 클라이언트와의 연결이 가능하다. 그 이유는 Internet 영역에서 통신을 할때 UDPIP 상위 프로토콜이므로 IP 프로토콜의 헤더를 통해서 해당 메시지가 어디에서 출발했는지 알수 있기 때문이다. 그러므로 굳이 select(2) 나 poll(2) 같은 작업없이 단지 하나의 소켓지정번호로 통신이 가능해진다.( UDP 프로그래밍의 기초를 참고하라.)

그러나 Unix Domain 영역의 udp 소켓이라면 이야기가 달라진다. 기본적으로 가질수 있는 정보가 "파일 경로" 뿐이므로 다중의 클라이 언트로 부터 메시지를 받았을때 어떤 클라이언트로 부터 메시지가 전달되었는지를 알수가 없는 것이다. 물론 이것은 데이타 영역에 클라이언트를 식별할수 있는 어떤 정보를 보냄으로써 해결할수 있긴 하겠지만, 설사 그렇다 하더라도 서버는 다중의 클라이언트중 원하는 클라이언트(client)로 메시지를 보낼방법이 없다. 만약 다중의 클라이언 트가 떠있는 상태에서 메시지를 보낸다면 누가 먼저 읽을지 예측할수가 없게된다.

이문제를 해결하기 위해서는 각 클라이언트 마다 통신을 위한 각각의 소켓지정파일 을 생성할수밖에 없다. 이것은 FIFO 에서 서버/다중클라이언트 를 구성하기 위한 방법과 마찬가지이다.

                  +----------+               +---| Client 1 |  +------+     |   +----------+  |      |-----+   +----------+  | 서버 |---------| Client 2 |  |      |-----+   +----------+  +------+     |   +----------+                   +---| Client 3 |                   +----------+ 			
결국에는 Domain 영역에서 udp 소켓을 사용하기 위해서는 select, poll 등을 써야만 한다는 결론에 도달한다.

여기에서 한가지 궁금한점이 문득 생길것이다. Domain 영역 UDP 소켓 에서의 다중클라이언트를 받아들이기 위한 서버의 제작을 위해서는 그럼 클라이언트의 숫자만큼 소켓파일을 생성시켜야한다는 뜻인데, 그렇다면 서버는 어떻게 클라이언트가 접근할줄 알고 클라이언트와 서버 모두가 알수 있는 소켓파일을 생성할것인가 ?

이문제의 해결방법은 여러가지가 될것이다. 마치 internet 의 TCP 소켓이 다중의 연결을 받아들이기 위해서 accept(2) 하는 하나의 endpoint 소켓을 두는 것처럼, Domain 영역에서도 하나의 endpoint 파일을 두는 것이다. 물론 이 endpoint 파일의 이름은 서버와 클라이언트간에 약속이 되어 있어야 할것이다.

그러먼 서버는 최초에 endpoint 를 위한 소켓파일을 하나 생성하고 이것을 통해서 클라이언트가 접근하면 적당한 이름으로 새로운 소켓파일을 만든다음에 이 파일이름을 클라이언트에게 전달해서 클라이언트가 전달받은 파일이름으로 쓰기를 하도록 하면 된다.


2.3.1절. 예상과는 달리 사용하는게 좀 번거롭군요 ?

예상과는 달리 다중의 클라이언트를 받아들이도록 Domain 영역 udp 소켓을 구성하는게 좀 까다롭다라는 생각이 들수도 있을 것이다.

그러나 어차피 내부 시스템에서의 통신용이라면 대부분의 경우 여러개의 클라이언트가 붙는다고 하더라도, 그 클라이언트가 정해져 있는 경우가 대부분이다. 그러므로 그냥 각 클라이언트 마다 어떤 파일을 통신에 쓰라고 아예 지정해주면 된다. 그러면 단지 select 나 poll 만을 돌림으로써, 간단하게 서버/클라이언트 프로그래밍이 가능하다.

또한 Domain 영역에서의 TCP 소켓과는 달리 다소 복잡한 연결과 연결대기를 위한 작업이 필요가 없으므로 코딩하기도 간편하다는 장점도 가지고 있다. 또한 패킷단위로 주고 받음으로 직관적으로 이해하기 쉬운 코드를 만들수 있다는 장점도 가진다.


2.4절. 예제 코드

그럼 예제 프로그램을 만들어 보도록 하겠다. UDP 프로그래밍의 기초 에서 다루었던 덧셈 프로그램인 sum_server.c 와 sum_client.c 를 Domain 영역에서 동작하도록 변경시킬 것이다. 위의 프로그램들을 참조하면서 코딩하도록 하자.

프로그램자체는 일반적인 Internet UDP 소켓 프로그램과 거의 동일함으로 이해하는데 이해가 없을것이다.


2.4.1절. 서버프로그램

예제 : sum_server.c

#include  #include  #include  #include  #include  #include  #include  #include  #include   struct data {     int a;     int b;     int sum; }; int main(int argc, char **argv) {     int sockfd;     int clilen;     struct data mydata;     struct sockaddr_un clientaddr, serveraddr;      sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);      if (sockfd < href="http://www.joinc.co.kr/modules/moniwiki/wiki.php/manSearch?google=none&name=perror">perror("socket error : ");         exit(0);     }         unlink(argv[1]);      bzero(&serveraddr, sizeof(serveraddr));     serveraddr.sun_family = AF_UNIX;     strcpy(serveraddr.sun_path, argv[1]);      if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < href="http://www.joinc.co.kr/modules/moniwiki/wiki.php/manSearch?google=none&name=perror">perror("bind error : ");         exit(0);     }     clilen  = sizeof(clientaddr);        while(1)     {         recvfrom(sockfd, (void *)&mydata, sizeof(mydata), 0, (struct sockaddr *)&clientaddr, &clilen);          printf("%d + %d = %d\n", mydata.a, mydata.b, mydata.a + mydata.b);             sendto(sockfd, (void *)&mydata, sizeof(mydata), 0, (struct sockaddr *)&clientaddr, clilen);     }     close(sockfd);     exit(0); } 				


2.4.2절. 클라이언트 프로그램

예제 : sum_client.c

// 클라이언트 아래와 같이 하면 안됩니다.  // 서버에서 보낼 때, 클라이언트가 바인드해야 정보를 받을 수 있습니다.  // 이 글을 보시는 분은 적절한 코드로 수정해 주시기 바랍니다.  // 그외 기타 간단한 오타 및 타입 오류는 제가 수정하고 갑니다. - 용운  #include  #include  #include  #include  #include  #include  #include  #include  #include   struct data {     int a;     int b;     int sum; }; int main(int argc, char **argv) {     int sockfd;     struct sockaddr_un serveraddr;     int  clilen;     struct data mydata;      sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);      if (sockfd < href="http://www.joinc.co.kr/modules/moniwiki/wiki.php/manSearch?google=none&name=perror">perror("exit : ");         exit(0);     }      bzero(&serveraddr, sizeof(serveraddr));     serveraddr.sun_family = AF_UNIX;     strcpy(serveraddr.sun_path, argv[1]);     clilen = sizeof(serveraddr);      mydata.a = atoi(argv[2]);     mydata.b = atoi(argv[3]);     mydata.sum =0;      if (sendto(sockfd, (void *)&mydata, sizeof(mydata), 0,                  (struct sockaddr *)&serveraddr, clilen) < href="http://www.joinc.co.kr/modules/moniwiki/wiki.php/manSearch?google=none&name=perror">perror("send error : ");         exit(0);     }     if (recvfrom(sockfd, (void *)&mydata, sizeof(mydata), 0,                 (struct sockaddr *)&serveraddr, (socklen_t *)&clilen) < href="http://www.joinc.co.kr/modules/moniwiki/wiki.php/manSearch?google=none&name=perror">perror("recv error : ");         exit(0);     }     printf("result is : %d\n", mydata.sum);       close(sockfd);     exit(0); } 				


2.4.3절. 테스트

먼저 서버를 띄운다음에, 클라이언트를 통해서 더하고 싶은 2개의 숫자를 인자를 통해서 넘기면 된다.

[root@localhost src]# ./unix_domain_udp_server /tmp/test_udp  5 + 6 = 11 7 + 1024 = 1031 ... 				
[root@localhost src]# ./unix_domain_udp_client /tmp/test_udp 5 6 [root@localhost src]# ./unix_domain_udp_client /tmp/test_udp 7 1024 				


3절. 결론

다른 Domain 영역 소켓과 마찬가지로, IPC 대용으로 사용가능하다. TCP 소켓이 IPC 대용으로 사용하기가 비교적 복잡하다는 단점과, 오늘 알아본 UDP 도메인 소켓과 아주 비슷한 FIFO 가 범용성이 좀더 떨어지는 단점을 보완한 적당하고 편리하게 사용가능한 IPC 로써의 용도를 제공해 줄것이다.




discussion by DISQUS

Add New Comment

[펌] UDS (UNIX 도메인소켓) 을 이용한 IPC

http://altibase.egloos.com/466834

[펌] UDS (UNIX 도메인소켓) 을 이용한 IPC

Unix Domain Socket(이하 UDS) 는 socket API를 수정없이 이용며,
port 기반의 Inernet Domain Socket에 비해서 로컬 시스템의 파일시스템을
이용해서 내부프로세스간의 통신을 위해 사용한다는 점이 다르다고 할수 있다.

ls 를 이용해서 통신을 위해서 만들어진 파일을 보면 다음과 같은 모습을
보인다.

[yundream@localhost tmp]$ ls -al
srwx------ 1 root nobody 0 12월 14 21:16 .fam_socket


보면 파일타입에 "s" 가 붙어 있는걸 알수 있으며, 파일사이즈가 0으로 되어 있는
걸 알수 있다. 왜냐하면 FIFO와 마찬가지로 메시지가 파일로 쌓이지 않고
커널로 전달되어서 커널에서 처리하기 때문이다.


파일을 통해서 통신을 하며, 커널내부에서 메시지를 관리한다는 점에서
FIFO와 매우 유사한면을 보여주지만, FIFO와는 달리 양방향 통신이 가능하다는
특징을 가지고 있다. 그러므로 다중의 클라이언트를 받아들이는 서버/클라이언트
모델을 만들기가 매우 쉽다.

또한 Inet 소켓을 통한 외부통신에 비해서 2배 이상의 효율을 보여준다라는
장점을 가지고 있다.


많은 경우 약간 복잡한 내부프로세스간 통신을 해야된다고 했을때 UDS을 많이
사용한다. INET 계층에서의 통신이 TCP/IP 4계층을 모두 거치는것과는
다르게, UDS 은 어플리케이션 계층에서 TCP 계층까지만 메시지가 전달되고,
다시 곧바로 어플리케이션 계층으로 메시지가 올라가게 된다. TCP/IP 계층에 대한
자세한 내용은 TCP/IP 개요(2)를 참고 하기 바란다.



위에서 INET 소켓보다 2배이상의 효율을 가진다고 했는데,
4계층의 레이어를 모두 거쳐야하는 INET 소켓에 비해서 단지 2개의 레이어만
사용한다는 점도 그 이유중 하나로 작용한다.


쏘쓰 코드는 다중연결서버 만들기(1)의 zipcode_multi.c 와
셈플로 알아보는 소켓프로그래밍(1)의 zipcode_cli.c 를 사용하도록할것이다.


예제: zipcode_local.c



#include 

#include
#include
#include
#include
#include

#include
#include

int main(int argc, char **argv)
{
int server_sockfd, client_sockfd;
int state, client_len;
pid_t pid;

FILE *fp;
struct sockaddr_un clientaddr, serveraddr;

char buf[255];
char line[255];

if (argc != 2)
{
printf("Usage : ./zipcode [file_name]\n");
printf("예 : ./zipcode /tmp/mysocket\n");
exit(0);
}

memset(line, '0', 255);
state = 0;

if (access(argv[1], F_OK) == 0)
{
unlink(argv[1]);
}
// 주소 파일을 읽어들인다.
client_len = sizeof(clientaddr);
if((fp = fopen("zipcode.txt", "r")) == NULL)
{
perror("file open error : ");
exit(0);
}

// internet 기반의 스트림 소켓을 만들도록 한다.
if ((server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
perror("socket error : ");
exit(0);
}
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sun_family = AF_UNIX;
strcpy(serveraddr.sun_path, argv[1]);


state = bind(server_sockfd , (struct sockaddr *)&serveraddr,
sizeof(serveraddr));
if (state == -1)
{
perror("bind error : ");
exit(0);
}

state = listen(server_sockfd, 5);
if (state == -1)
{
perror("listen error : ");
exit(0);
}


printf("accept : \n");
while(1)
{
client_sockfd = accept(server_sockfd, (struct sockaddr *)&clientaddr,
&client_len);
printf("test test\n");
pid = fork();
if (pid == 0)
{
if (client_sockfd == -1)
{
perror("Accept error : ");
exit(0);
}
while(1)
{
memset(buf, '0', 255);
if (read(client_sockfd, buf, 255) <= 0)
{
close(client_sockfd);
fclose(fp);
exit(0);
}

if (strncmp(buf, "quit",4) == 0)
{
write(client_sockfd, "bye bye\n", 8);
close(client_sockfd);
fclose(fp);
break;
}

while(fgets(line,255,fp) != NULL)
{
if (strstr(line, buf) != NULL)
write(client_sockfd, line, 255);
memset(line, '0', 255);
}
write(client_sockfd, "end", 255);
printf("send end\n");
rewind(fp);
}
}
}
close(client_sockfd);
}






다음은 클라이언트 프로그램이다.

예제: zipcode_cli_local.c



#include 
#include

#include
#include
#include
#include
#include

#include


int main(int argc, char **argv)
{

int client_len;
int client_sockfd;

FILE *fp_in;
char buf_in[255];
char buf_get[255];

struct sockaddr_un clientaddr;

if (argc != 2)
{
printf("Usage : ./zipcode_cl [file_name]\n");
printf("예 : ./zipcode_cl /tmp/mysocket\n");
exit(0);
}


client_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (client_sockfd == -1)
{
perror("error : ");
exit(0);
}
bzero(&clientaddr, sizeof(clientaddr));
clientaddr.sun_family = AF_UNIX;
strcpy(clientaddr.sun_path, argv[1]);
client_len = sizeof(clientaddr);
if (connect(client_sockfd, (struct sockaddr *)&clientaddr, client_len) < 0)
{
perror("Connect error: ");
exit(0);
}
while(1)
{
printf("지역이름 입력 : ");
fgets(buf_in, 255,stdin);

buf_in[strlen(buf_in) - 1] = '0';
write(client_sockfd, buf_in, 255);
if (strncmp(buf_in, "quit", 4) == 0)
{
close(client_sockfd);
exit(0);
}
while(1)
{
read(client_sockfd, buf_get, 255);
if (strncmp(buf_get, "end", 3) == 0)
break;

printf("%s", buf_get);
}
}

close(client_sockfd);
exit(0);
}







기존의 INET 버젼의 프로그램과 비교해 보면 고작 3줄 정도만 수정되었음을
알수 있을것이다. 단지 소켓 구조체가 sockaddr_un 으로 바뀌고,
AF_INET 대신 AF_UNIX 를 그리고 port 번호대신에 파일명을 사용했음을
알수 있다.


나머지의 모든 코드는 INET 코드와 완전히 같다. 그러므로
Unix Domain Socket 를 사용하면 Inet Domain Socket 와 코드 일관성을
유지할수 있으며, 동일한 기술을 사용해서 프로그래밍을 할수 있다.


또한 다른 대부분의 IPC 설비들이, 범용적으로 사용하기에는 부족한 여러가지
단점들을 가진반면(단방향 이거나, 읽기만 가능하다거나, 제어하기가 어려운)
UDS는 매우 범용적인 IPC 로써 사용가능하다라는 장점을 가지고 있다.


실제로 X 서버 같은경우에 외부에서의 접근시에는 INET 연결을 내부에서의
연결을 위해서는 UDS 를 사용한다. 이밖에도 mysql, pgsql, KDE, Gnome 과 같은
대부분의 서버프로그램이 내부통신을 위해서 UDS 를 사용한다.