QT에서 UDS 통신 프로그램을 작성하면서 타이머와 POLL을 이용했습니다만, 시간이 날때 마다 UDS도 QSocket처럼 SIGNAL과 SLOT를 이용하는 방법이 없을까 찾아 보았습니다. 그러나 QSocket 소스를 뒤져바도 잘 모르겠더군요. 하는 수 없이 결국 제가 알고 있는 방법인 POLL을 이용하는 방법을 먼저 올리게 되었습니다.

그러나 제 글을 보신 (주)FALINUX의 유영창이사님께서 QSocketNotifier를 알려 주셨습니다. POLL을 이용해도 좋지만 역시 QT는 QT다웁게 작성하는 것이 어떻겠냐며, 매우 겸손하고 조심스럽게 말씀을 주셨습니다.

       QSocketNotifier?!!

이런 제가 소스 분석을 수박 겉핥기식으로 했나요? 저는 그 객체가 있는지를 몰랐습니다 말씀 드렸더니,

    "라이브러리 소스 중 network 디렉토리만 보셨죠? ㅋㅋ"

헉!! 어떻게 아셨지? ^^;

QSocketNotifier은 kernel 디렉토리에 있답니다. 이런...Qt Reference Documentation 페이지를 자주 보는데도 바로 밑에 있는 QSocketNotifier를 못 보았을까요? 이궁~

방법을 알았으니 코드로 직접 확인해 보아야 겠지요. ^^

프로그램 코딩 시작

역시 작업 순서대로 글을 올리겠습니다.

  1. 프로제트 파일 생성, "C++ Project"를 선택합니다.


  2. 적당한 프로젝트 이름을 입력합니다.


  3. 프로젝트 파일을 저장합니다.


  4. 폼 이름을 지정합니다. 이 이름이 클래스 이름이 되므로 적당한 이름을 지정해 줍니다. 여기서는 frmMain으로 입력하겠습니다.


  5. 필요한 위젯을 배치합니다.


  6. labCounter는 타이머 작동을 확인하기 위해 준비했으며, 타이머가 작동할 때 마다, 숫자가 하나씩 증가할 것입니다.

  7. labReceive 라벨은 수신된 자료를 출력하겠습니다.

  8. 슬롯을 등록합니다.
    onCenterTimer()은 타이머를 위한 슬롯입니다.
    onReceiveFromUds()는 UDS 소켓으로부터 수신된 자료를 처리하기 위한 슬롯입니다.
    init()는 폼이 생성된 후에 발생되는 함수입니다.


  9. 전체 소스입니다. 이전 강좌 소스와는 달리 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 윈도우에 전송한 문자열이 출력됩니다.