2013.07.05 11:25


이제부터 프로그래밍이라는걸 시작해 보도록 하겠습니다.

대부분 프로그래밍의 처음이 그렇듯이 소켓 프로그램도 마찬가지로 간단한 문자열(Hello World)을 네트워크상에서 출력해 보는 것으로 시작하도록 하겠습니다.

일반적으로 소켓 프로그램은 서비스를 요청하는 클라이언트와 클라이언트의 요청을 받아 서비스하는 서버로 구성됩니다.

지금부터 구현할 소켓 프로그램은 클라이언트 프로그램이 네트워크상에서 통신 채널을 통해 서버측에 연결되면 서버 프로그램이 즉시 문자열을 클라이언트측에 전송하고, 클라이언트 프로그램은 전송받은 문자열을 화면에 출력합니다.

그럼 먼저 서버 프로그램을 구현해 보도록 하겠습니다.
서버 프로그램은 시작 버튼 하나와 상태를 알려주는 스태틱 텍스트로 구성되고, 사용자가 시작 버튼을 누르면 서버가 클라이언트와의 연결을 기다리는 ‘Server Start’라는 상태를 알리게 됩니다.

MFC로 서버 프로그램을 구현할 때 다음의 순서로 진행하도록 하겠습니다.

1 응용 프로그램 마법사로 프로젝트를 만듭니다.
2 소켓으로 사용할 CSocket::CListenSocket과 CSocket::CServiceSocket 객체를 생성합니다.
3 GUI를 설계하고, 각각의 컨트롤에 대해 멤버 함수와 멤버 변수를 추가하고 코딩합니다.
4 클라이언트 프로그램과 연결해서 서버로서의 기능을 검증합니다.

서버 프로그램을 구현하기 위해서 먼저 프로젝트를 생성하겠습니다.
대화 상자 기반으로 고급기능에서 ‘Window 소켓’을 체크하여 프로젝트를 생성합니다.
프로젝트 이름은 HelloServer로 하겠습니다.

저는 Visual Studio 2008을 사용하였습니다.

응용 프로그램 종류에서 대화 상자 기반으로 선택합니다.

고급기능에서 Window 소켓 항목을 체크하고 프로젝트를 생성합니다.

다음으로 소켓 객체(CSocket::CListenSocket과 CSocket::CServiceSocket)의 생성하겠습니다.
CListenSocket 객체는 클라이언트로부터의 연결 요청을 받아들이는 역할을 합니다. 이 객체는 클라이언트의 연결 요청을 받으면 OnAccept 메시지 처리기가 실행되어 연결 요청을 처리합니다.

먼저 MFC 클래스 마법사를 이용하여 CSocket 클래스를 기본클래스로 해서 CListenSocket 객체를 생성합니다.


CListenSocket 객체는 HelloServer 프로젝트의 주 대화 상자 객체와 서로 자료나 메시지를 주고 받으면서 작업해야 합니다. 따라서 프로젝트의 이름으로 HelloServer를 사용하기 때문에 주 대화 상자 객체의 이름은 CHelloServerDlg가 됩니다.

CHelloServerDlg 객체의 시작 버튼을 누르면 CListenSocket 객체를 새로 생성하게 됩니다. CListenSocket 객체로 클라이언트로부터의 연결 요청이 오면, OnAccept 메시지 처리기가 실행되지만 OnAccept 메시지 처리기에서 클라이언트의 연결 요청을 처리하지 않고, 이를 대화 상자 CHelloSeverDlg로 객체로 전달해서 처리하게 됩니다. 따라서 CHelloSeverDlg CListenSocket는 서로의 객체에 대한 주소를 주고 받습니다.

CListenSocket.h 파일을 다음과 같이 작성합니다.

소스 설명은 주석으로 대신 하도록 하겠습니다.

헤더 파일의 작성이 끝났습니다.

다음으로 CListenSocket.cpp을 다음과 같이 구현하겠습니다.

CListenSocket에 대한 준비가 끝났습니다.

다음으로 CListenSocket 객체를 이용하여 클라이언트로부터의 연결 요청을 받아들이고 해당 클라이언트에 대한 처리를 수행할 CServiceSocket 객체를 생성합니다.

