2013.07.05 11:35


'MFC를 이용한 소켓 프로그래밍 시작하기 (서버편)'을 통해 간단한 소켓 프로그램을 작성해 보았습니다.

해당 프로그램의 참고자료로 프로그램 코드 링크를 올려드립니다.



HelloServer.vol1.egg


HelloServer.vol2.egg




신고


Posted by injunech
2013.07.05 11:29


어째뜬...

MFC를 이용한 채팅 프로그램 시작하기(클라이언트편)를 시작해 보도록 하겠습니다.

그럼 서버 프로그램에 이어서 클라이언트 프로그램을 만들어 서버와 클라이언트간의 동작을 확인해 보도록 하겠습니다.

클라이언트 프로그램은 다음의 작업을 수행하게 됩니다.

서버에 연결 요청을 하고 연결이 되면 서버와 데이터를 송수신합니다.


그럼 클라이언트 프로그램을 만들어 보도록 하겠습니다.

채팅 클라이언트 프로그램도 서버 프로그램과 마찬가지로 윈도우 소켓을 지원받는 대화상자 기반으로 프로젝트를 생성하겠습니다. (프로젝트명은 ChatClient로 하겠습니다.)

서버 프로그램과 마찬가지로 GUI를 구성하도록 하겠습니다.

클라이언트는 두개의 대화상자로 구성됩니다.

서버 프로그램과 비슷한 구조의 ChatClient와 서버에 접속을 요청하기 위한 IP주소를 입력받아 서버에 접속을 요청하는 대화상자로 구성됩니다.

아래 그림과 같이 채팅 클라이언트 GUI를 구성합니다.

ChatClient 대화상자의 구성이 완료되면 Connect Setting 버튼을 클릭하면 서버에 연결을 위한 IP주소를 세팅하는 대화상자를 추가하여 아래와 같이 GUI를 구성합니다.


서버와 연결을 위한 대화 상자 템플릿을 대화 상자 클래스(CConnectDlg)로 구현합니다.

서버에 연결하기 위해 서버의 IP주소를 입력받는데 입력받은 값을 저장하기 위한 변수를 선언합니다.

Connect 버튼을 누르면 호출되는 이벤트 처리기를 추가하여 서버와의 연결을 위한 서버 IP 주소를 저장합니다. 그리고 추가로 서버 IP주소를 매번 입력하지 않도록 OnInitDialog() 함수를 CConnectDlg 클래스에 재정의하여 초기화 합니다.

다음으로 ChatClient 대화 상자에서 Connect Setting 버튼을 누르면 Connect 대화상자를 호출하고 IP주소를 입력후 Connect 버튼을 눌렀을 때 서버에 연결을 요청하는 Connect함수를 호출하도록 OnBnClickConnect()와 OnBnClickedSend() 함수를 아래와 같이 재정의 합니다.

다음으로 서버와의 통신을 위해 연결 요청을 하고 데이터를 송수신하는 CSocket의 파생클래스로클라이언트 소켓을 정의 하도록 하겠습니다.

어플리케이션 클래스에 서버와의 접속을 위해서 접속을 요청하는 클라이언트 소켓을 위한 멤버 변수를 선언합니다.

생성자에 추가한 멤버 변수를 초기화 합니다.

다음으로 서버와 마찬가지로 접속과 데이터 송수신에 필요한 함수를 만들도록 하겠습니다.
필요한 함수는 다음과 같습니다.

void Connect()

서버 소켓으로 접속요청

void CleanUp()

소켓을 닫고 메모리 해제

void SendData(CString strData)

데이터 송신

void ReceiveData()

데이터 수신

void CloseChild()

자소켓 닫고 메모리 해제

함수는 아래와 같이 내용을 구현합니다.

다음으로 서버 프로그램과 마찬가지로 데이터 송수신을 위한 가상함수를 재정의 해야 합니다.
CClientSocket 클래스의 OnReceive()함수와 OnClose() 함수를 재정의 합니다.

수고하셨습니다. 모든 작업이 끝났습니다.^^
이제 프로그램을 실행시켜 서버와 클라이언트간에 접속과 데이터 송수신을 확인해 보도록 하겠습니다.
서버 프로그램이 대기 상태에 있어야 클라이언트가 접속할 수 있으니 서버 프로그램을 먼저 실행시킵니다.

서버 프로그램은 실행과 동시에 클라이언트의 요청을 기다리게 됩니다.
서버 프로그램이 정상적으로 동작하고 있으니 클라이언트 프로그램을 실행합니다.

클라이언트 프로그램을 실행하고 Connect Setting 버튼을 눌러 연결을 요청할 서버의 IP주소를 입력(루프백 주소를 사용하도록 하겠습니다.)하고 Connect 버튼을 누르면 클라이언트가 서버와 연결을 시도하게 됩니다.
서버와의 연결이 성공적으로 이루어 지면 아래와 같이 서버에 Accept Success 메시지가 나타납니다.

