그래픽 & 멀티미디어
QT에서 UDS 통신 프로그램을 작성하면서 타이머와 POLL을 이용했습니다만, 시간이 날때 마다 UDS도 QSocket처럼 SIGNAL과 SLOT를 이용하는 방법이 없을까 찾아 보았습니다. 그러나 QSocket 소스를 뒤져바도 잘 모르겠더군요. 하는 수 없이 결국 제가 알고 있는 방법인 POLL을 이용하는 방법을 먼저 올리게 되었습니다.
그러나 제 글을 보신 (주)FALINUX의 유영창이사님께서 QSocketNotifier를 알려 주셨습니다. POLL을 이용해도 좋지만 역시 QT는 QT다웁게 작성하는 것이 어떻겠냐며, 매우 겸손하고 조심스럽게 말씀을 주셨습니다.
QSocketNotifier?!!
이런 제가 소스 분석을 수박 겉핥기식으로 했나요? 저는 그 객체가 있는지를 몰랐습니다 말씀 드렸더니,
"라이브러리 소스 중 network 디렉토리만 보셨죠? ㅋㅋ"
헉!! 어떻게 아셨지? ^^;
QSocketNotifier은 kernel 디렉토리에 있답니다. 이런...Qt Reference Documentation 페이지를 자주 보는데도 바로 밑에 있는 QSocketNotifier를 못 보았을까요? 이궁~
방법을 알았으니 코드로 직접 확인해 보아야 겠지요. ^^
프로그램 코딩 시작
역시 작업 순서대로 글을 올리겠습니다.
- 프로제트 파일 생성, "C++ Project"를 선택합니다.
- 적당한 프로젝트 이름을 입력합니다.
- 프로젝트 파일을 저장합니다.
- 폼 이름을 지정합니다. 이 이름이 클래스 이름이 되므로 적당한 이름을 지정해 줍니다. 여기서는 frmMain으로 입력하겠습니다.
- 필요한 위젯을 배치합니다.
- labCounter는 타이머 작동을 확인하기 위해 준비했으며, 타이머가 작동할 때 마다, 숫자가 하나씩 증가할 것입니다.
- labReceive 라벨은 수신된 자료를 출력하겠습니다.
- 슬롯을 등록합니다.
onCenterTimer()은 타이머를 위한 슬롯입니다.
onReceiveFromUds()는 UDS 소켓으로부터 수신된 자료를 처리하기 위한 슬롯입니다.
init()는 폼이 생성된 후에 발생되는 함수입니다.
- 전체 소스입니다. 이전 강좌 소스와는 달리 POLL이 없고 대신에 QSocketNotifier이 추가되었습니다.
#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 <qapplication.h> #include <qtimer.h> #include <qsocketnotifier.h> #include <qmessagebox.h> #define SOCK_LOCALFILE "/tmp/uds_a" #define BUFF_SIZE 1024 char buffReceive[BUFF_SIZE+5]; size_t szAddr; struct sockaddr_un addrLocal; struct sockaddr_un addrGuest; QTimer *tmrCenter; QSocketNotifier *notUdsRead; int sock; int nCounter; // 타이머에서 출력할 카운터로, 프로그램이 block되었는지 // 확인하기 위해 사용합니다. // 폼이 생성된 후에 불리워지는 함수, init()입니다. // 타이머, 소켓 그리고 QSocketNotifier를 생성하겠습니다. void frmMain::init() { // 타이머를 생성하고 카운터를 0으로 초기화 합니다. tmrCenter = new QTimer( this); nCounter = 0; // UDS에서 사용할 파일이 존재하면 삭제합니다. if ( 0 == access( SOCK_LOCALFILE, F_OK)) unlink( SOCK_LOCALFILE); // UDS 소켓을 생성합니다. sock = socket( PF_FILE, SOCK_DGRAM, 0); if( -1 == sock) { QMessageBox::information( this, "UDS", "I cannot build a socket!!"); return; } // UDS 소켓 정보를 완성하고 bind()함수를 호출하여 // 다른 시스템에서 전송되어 오는 자료를 수신할 준비를 합니다. memset( &addrLocal, 0, sizeof( addrLocal)); addrLocal.sun_family = AF_UNIX; strcpy( addrLocal.sun_path, SOCK_LOCALFILE); if( -1 == bind( sock, (struct sockaddr*)&addrLocal, sizeof( addrLocal)) ) { QMessageBox::information( this, "UDS", "bind error!!"); return; } // UDS로부터 수신을 감시하는 QSocketNotifier를 생성합니다. // 그리고 생성한 QSocketNotifier 시그널에 대해 슬롯을 연결합니다. notUdsRead = new QSocketNotifier( sock, QSocketNotifier::Read, this); connect( notUdsRead, SIGNAL( activated( int)), this, SLOT( onReceiveFromUds())); // 타이머와 종료 버튼 시그널 처리입니다. connect( tmrCenter, SIGNAL( timeout()), this, SLOT( onCenterTimer())); connect( btnClose, SIGNAL( clicked()), qApp, SLOT( quit())); // 타이머를 가동합니다. tmrCenter->start(500, false); } // 이전 강좌에 올려진 타이머하고는 비교되지 않을 정도로 할 일이 없어져죠. // 프로그램이 block되지 않았는지 확인하기 위해 카운터를 출력합니다. void frmMain::onCenterTimer() { labCounter->setNum( nCounter++); // 카운터를 출력하고 증가합니다. } // UDS 소켓으로부터 자료가 수신되었다면, 아래의 슬롯 함수가 실행됩니다. // 수신된 값을 읽어 들이고 화면에 출력합니다. void frmMain::onReceiveFromUds() { int szRead; szAddr = sizeof( addrGuest); memset( buffReceive, 0, BUFF_SIZE); szRead = recvfrom( sock, buffReceive, BUFF_SIZE, 0 , ( struct sockaddr*)&addrGuest, &szAddr); // 여기까지 실행되었다면 자료가 수신된 것입니다. // 화면의 라벨에 출력하고 buffReceive[szRead] = '\0'; labReceive->setText( buffReceive); // 상대방 시스템으로 전송합니다. sendto( sock, buffReceive, szRead+1, 0, ( struct sockaddr*)&addrGuest, szAddr); }
프로그램 완성
main()함수를 위해 main.cpp를 생성하고 컴파일하면 모든 작업이 끝납니다.
프로젝트를 빌드하기 위해서 main.cpp를 생성합니다. File >> New메뉴를 선택합니다. main.cpp를 선택합니다.
frmMain을 선택하고 프로젝트를 빌드합니다.
]# qmake ]# make ]# ./uds -> 프로젝트 파일 이름
프로그램 실행
프로그램을 실행하면 Count 값이 계속 증가되는 것을 보실 수 있습니다. 이렇게 증가되는 것을 보시면 프로그램이 block되지 않았음을 알 수 있습니다.
.
프로그램 테스트를 위해 .tester_user 폴더에 uds.c 파일을 실행합니다.
]$ gcc uds.c ]$ ./a.out forum.falinux.com
이렇게 UDS로 문자열을 전송하면 frmMain 윈도우에 전송한 문자열이 출력됩니다.
- QT,
- IPC,
- UDS,
- QSocketNotifier