CServiceSocket 객체 역시 HelloServer 프로젝트의 주 대화 상자인 CHelloServerDlg 객체와 자료를 송수신하기 위해 서로의 객체에 대한 주소를 주고 받습니다.

ServiceSocket.h을 다음과 같이 작성합니다.

ServiceSocket.cpp을 다음과 같이 구현합니다.

다음으로 GUI설계와 컨트롤에 대한 멤버 함수와 멤버 변수를 추가하도록 하겠습니다.
GUI를 다음과 같이 구성합니다.

HelloServer.h를 작성하도록 하겠습니다. 시작 버튼을 누르면 수행하는 OnBnClickedButtonConnect 메소드를 작성합니다.

CHelloServerDlg.cpp을 다음과 같이 구현합니다.

Hello World를 출력하기 위한 서버프로그램의 준비가 끝났습니다.

이제 빨리 클라이언트 프로그램을 만들어 서버 프로그램과 서로 잘 동작하는지 확인는 일만 남았습니다.

클라이언트 프로그램은 다음 시간에~^^;

그럼 오늘은 여기까지 입니다.

수고하셨습니다.

이 글은 열혈강의 Visual C++ 2008 MFC 윈도우 프로그래밍, 정석용의 TCP/IP 소켓 프로 그래밍 책을 참고하여 작성되었습니다.



신고


Posted by injunech
2013.07.05 06:58


제목/목차

1. 소개
2. 클라이언트와 서버간 통신 개요
3. 간단한 서버와 클라이언트 구현
3.1 서버 - 기다리는 소켓 만들기
3.2 클라이언트 - 서버로 연결
3.3 서버 - 클라이언트의 연결 시도 받아들이기
3.4 클라이언트와 서버 - 자료 주고받기
4 만든 클라이언트와 서버를 컴파일하고 테스트
4.1 파일 목록
4.2 컴파일과 테스트
5. 결론

1. 소개

소켓은 프로세스간에 자료를 교환하는 수단이다. 프로세스는 같은 컴퓨터에 있거나 네트웍으로 연결된 서로 다른 컴퓨터에 있을 수 있다. 소켓이 연결되면 한쪽이 연결을 닫을 때까지 양편 모두 자료를 보낼 수 있다.

나는 작업중인 프로젝트에 소켓이 필요해서 소켓 API 함수를 감싸는 C++ 클래스를 개발하고 다듬었다. 일반적으로 자료를 요청하는 프로그램을 클라이언트, 요청에 응답하는 프로그램을 서버라고 한다. 내가 만든 ClientSocket과 ServerSocket, 주된 두 클래스를 사용하여 클라이언트와 서버가 서로 자료를 교환할 수 있다.

이 글의 목표는 프로그램에서 ClientSocket과 ServerSocket 클래스를 사용하는 방법을 알리는 것이다. 먼저 클라이언트와 서버간 통신에대해 간단히 다룬 다음, 이 두 클래스를 사용하여 간단한 서버와 클라이언트 예제를 만들 것이다.

2. 클라이언트와 서버간 통신 개요

코드로 들어가기전에 전형적인 클라이언트와 서버간 연결 단계를 간단히 살펴보자. 다음 표는 단계들을 보여준다.

서버클라이언트
1. 기다리는 소켓(listening socket)을 만들고 클라이언트에서 연결을 기다린다.
2. 클라이언트 소켓을 만들고 서버로 연결을 시도한다.
3. 클라이언트의 연결 시도를 받아들인다.
4. 자료를 주고받는다.4. 자료를 주고받는다.
5. 연결을 닫는다.5. 연결을 닫는다.

기본적으로는 이렇다. 먼저 서버가 기다리는 소켓을 만들고 클라이언트의 연결 시도를 기다린다. 클라이언트가 자체 소켓을 만들고 서버와 연결을 시도한다. 서버는 연결을 받아들이고, 자료 교환이 시작된다. 소켓 연결을 통해 모든 자료가 전달되면 양쪽 중 하나가 연결을 닫는다.