정상적으로 서버와 클라이언트가 연결되었습니다.
이제 서버와 클라이언트에서 메시지를 입력하고 Send 버튼을 눌러 데이터의 송수신이 이루어 지는지 확인합니다.

모든 것이 정상적으로 동작합니다.

확인하셨죠?^^

오랜시간 수고하셨습니다.
이상으로 소켓을 이용한 간단한 채팅 서버와 클라이언트를 구현해 보았습니다.




신고


Posted by injunech
2013.07.05 11:28


그럼 MFC를 이용한 채팅 프로그래밍 시작하기(서버편) - 1에 이어서 계속 진행 하도록 하겠습니다.

앞서 말씀드린대로 남은 작업은 데이터 송수신을 위한 가상함수 재정의 부분과 대화상자 클래스에서 서버 소켓을 초기화 하고 데이터를 전송하는 부분입니다.

그럼 먼저 데이터 송수신을 위한 가상함수를 재정의 하도록 하겠습니다.
먼저 서버 소켓에 재정의 해야 하는 OnAccept() 함수를 보도록 하겠습니다.
서버 소켓은 클라이언트의 요청을 기다리는 대기 상태입니다. 이 때 클라이언트는 서버 IP주소와 포트번호로 서버에 접속 요청을 위해 Connect()함수를 호출합니다. OnAccept() 함수는 사용자의 이벤트에 의해서 호출되지 않고 클라이언트 소켓의 Connect() 함수를 통해서 호출됩니다. 따라서 이 메소드는 서버 소켓에서 재정의 되어야 하고 재정의된 함수 내부에는 연결을 받아들여 이를 자소켓에 연결해 두어야 합니다.

다음은 자소켓에 재정의 해야 하는 OnReceive() 함수 입니다.
클라이언트가 데이터를 송신하기 위해 Send() 함수를 호출하게 되면 자소켓의 OnReceive() 함수가 호출됩니다. 따라서 데이터를 수신하기 위한 ReceiveData()함수를 호출하도록 함수를 재정의 해야 합니다.

마지막으로 자소켓에 재정의 해야 하는 OnClose() 함수 입니다.
클라이언트 소켓이 닫히게 되면 OnClose() 함수가 호출됩니다. 따라서 클라이언트와 연결된 자소켓을 닫고 메모리를 해제 하도록 CloseChild()함수를 호출하도록 재정의 해야 합니다.

이제 마지막 작업만을 남겨두고 있습니다.


대화상자 클래스에서 서버 소켓을 초기화 하고 데이터를 전송하는 작업입니다.

CChatServerDlg 클래스의 OnInitDialog() 함수에서 서버 소켓을 초기화 하는 InitServer() 함수를 호출하여 서버 소켓이 클라이언트의 연결 요청을 대기하도록 합니다.

다음으로 Send 버튼을 누를 때 호출되는 메시지 핸들러를 추가한 후 클라이언트로 데이터를 송신하는 SendData() 함수를 호출 하도록 합니다.

이것으로 채팅 서버 프로그램을 완성하였습니다.
이제 클라이언트 프로그램을 만들어 확인하는 일만 남았습니다.

그럼 다음시간에 클라이언트 프로그램을 만들어 간단한 채팅 테스트를 진행하도록 하겠습니다.

감사합니다.


신고


Posted by injunech
2013.07.05 11:28


이번시간에는 소켓을 이용해 간단한 채팅 프로그램을 만들어 보도록 하겠습니다.

먼저 채팅 프로그램의 서버와 클라이언트의 통신 순서를 그림을 통해 알아보도록 하겠습니다.

아래 그림을 봐주세요.

그림에서 보는바와 같이 서버와 클라이언트는 통신을 하게 됩니다.

순서를 간단히 살펴보면 다음과 같습니다.

1. 서버 프로그램에서 서버 소켓이 클라이언트의 접속을 기다립니다.
서버측에서 클라이언트의 접속을 기다리기 위해 소켓을 생성후 Listen() 함수를 실행해 클라이언트가 접속할 때까지 대기 하도록 합니다. 서버 소켓을 생성할 때 다른 응용프로그램과의 구분을 위해 특정 포트를 지정해야 합니다.(여기서는 1470번 포트를 사용하도록 하겠습니다.)

2. 클라이언트 프로그램에서 서버 소켓에 접속을 요청합니다.
서버가 Listen()함수를 실행하여 대기 상태에 있을 때 클라이언트 프로그램에서 Connect() 함수를 호출하면 접속할 수 있습니다.

