댓글 쓰기 권한이 없습니다. 로그인 하시겠습니까?
개념
2012.02.28 13:41
UDS (Unix Domain Socket)
조회 수 34717 댓글 0
프로세스 끼리의 통신 UDS 내부 프로세스들 끼리의 통신을 위한 IPC 방법 중에 UDS 를 이용하여 프로그램을 작성하는 방법에 대해 알아 봅니다. UDS UDS(Unix Domain Socket)은 내부 프로세스들 끼리 TCP 또는 UDP 프로토콜을 이용하여 통신할 수 있도록 도와주는 소켓입니다. 이 소켓을 이용하시면 기존의 TCP/IP, UDP/IP에서 사용하던 함수로 같은 방법을 사용하여 프로세스들끼리 통신할 수 있어 매우 편리하며, 이점이 장점입니다. 말씀 드린 바와 같이 TCP/IP, UDP/IP와 같은 함수를 사용하고 다만 필요한 변수값만 바꾸어 주면 됩니다. 예를 들어 소켓을 생성해 보겠습니다. TCP/IP와 UDP/IP에서 처럼 socket() 함수를 이용합니다. 우선 #include <sys/socket.h> 대신에 #include <sys/un.h>를 사용합니다. 자, socket() 함수를 호출해 보겠습니다.
지금 UDP 프로토콜을 이용하기 위한 UDS 소켓 하나를 만들었습니다. 기존의 UDP/IP와는 달리 PF_INET 대신에 PF_FILE을 사용했습니다. UDS는 데이터를 전송하기 위해 파일을 이용하기 때문입니다. UDS의 사용은 이와 같습니다. 기존의 TCP/IP, UDP/IP 통신에서 사용하는 함수를 그대로 사용하면서 다만 필요한 정보를 몇 가지 바꾸어 주기만 하면 됩니다. socket()에 대한 더 자세한 말씀은 "Unix C Reference의 11장 7절 소켓 열고 닫기"를 참고하십시오. 이번에는 bind() 함수를 이용하여 소켓에 통신 케이블을 연결해 보겠습니다. bind() 함수를 이용하기 위해서는 주소정보가 필요합니다. 이를 위해서는 struct sockaddr_in 대신에 struct sockaddr_un을 사용합니다.
역시 TCP/IP, UDP/IP에서 사용했던 루틴이죠? 다만 sun_family 속성에 AF_UNIX 또는 AF_FILE로 대입해 주고, sun_path에 통신에 사용할 파일을 지정해 주시면 됩니다. 이후로는 기존의 TCP/IP, UDP/IP 프로그램하고 똑 같습니다!! 너무 멋지지 않습니까? 다만 소켓을 close() 해도 에 지정한 파일이 삭제되지 않아서 실행하기 전에 미리 삭제해 주어야 한다는 것 외에는 기존 루틴을 그대로 사용할 수 있습니다. 이런 귀찮은 것도 아래 코드로 간단히 해결할 수 있습니다.
이제 UDS를 이용한 TCP와 UDP 프로토콜 구현하는 프로그램을 보도록 하겠습니다. UDS를 이용한 TCP 아래의 헤더파일을 꼭 포함합니다.
서버 소켓에 사용할 파일의 이름을 상수로 정의하겠습니다. 이 파일은 서버 프로그램이나 클라이언트나 모두 같이 사용되는 파일 이름입니다.
예제 프로그램에서는 클라이언트 프로그램이 서버 프로그램에게 문자를 전송하면 서버가 문자열의 길이와 전송한 문자열을 반송하는 프로그램을 작성해 보겠습니다. TCP 서버 프로그램 소켓 변수를 선언하고 소켓을 생성합니다. PF_INET가 아닌 PF_FILE입니다.
이제 리슨 대기 모드에 사용할 서버 소켓으로 등록하기 위해 먼저 bind() 로 소켓에 주소 정보를 지정합니다. struct sockaddr_un 구조체 변수를 선언부터 하겠습니다.
bind() 실행에 에러가 없다면 이제 리슨 대기 모드로 진입합니다.
listen()에서 에러 없이 복귀했다면 클라이언트로부터 연결이 요청된 것입니다. accept() 로 접속을 수락합니다. 클라이언트 주소 정보를 위해 struct sockaddr_un 구조체 변수를 선언하겠습니다.
accept() 까지 해서 접속이 이루어 졌습니다. 이제 read()/write()를 이용하여 통신을 하면 되겠습니다. 클라이언트로부터 수신한 문자열의 길이를 구한 후 클라이언트로 전송합니다.
그리고 close()를 이용하여 클라이언트와의 연결을 종료합니다.
TCP 클라이언트 프로그램 역시 소켓부터 생성합니다.
목적지인 서버 시스템의 주소 정보를 준비합니다.
이제 서버와 접속을 시도합니다.
connect() 함수에서 에러 없이 복귀했다면 접속이 된 것입니다. 이제 read()/write() 를 이용하여 서버와 통신합니다. 프로그램을 실행할 때 인수로 받은 문자열을 전송합니다.
서버에서는 전송된 문자열에 대한 문자열 길이를 계산해서 다시 보내 줍니다. 서버가 보내준 데이터를 받아서 화면에 출력한 후 close() 함수로 연결을 종료합니다.
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> #define BUFF_SIZE 1024 #define FILE_SERVER "/tmp/test_server" int main( void) { int server_socket; int client_socket; int client_addr_size; int option; struct sockaddr_un server_addr; struct sockaddr_un client_addr; char buff_rcv[BUFF_SIZE+5]; char buff_snd[BUFF_SIZE+5]; if ( 0 == access( FILE_SERVER, F_OK)) unlink( FILE_SERVER); server_socket = socket( PF_FILE, SOCK_STREAM, 0); if( -1 == server_socket) { printf( "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( "bind() 실행 에러n"); exit( 1); } while( 1) { if( -1 == listen(server_socket, 5)) { printf( "대기상태 모드 설정 실패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( "클라이언트 연결 수락 실패n"); exit( 1); } read ( client_socket, buff_rcv, BUFF_SIZE); printf( "receive: %sn", buff_rcv); sprintf( buff_snd, "%d : %s", strlen( buff_rcv), buff_rcv); write( client_socket, buff_snd, strlen( buff_snd)+1); // +1: NULL까지 포함해서 전송 close( client_socket); } return 0; } 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 "sample.h" #define BUFF_SIZE 1024 #define FILE_SERVER "/tmp/test_server" int main( int argc, char **argv) { int client_socket; struct sockaddr_un server_addr; char buff[BUFF_SIZE+5]; client_socket = socket( PF_FILE, SOCK_STREAM, 0); if( -1 == client_socket) { printf( "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"); exit( 1); } write( client_socket, argv[1], strlen( argv[1])+1); // +1: NULL까지 포함해서 전송 read ( client_socket, buff, BUFF_SIZE); printf( "%sn", buff); close( client_socket); return 0; } UDS를 이용한 UDP 프로그램 앞서 UDS를 이용한 TCP 프로그램을 이해하셨다면 UDP는 별반 다를 것이 없습니다. 예제에서는 두 개의 프로그램을 작성하며, 한쪽에서 문자열을 전송하면 받는 쪽은 문자열을 겹쳐서 반송해 줍니다. 이렇게 자료를 주고 받을 수 있도록 하기 위해 각각의 프로그램에 파일을 따로 따로 두도록 하겠습니다. 예제 프로그램 1은 /tmp/process_a 를 프로그램 2는 /tmp/process_b 파일을 사용하도록 하겠습니다. UDS를 이용한 UDP 프로그램 1 프로그램 1에서 사용할 소켓용 파일은 /tmp/process_a 입니다.
이전 실행에서 생성한 소켓용 파일을 삭제하겠습니다.
역시 소켓부터 만들겠습니다.
상대로부터의 자료를 수신하기 위해 주소정보를 준비하고 bind()함수로 소켓에 주소정보를 할당하고 커널에 올립니다.
에러없이 bind()가 실행되었다면 이제 recvfrom() 함수와 sendto() 함수를 이용하여 통신할 수 있습니다. recvfrom() 함수를 이용하여 상대로부터 전송되어온 전문을 수신합니다.
buff_rcv에는 수신한 데이터가 guest_addr 에는 전송지의 주소 정보가 있습니다. 예제인만큼 주소지 정보를 출력해 보겠습니다. 출력해 보면 두번째 프로그램의 소켓 파일인 /tmp/process_b 가 출력될 것입니다.
이제 수신한 자료를 중복하여 겹친 후 전송지로 다시 보내 줍니다.
UDS를 이용한 UDP 프로그램 2 프로그램 2은 실행 시 입력받은 인자를 전송하고 다시 프로그램 1에서 전송한 데이터를 출력해 보겠습니다. 우선 소켓용 파일을 정리합니다. 이번에는 SOCK_LOCALFILE 은 /tmp/process_b 임을 확인하여 주십시오.
이전 실행으로 생성된 소켓용 파일을 삭제합니다.
자, 소켓을 만듭니다.
상대편이 제대로된 주소 정보를 받을 수 있고, 또한 자신도 다른 프로세스로부터 자료를 수신 받기 위해 소켓에 주소정보를 할당해 줍니다.
이제 데이터를 전송할 목적지 주소를 구성합니다.
목적지인 프로그램 1로 자료를 전송합니다.
다시 프로그램 1로부터 recvfrom()을 이용하여 자료를 수신합니다. 이때 프로그램 1 에서 처럼 recvfrom()에서 송신지의 주소 정보를 구할 수 있으나, 이미 알고 있으므로 인수에 NULL로 간편화 하겠습니다.
수신한 자료를 출력하고 close()로 소켓을 닫아 통신을 종료하겠습니다.
UDP 프로그램 A #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/un.h> #define BUFF_SIZE 1024 #define SOCK_LOCALFILE "/tmp/process_a" int main( void) { int sock; size_t addr_size; struct sockaddr_un local_addr; struct sockaddr_un guest_addr; char buff_rcv[BUFF_SIZE+5]; char buff_snd[BUFF_SIZE+5]; if ( 0 == access( SOCK_LOCALFILE, F_OK)) unlink( SOCK_LOCALFILE); sock = socket( PF_FILE, SOCK_DGRAM, 0); if( -1 == sock) { printf( "socket 생성 실패n"); exit( 1); } memset( &local_addr, 0, sizeof( local_addr)); local_addr.sun_family = AF_UNIX; strcpy( local_addr.sun_path, SOCK_LOCALFILE); if( -1 == bind( sock, (struct sockaddr*)&local_addr, sizeof( local_addr)) ) { printf( "bind() 실행 에러n"); exit( 1); } while( 1) { addr_size = sizeof( guest_addr); recvfrom( sock, buff_rcv, BUFF_SIZE, 0 , ( struct sockaddr*)&guest_addr, &addr_size); printf( "receive: %sn", buff_rcv); printf( "%sn", guest_addr.sun_path); // 게스트 프로세스의 소켓용 파일 이름을 출력해 봅니다. sprintf( buff_snd, "%s%s", buff_rcv, buff_rcv); printf( "%sn", buff_snd); sendto( sock, buff_snd, strlen( buff_snd)+1, 0, ( struct sockaddr*)&guest_addr, sizeof( guest_addr));// +1: NULL까지 포함해서 전송 } } UDP 프로그램 B #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/un.h> #define BUFF_SIZE 1024 #define SOCK_LOCALFILE "/tmp/process_b" #define SOCK_TARGETFILE "/tmp/process_a" int main( int argc, char **argv) { int sock; struct sockaddr_un local_addr; struct sockaddr_un target_addr; char buff_rcv[BUFF_SIZE+5]; if ( 0 == access( SOCK_LOCALFILE, F_OK)) unlink( SOCK_LOCALFILE); sock = socket( PF_FILE, SOCK_DGRAM, 0); if( -1 == sock) { printf( "socket 생성 실패n"); exit( 1); } memset( &local_addr, 0, sizeof( local_addr)); local_addr.sun_family = AF_UNIX; strcpy( local_addr.sun_path, SOCK_LOCALFILE); if( -1 == bind( sock, (struct sockaddr*)&local_addr, sizeof( local_addr))) { printf( "bind() 실행 에러n"); exit( 1); } memset( &target_addr, 0, sizeof( target_addr)); target_addr.sun_family = AF_UNIX; strcpy( target_addr.sun_path, SOCK_TARGETFILE); sendto( sock, argv[1], strlen( argv[1])+1, 0, ( struct sockaddr*)&target_addr, sizeof( target_addr)); // +1: NULL까지 포함해서 전송 recvfrom( sock, buff_rcv, BUFF_SIZE, 0 , NULL, 0); printf( "%sn", buff_rcv); close( sock); return 0; } 출처 : http://unabated.tistory.com/entry/프로세스-끼리의-통신-UDS Dreamy의 코드 스크랩내가 모으고 내가 보는
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Designed by sketchbooks.co.kr / sketchbook5 board skin
Sketchbook5, 스케치북5
Sketchbook5, 스케치북5
Sketchbook5, 스케치북5
Sketchbook5, 스케치북5