3. 간단한 서버와 클라이언트 구현

이제 코드로 들어갈 시간이다. 이제부터 개요에서 다룬 단계를 모두 수행하는 클라이언트와 서버를 만든다. 우리는 일반적으로 일어나는 순서대로 - 예를 들어 먼저 소켓을 기다리는 서버 부분을 만든 다음 서버로 연결하는 클라이언트 부분을 만드는 등 - 구현한다. 전체 코드는 simple_server_main.cppsimple_client_main.cpp에서 볼 수 있다.

미리 소스코드를 살펴보고 실행해보려면 여기를 읽어봐라. 프로젝트 파일 목록과 어떻게 컴파일하고 테스트하는지를 설명한다.

3.1 서버 - 기다리는 소켓 만들기

첫번째 할 일은 클라이언트에서 들어오는 요청을 기다리는 간단한 서버를 만드는 것이다. 다음은 서버 소켓을 만드는 코드이다.

목록 1 : 서버 소켓 만들기 ( simple_server_main.cpp의 일부 )
#include "ServerSocket.h"
#include "SocketException.h"
#include 

int main ( int argc, int argv[] )
{
  try
    {
      // Create the server socket
      ServerSocket server ( 30000 );

      // rest of code -
      // accept connection, handle request, etc...

    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
    }

  return 0;
}


이것만으로 끝났다. ServerSocket 클래스의 생성자는 기다리는 소켓을 만드는데 필요한 소켓 API를 부른다. 자세한 사항을 감추기때문에 로컬 포트에 기다리기위해 이 클래스 객체를 생성하기만 하면 된다.

try/catch 문을 주목하라. ServerSocket과 ClientSocket 클래스는 C++의 예외처리 기능을 사용한다. 클래스 함수가 어떤 이유에서건 문제가 생기면 SocketException.h에 정의된 SocketException 형의 예외를 발생한다. 이 예외를 처리하지 않으면 프로그램이 끝나기때문에 처리해주는 것이 좋다. 위에서처럼 SocketException의 description() 함수를 사용하여 오류문을 얻을 수 있다.

3.2 클라이언트 - 서버로 연결

전형적인 클라이언트와 서버간 연결의 두번째 단계는 클라이언트가 서버로 연결을 시도하는 일이다. 코드는 방금 전에 본 서버 코드와 비슷하다.

목록 2 : 클라이언트 소켓 만들기 ( simple_client_main.cpp의 일부 )
#include "ClientSocket.h"
#include "SocketException.h"
#include 
#include 

int main ( int argc, int argv[] )
{
  try
    {
      // Create the client socket
      ClientSocket client_socket ( "localhost", 30000 );

      // rest of code -
      // send request, retrieve reply, etc...

    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\n";
    }

  return 0;
}


ClientSocket 클래스 객체를 만들기만하면 리눅스 소켓을 만들어서 생성자에 주어진 호스트와 포트로 연결한다. ServerSocket 클래스와 같이 생성자가 어떤 이유에서건 문제가 생기면 오류가 발생한다.

3.3 서버 - 클라이언트의 연결 시도 받아들이기

클라이언트와 서버간 연결의 다음 단계는 서버에서 일어난다. 서버가 클라이언트의 연결 시도를 받아들이면, 두 소켓 양단간 통신 채널이 열린다.

우리는 간단한 서버에 이 기능을 추가해야 한다. 다음은 수정된 버전이다.

목록 3 : 클라이언트 연결 받아들이기 ( simple_server_main.cpp의 일부 )
#include "ServerSocket.h"
#include "SocketException.h"
#include 

int main ( int argc, int argv[] )
{
  try
    {
      // Create the socket
      ServerSocket server ( 30000 );

      while ( true )
	{
	  ServerSocket new_sock;
	  server.accept ( new_sock );

	  // rest of code -
	  // read request, send reply, etc...

	}
    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
    }

  return 0;
}

연결을 받아들이기위해 accept 함수를 호출하면 된다. 이 함수는 연결 시도를 받아들이고, 연결에 대한 소켓 정보를 new_sock에 채운다. 우리는 다음 절에서 어떻게 new_sock을 사용하는지 볼 것이다.