3. 데이터 송수신
클라이언트가 서버와 접속에 성공하면 서버와 클라이언트는 각각 소켓 객체를 갖게 됩니다. 각각의 소켓은 데이터 송수신을 위해 사용됩니다.

그럼 위와 같은 순서로 동작하게될 채팅 프로그램을 작성해 보도록 하겠습니다.


먼저 서버 프로그램을 만들어 보겠습니다.

채팅 서버는 클라이언트의 요청을 받아줄 소켓과 실제 클라이언트와 통신을 담당할 소켓(자소켓)이 필요합니다.
CSocket의 파생 클래스에 OnAccept() 함수를 재정의 해야 합니다. 클라이언트의 연결 요청이 발생하였을 때 클라이언트와 통신을 담당할 소켓을 생성해 주는 코드를 기술하게 됩니다.
클라이언트와 통신을 담당할 자소켓 클래스 역시 CSocket의 파생 클래스여야 하고 OnReceive() 함수를 재정의 해야 합니다. 이 함수에는 클라이언트에서 데이터를 송신하면 이를 수신하는 코드를 기술하게 됩니다.

먼저 윈도우 소켓을 지원받는 프로젝트 생성을 위해 대화상자 기반으로 MFC 프로젝트를 생성합니다. (프로젝트명은 ChatServer로 하겠습니다.)

아래 그림과 같이 채팅 서버 GUI를 구성합니다.

다음으로 클라이언트의 연결 요청을 기다리며 대기하는 서버 소켓 클래스와 클라이언트와 통신을 담당하게될 자소켓을 CSocket의 파생 클래스로 정의합니다.

아래 그램과 같이 MFC 클래스 마법사를 이용해서 CServerSocket과 CChildSocket 클래스를 CSocket의 파생 클래스로 구현합니다.

다음으로 CChatServerApp 클래스에 클라이언트의 접속을 위해 대기하는 서버 소켓과 통신을 위한 자소켓을 위한 멤버 변수 아래 그림과 같이 선언합니다.

아래와 같이 생성자에 추가한 멤버 변수를 초기화 합니다.

서버 소켓과 자소켓 객체가 어플리케이션 클래스에 생성이 되었습니다.

이제 접속과 데이터 송수신에 필요한 함수를 만들도록 하겠습니다.

필요한 함수는 다음과 같습니다.

void InitServer()

서버 소켓 초기화

void CleanUp()

소켓을 닫고 메모리 해제

void SendData(CString strData)

데이터 송신

void ReceiveData()

데이터 수신

void Accept()

접속 허용

void CloseChild()

자소켓 닫고 메모리 해제

각각의 함수는 아래와 같이 내용을 구현합니다.

함수 구현까지 마쳤습니다.

이제 남은 작업은 데이터 송수신을 위한 가상함수 재정의 부분과 대화상자 클래스에서 서버 소켓을 초기화 하고 데이터를 전송하는 부분입니다.

남은 작업은 다음시간에 계속 이어가도록 하겠습니다.

감사합니다.

이글은 초보 개발자를 위한 비주얼 C++ MFC 입문과 정석용의 TCP/IP 소켓 프로그래밍 책을 참고하여 작성하였습니다.


신고


Posted by injunech
2013.07.05 11:26


서버 프로그램이 준비되었으니 클라이언트 프로그램을 구현하도록 하겠습니다.
클라이언트 프로그램은 ‘연결’ 버튼을 클릭하면 서버 프로그램에 연결하여 서버로부터 전송된 문자열을 화면에 출력하게 됩니다.

MFC로 클라이언트 프로그램을 구현할 때 다음의 순서로 진행하도록 하겠습니다.
1 응용 프로그램 마법사로 프로젝트를 만듭니다.
2 GUI를 설계하고, 각각의 컨트롤에 대해 멤버 함수와 멤버 변수를 추가하고 코딩합니다.
3 서버 프로그램과 연결해서 기능을 검증합니다.

클라이언트 프로그램을 구현하기 위해서 프로젝트를 생성하겠습니다.
대화 상자 기반으로 고급기능에서 ‘Window 소켓’을 체크하여 프로젝트를 생성합니다.
프로젝트 이름은 HelloClient로 하겠습니다.
서버 프로그램과 마찬가지 순서로 프로젝트를 생성합니다.

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

준비가 되었으면 HelloClientDlg.h를 작성하도록 하겠습니다.

다음으로 CHelloClientDlg 클래스에서 연결 버튼을 누르면 수행하는 OnBnClickedButtonConnect 멤버 함수의 내용을 구현하도록 하겠습니다.

프로그램 구현을 모두 끝냈습니다.

이제 모든 준비가 끝났습니다.

준비된 서버 프로그램과 클라이언트 프로그램을 실행하여 잘 동작하는지 확인해 보도록 하겠습니다.

먼저 서버 프로그램을 실행합니다.