3.4 클라이언트와 서버 - 자료 주고받기

이제 서버는 클라이언트의 연결 요청을 받아들였고, 소켓 연결을 통해 자료를 주고받을 시간이다.

C++의 고급 기능중 하나가 연산자를 오버로딩하는 - 간단히 말해서, 연산자가 특정 작업을 하도록 만드는 - 능력이다. 나는 ClientSocket과 ServerSocket 클래스의 <<와 >> 연산자를 오버로딩하여, 연산자를 사용하여 소켓에서 자료를 읽고쓸 수 있게 만들었다. 다음은 간단한 서버의 수정된 버전이다.

목록 4 : 간단한 서버 구현 ( simple_server_main.cpp의 일부 )
#include "ServerSocket.h"
#include "SocketException.h"
#include 

int main ( int argc, int argv[] )
{
  try
    {
      // Create the socket
      ServerSocket server ( 30000 );

      while ( true )
	{

	  ServerSocket new_sock;
	  server.accept ( new_sock );

	  try
	    {
	      while ( true )
		{
		  std::string data;
		  new_sock >> data;
		  new_sock << data;
		}
	    }
	  catch ( SocketException& ) {}

	}
    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\nExiting.\n";
    }

  return 0;
}

new_sock 변수는 모든 소켓 정보를 저장하고 있어서, 클라이언트와 자료를 교환하는데 사용한다. "new_sock >> data;" 줄은 "new_sock에서 자료를 읽어서 'data'라는 문자열 변수에 저장한다"는 뜻이다. 비슷하게 다음 줄은 'data'에 있는 자료를 소켓을 통해 클라이언트로 보낸다.

주의를 기울였다면 여기서 만든 것이 echo 서버임을 알 수 있을 것이다. 클라이언트가 보낸 자료는 모두 다시 클라이언트에게 그대로 되돌려진다. 우리는 자료를 보내고 서버의 응답을 출력하는 클라이언트를 작성할 수 있다.

목록 5 : 간단한 클라이언트 구현 ( simple_client_main.cpp의 일부 )
#include "ClientSocket.h"
#include "SocketException.h"
#include 
#include 

int main ( int argc, int argv[] )
{
  try
    {

      ClientSocket client_socket ( "localhost", 30000 );

      std::string reply;
      try
	{
	  client_socket << "Test message.";
	  client_socket >> reply;
	}
      catch ( SocketException& ) {}

      std::cout << "We received this response from the server:\n\"" << reply << "\"\n";;

    }
  catch ( SocketException& e )
    {
      std::cout << "Exception was caught:" << e.description() << "\n";
    }

  return 0;
}

우리는 문자열 "Test Message."를 서버로 보내고, 서버의 응답을 표준출력으로 출력한다.

4. 만든 클라이언트와 서버를 컴파일하고 테스트

우리는 ClientSocket과 ServerSocket 클래스의 기본적인 사용법을 살펴봤다. 이제 프로젝트 전체를 컴파일하여 테스트해보자.

4.1 파일 목록

예제는 아래 파일들로 구성된다.

기타:
Makefile - 이 프로젝트의 Makefile
Socket.h, Socket.cpp - 소켓 API 함수를 구현한 Socket 클래스
SocketException.h - SocketException 클래스
서버:
simple_server_main.cpp - 주파일
ServerSocket.h, ServerSocket.cpp - ServerSocket 클래스
클라이언트:
simple_client_main.cpp - 주파일
ClientSocket.h, ClientSocket.cpp - ClientSocket 클래스

4.2 컴파일과 테스트

컴파일은 간단하다. 먼저 모든 프로젝트 파일을 하위디텍로리에 저장하고, 명령행 프롬프트에 다음과 같이 입력한다.

prompt$ cd directory_you_just_created
prompt$ make

그러면 프로젝트의 모든 파일을 컴파일하여 simple_server와 simple_client 출력 파일을 만든다. 두 출력 파일을 테스트하기위해 한 명령행 프롬프트에서 서버를 실행하고, 다른 명령행 프롬프트에서 클라이언트를 실행한다.

첫번째 프롬프트:
prompt$ ./simple_server
running....



두번째 프롬프트:
prompt$ ./simple_client
We received this response from the server:
"Test message."
prompt$

클라이언트는 서버로 자료를 보내고, 응답을 읽어서 위에서처럼 표준출력으로 출력한다. 클라이언트를 원하는만큼 실행할 수 있다. 서버는 매 요청에 응답한다.

5. 결론

소켓은 프로세스간에 자료를 보내는 간단하고 효율적인 방법이다. 이 글에서 우리는 소켓 통신을 살펴보고 서버와 클라이언트 예제를 만들어봤다. 이제 당신의 프로그램에 소켓 통신을 추가할 수 있다!

신고

'Computer > CPP' 카테고리의 다른 글

멀티바이트와 유니코드  (0) 2013.07.27
BroadCasting 참고 코드  (0) 2013.07.09
Virtual Key Code  (0) 2013.07.06
키보드 이벤트 처리하기  (0) 2013.07.05
Visual Studio 2012 단축키  (0) 2013.07.05
소켓통신  (0) 2013.07.05


Posted by injunech
2013.06.07 17:09


안드로이드 작업을 하다보면 메니페스트 파일에 넣어야하는 User-permission 을 잊거나 생각이

안날경우가 있따. 그래서 일단 적어 놓는다.

</application> 밑에 추가할것 ㅋ

 

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

//위치정보 확인함
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>

//위치정보 확인함

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>

//wifi 연결을 확인함
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

//wifi 체인지를 확인함

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

//네트웍이 연결된것을 확인할수 있게함

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

//부팅완료를 확인할수있게함

<uses-permission android:name="android.permission.INTERNET"/>

// 인터넷을 사용함

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

// 외장메모리 사용

<uses-permission android:name="android.permission.RECODER_AUDIO"/>

//녹음이 가능하게 함

 