서버 프로그램이 실행되면 시작버튼을 눌러 클라이언트의 요청을 기다립니다.

소켓 생성을 성공하면 위와 같이 Socket Create Success 메시지를 출력하고 서버는 클라이언트의 연결 요청을 기다리게 됩니다.

소켓 생성을 실패한 경우 아래와 같이 메시지를 출력하게 됩니다.

서버 준비가 완료되면 클라이언트 프로그램을 실행합니다.

클라이언트 프로그램이 실행되면 연결버튼을 눌러 서버로부터 문자열을 바당 출력하는지 확인합니다.

Hello World를 확인하셨나요?

네트워크 프로그래밍의 세계에 오신걸 환영합니다^^

그럼 다음시간에 만날 수 있길 바라며 이만 줄입니다.



신고


Posted by injunech
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
2013.05.31 11:28


FROM drawable TO bitmap :

Drawable d = ImagesArrayList.get(0);  
Bitmap bitmap = ((BitmapDrawable)d).getBitmap();

 

FROM resource id of drawable TO bitmap:

Bitmap b1 = BitmapFactory.decodeResource(context.getResources(),R.drawable.icon_resource);

 

Reference:

http://stackoverflow.com/questions/3035692/how-to-convert-a-drawable-to-a-bitmap

신고

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

Android Bitmap Utility(resize, crop) 클래스..  (0) 2013.07.25
android uses-permission  (0) 2013.06.07
Convert drawable to bitmap  (0) 2013.05.31
안드로이드 디렉토리 생성 및 파일 I/O  (0) 2013.05.29
Android WebView 예제  (0) 2013.05.27
Bitmap Merge  (0) 2013.05.26


Posted by injunech
2013.05.29 10:51


보안상의 이유로 응용 프로그램은 허가받은 위치에만 파일 생성 가능.. 이 제한을 일일이 준수하기 어려우므로 
보안이 적용된 파일 관리 메서드를 별도로 제공..

FileOutputStream openFileOutput(String name, int mode)
FileInputStream openFileInput(String name)

 MODE_PRIVATE 혼자만 사용하는 배타적인 모드로 파일 생성 .. (디폴트)
 MODE_APPEND 파일이 이미 존재할 경우 덮어쓰기 모드로 열지 않고 추가 모드로 연다. ( 기존 내용에 추가 )
 MODE_WORLD_READABLE 다른 응용 프로그램이 읽을 수 있도록 허용
 MODE_WORLD_WRITEABLE 다른 응용 프로그램이 쓸 수 있도록 허용

// 파일 저장

FileOutputStreamfos = openFileOutput("text.txt", Context.MODE_WORLD_READABLE);


String str = mEdit.getText().toString();


                                

fos.write(str.getBytes());


fos.close();


mEdit.setText("Write Success");



파일 경로 : /data/data/패키지명/files/ 디렉토리 .. 


- 직접적으로 확인은 불가하다 .. ( Root Explorer(App) 이용해서 확인 .. 루트 권한 필요 )


// 파일 읽기

FileInputStream fis = openFileInput("text.txt");


byte[] data = new byte[fis.available()];


while(fis.read(data) != -1){}


fis.close();


mEdit.setText(new String(data));



*SD Card 이용 I/O

기본 java I/O 와 동일 


// 파일 저장

String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();


File dir = new File(sdPath,  "testing");


dir.mkdir();


File file = new File(dir, "file.txt");


FileOutputStream fos = new FileOutputStream(file);


String str = mEdit.getText().toString();


fos.write(str.getBytes());


fos.close();


mEdit.setText("write success");






// 파일 읽기

String sdPath = Environment.getExternalStorageDirectory().getAbsolutePath();

FileInputStream fis = new FileInputStream(sdPath + "/testing/file.txt");

byte[] data = new byte[fis.available()];

while(fis.read(data)!=-1){}

fis.close();

mEdit.setText(new String(data));



※ SD Card 디렉토리 생성시 에러상황 .. 해결법 ..


위와 같이 코드를 이용해 디렉토리 생성시 에러가 발생 할 경우 아래의 내용을 추가해야한다.. 


AndroidManifest.xml 


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


SD card의 파일을 엑세스 하려면 Manifest에 퍼미션을 지정해야한다.


※ SD Card 관련 정보 보기


1. Environment.getExternalStorageState()

SD card 현재 상태 .. mounted : 읽고 쓰기 가능한 상태 .. unmounted : 불가능 상태

2. Environment.getExternalStorageDirectory().getAbsolutePath()

SD card 가 마운트된 경로 .. 

3. Environment.getRootDirectory().getAbsolutePath()

4. Environment.getDataDirectory().getAbsolutePath()

5. Environment.getDownloadCacheDirectory().getAbsolutePath() 

신고


Posted by injunech