ACCESS_CHECKIN_PROPERTIES      체크인데이터베이스의_속성테이블로_액세스
ACCESS_COARSE_LOCATION         코스_로케이션_액세스_(Cell-ID/WiFi)
ACCESS_FINE_LOCATION           파인로케이션_액세스(GPS)          
ACCESS_LOCATION_EXTRA_COMMANDS 로케이션_옵션_커맨드_액세스       
ACCESS_MOCK_LOCATION           목_로케이션_프로바이더_생성_(테스트용)
ACCESS_NETWORK_STATE           네트워크_상태_접근                
ACCESS_SURFACE_FLINGER         서피스_플링거_접근                
ACCESS_WIFI_STATE              WiFi상태_접근                     
ADD_SYSTEM_SERVICE             시스템서비스_추가                 
BATTERY_STATS                  배터리_상태                       
BLUETOOTH                      블루투스                          
BLUETOOTH_ADMIN                블루투스_어드민                   
BRICK                          디바이스_실효성_지정              
BROADCAST_PACKAGE_REMOVED      제거된_패키지에_대한_notification_브로드캐스트
BROADCAST_SMS                  SMS에_대한_브로드캐스트           
BROADCAST_STICKY               인텐트_브로드캐스트               
CALL_PHONE                     통화                              
CALL_PRIVILEGED                통화(긴급전화_포함)               
CAMERA                         카메라                            
CHANGE_COMPONENT_ENABLED_STATE 컴포넌트의_실효성_변경            
CHANGE_CONFIGURATION           컨피그_변경                       
CHANGE_NETWORK_STATE           통신상태_변경                     
CHANGE_WIFI_STATE              WiFi상태_변경                     
CLEAR_APP_CACHE                어플리케이션_캐시_클리어          
CLEAR_APP_USER_DATA            어플리케이션의_유저데이터_클리어  
CONTROL_LOCATION_UPDATES       위치정보_갱신                     
DELETE_CACHE_FILES             캐시파일_제거                     
DELETE_PACKAGES                패키지_제거                       
DEVICE_POWER                   전원상태에_대한_로우레벨_접근     
DIAGNOSTIC                     진단리소스_읽고쓰기               
DISABLE_KEYGUARD               키_가드_끄기_DUMP_덤?            
EXPAND_STATUS_BAR              상태표시줄_확장                   
FACTORY_TEST                   팩토리_테스트                     
FLASHLIGHT                     플래시라이트                      
FORCE_BACK                     포스백                            
GET_ACCOUNTS                   어카운트_획득                     
GET_PACKAGE_SIZE               패키지_획득                       
GET_TASKS                      태스크_획득                       
HARDWARE_TEST                  하드웨어테스트                    
INJECT_EVENTS                  유저이벤트_키/트랙볼              
INSTALL_PACKAGES               패키지_인스톨                     
INTERNAL_SYSTEM_WINDOW         내부_시스템윈도_활용              
INTERNET                       인터넷                            
MANAGE_APP_TOKENS              어플리케이션_토큰관리             
MASTER_CLEAR                   마스터_클리어                     
MODIFY_AUDIO_SETTINGS          오디오설정_편집                   
MODIFY_PHONE_STATE             전화상태_편집                     
MOUNT_UNMOUNT_FILESYSTEMS      파일시스템_편집                   
PERSISTENT_ACTIVITY            액티비티_지속                     
PROCESS_OUTGOING_CALLS         전화_발신처리_접근                
READ_CALENDAR                  캘린더_읽어오기                   
READ_CONTACTS                  주소록_읽어오기                   
READ_FRAME_BUFFER              프레임버퍼_읽어오기               
READ_INPUT_STATE               입력상태_읽어오기                 
READ_LOGS                      로그_읽어오기                     
READ_OWNER_DATA                owner_data읽어오기                
READ_PHONE_STATE               통화상태_읽어오기_READ_SMS_SMS읽어오기
READ_SYNC_SETTINGS             동기설정_읽어오기                 
READ_SYNC_STATS                동기상태_읽어오기                 
REBOOT                         reboot                            
RECEIVE_BOOT_COMPLETED         boot완료                          
RECEIVE_MMS                    MMS수신                           
RECEIVE_SMS                    SMS수신                           
RECEIVE_WAP_PUSH               WAP수신                           
RECORD_AUDIO                   오디오_수신                       
REORDER_TASKS                  태스크_Z오더                      
RESTART_PACKAGES               패키지_리스타트                   
SEND_SMS                       SMS송신                           
SET_ACTIVITY_WATCHER           액티비티_왓쳐지정                 
SET_ALWAYS_FINISH              액티비티_전체_종료                
SET_ANIMATION_SCALE            스케일_애니메이션_지정            
SET_DEBUG_APP                  디버그어플리케이션_지정           
SET_ORIENTATION                스크린_로테이션지정               
SET_PREFERRED_APPLICATIONS     자주_사용하는_어플리케이션_지정   
SET_PROCESS_FOREGROUND         포어그라운드_처리지정             
SET_PROCESS_LIMIT              제한처리_지정                     
SET_TIME_ZONE                  타임존_지정                       
SET_WALLPAPER                  배경화면_지정                     
SET_WALLPAPER_HINTS            배경화면_힌트_지정                
SIGNAL_PERSISTENT_PROCESSES    지속처리_시그널_지정              
STATUS_BAR                     상태표시줄_지정                   
SUBSCRIBED_FEEDS_READ          서브스트립드_피즈_읽어오기        
SUBSCRIBED_FEEDS_WRITE         서브스트립드_피즈_쓰기            
SYSTEM_ALERT_WINDOW            알림_윈도우                       
VIBRATE                        진동                              
WAKE_LOCK                      알람                              
WRITE_APN_SETTINGS             APN설정_쓰기                      
WRITE_CALENDAR                 캘린더_쓰기                       
WRITE_CONTACTS                 주소록_쓰기                       
WRITE_GSERVICES                G서비스_쓰기                      
WRITE_OWNER_DATA               owner_data쓰기                    
WRITE_SETTINGS                 설정_쓰기
WRITE_SMS                      SMS쓰기 
WRITE_SYNC_SETTINGS            동기설정_쓰기

신고


Posted by injunech