Network Socket Programming
Contents Network 접속 MFC 소켓 클래스 CAsyncSocket CSocket 범용 소켓 만들기
Network 접속 과정
Contents Network 접속 MFC 소켓 클래스 CAsyncSocket CSocket 범용 소켓 만들기
CAsyncSocket Socket 인터페이스에 필요한 기능들을 클래스로 구현 Notification function 함수들을 application에 따라 overriding하여 구현 상대가 MFC 기반의 프로그램이 아니더라도 통신 가능 Nonblocking mode로 동작 어떤 작업 수행 시간이 오래 걸리더라도 그 결과를 기다리지 않고 함수가 바로 리턴되어 다음 작업을 수행하는 동작 방식
CAsyncSocket Notification function(On~()함수) CAsyncSocket, CSocket class는 windows program의 기본 구동 방식인 사건 발생에 따른 message 전달법 대신, message를 notification 함수로 변환하고, 프로그래머는 이 notification 함수들이 어떻게 동작할지를 application에 따라 적절히 overriding하여 구현
소켓을 닫음. CAsyncSocket의 소멸자 함수에서 호출 소켓 생성과 종료 Create 소켓 오브텍트를 생성합니다. Close 소켓을 닫음. CAsyncSocket의 소멸자 함수에서 호출 OnClose 소켓에 연결된 소켓이 닫힐 때 호출
CAsyncSocket 서버 소켓의 기능(클라이언트의 접속을 받아들이기 위해) Listen Accept OnAccept 클라이언트의 접속 요청을 감지하기 시작 Accept 클라이언트의 접속 요청을 받아들여, 새로운 소켓을 한 클라이언트 소켓을 연결시켜 줌 OnAccept Listen 함수를 호출하고 클라이언트의 접속 요청을 기다리고 있는 서버 소켓에 클라이언트로부터 접속 요청이 있을 때 호출. 일반적으로 접속 요청이 있을 때 접속을 받아들이기 위해 Accept 함수를 호출하므로, 이 함수를 오버라이딩한 후 그 안에서 Accept 함수를 호출
서버에 접속 요청을 함. 이 함수의 인자로 접속하고자 CAsyncSocket 클라이언트 소켓의 기능(서버에 접속하기 위해) Connect 서버에 접속 요청을 함. 이 함수의 인자로 접속하고자 하는 서버의 IP 주소와 포트 번호를 넘겨 줌. OnConnect 함수 호출에 의한 접속 요구가 받아들여져, 클라이언트가 서버에 연결됐을 때 호출
CAsyncSocket 데이터를 주고 받는 기능 Send 데이터를 보낸다. OnSend Receive OnReceive 데이터를 전송할 수 있을 때 호출. Receive 데이터를 받는다. 수신된 데이터는 윈도우 운영체제가 보관하고 있는데, Receive 함수는 윈도우 운영체제가 보관하고 있는 수신데이터를 가져오는 기능을 수행. OnReceive 새로운 데이터가 수신되었을 때 호출. 일반적으로 새로운 데이터가 수신되었을 때, 이 데이터를 접수하기 위해 Receive 함수를 호출하므로, 이 함수를 오버라이딩하여 Receive 함수를 호출.
동기와 비동기 (1/3) 비동기 함수 오류 메시지 동기와 비동기 함수의 특성 동기(Synchronous = Blocking) 함수 함수가 작업을 완료한 후 리턴한다. 비동기(Asynchronous = Nonblocking) 함수 함수가 작업을 완료하지 않더라도 곧바로 리턴한다.
동기와 비동기 (2/3) 동기 함수의 문제점 void CMyServerView::OnLButtonDblClk(UINT nFlags, CPoint point) { .... char buf[256]; socket.Recv(buf, 256); // 여기서 함수가 리턴하지 않으면 메시지 루프가 정지한다. CView::OnLButtonDblClk(nFlags, point); } 해결: 스레드 사용 또는 비동기 함수 사용
동기와 비동기 (3/3) CSocket 클래스 중요 함수의 경우 동기적으로 동작하되, 함수 호출시 메시지 루프가 정지하는 일이 없도록 내부적으로 윈도우 메시지를 처리 예) Accept(), Receive(), Send(), Connect(), ReceiveFrom(), SendTo() 동기 함수를 호출해야 할 시점을 가상 함수 호출로 알려줌. CAsyncSocket 클래스가 제공하는 On~ 함수를 정의해두면 자동으로 해당 On~ 함수가 호출. 예) 클라이언트가 접속하면 가상 함수 OnAccept()가 자동 으로 호출됨. 이때 Accept()를 호출.
CSocket CAsyncSocket에서 상속 받아 정의된 클래스 CArchive 클래스를 이용하여 데이터를 주고 받는 기능(Send/Receive 함수를 이용하여 데이터를 주고 받는 것도 가능) 구현이 간단 상대방도 MFC로 짜여진 프로그램을 사용해야 함 Blocking mode로 동작 어떤 함수를 호출하고 나서, 그 함수의 작업의 끝날 때까지 다음 동작을 수행하지 못하고 기다려야 함
CSocket(CArchive 클래스를 이용할 때) CSocketFile class를 매개로 CArchive class와 연결 CArchive는 Serialize() 함수를 이용, 클래스 데이터를 파일에 저장하는 데 사용
CArchive class CDocument와 CFile, CMemFile, CSocketFile을 연결
인터넷 프로그램밍에서 주의점 Byte 순서 인터넷 상에 서로 다른 기종 간에 통신을 할 때는 CPU마다 바이트 순서가 다르다는 점에 주의 EX) double형과 int형이 있을 때 Little-Endian 방식: 하위 바이트부터 왼쪽에 저장 Big-Endian 방식: 상위 바이트부터 왼쪽에 저장 다른 기종 간 통신에서 CAsyncSocket을 이용 때 byte 순서 고려해야 함 CSocket을 이용 시 byte 순서 처리를 신경쓰지 않아도 됨
Contents Network 접속 MFC 소켓 클래스 CAsyncSocket CSocket 범용 소켓 만들기
범용 소켓 클래스(CSocket에서 상속) 서버 소켓 클래스(CServerSocket) 서버에서 클라이언트의 접속 요청을 받아들여 클라이언트와 서버의 데이터 소켓을 연결시켜 주는 역할 데이터 소켓 클래스(CDataSocket) 데이터를 주고받는데 관련된 기능을 수행 데이터 구조 클래스(CData) 전송될 데이터 포맷을 정의( 통신 프로그램이 서로 간에 데이터를 주고받으려면 양쪽에서 데이터 포맷이 같아야 함)
서버 소켓(CServerSocket) 클래스 접속 요청이 들어오면 받아들이는 일은 main window에서 수행 (범용성 고려) 접속요청이 들어오면 CServerSocket 의 OnAccept가 자동으로 호출 OnAccept -> UM_ACCEPT #define UM_ACCEPT (WM_USER) CServerSocket 클래스
서버 소켓(CServerSocket) 클래스 #define UM_ACCEPT (WM_USER) class CServerSocket : public CSocket { //… public: void Init(CWnd *pWnd, int nPortNum); CWnd *m_pWnd; // ClassWizard generated virtual function overrides virtual void OnAccept(int nErrorCode);
서버 소켓(CServerSocket) 클래스 void CServerSocket::Init(CWnd *pWnd, int nPortNum) { m_pWnd = pWnd; Create(nPortNum); //소켓 생성 Listen(); //접속 요청 감지 } void CServerSocket::OnAccept(int nErrorCode) m_pWnd->SendMessage(UM_ACCEPT); CSocket::OnAccept(nErrorCode);
데이터 소켓(CDataSocket) 새로운 데이터가 수신되었을 때, main window에서 수행(범용성 고려) 새로운 데이터가 수신되면, CDataSocket의 OnReceive가 자동으로 호출 OnReceive -> UM_DATARECEIVE #define UM_DATARECEIVE (WM_USER+1) void CDataSocket::OnReceive(int nErrorCode){ m_pWnd->SendMessage(UM_DATARECEIVE); CSocket::OnReceive(nErrorCode); }
데이터 소켓(CDataSocket) CDataSocket 클래스는 CArchive, CSocketFile 클래스와 연계되어 class CDataSocket : public CSocket {public: CWnd *m_pWnd; CSocketFile *m_pFile; CArchive *m_pArchiveIn; CArchive *m_pArchiveOut; public: CDataSocket(); virtual ~CDataSocket(); void operator << (CData &data); void operator >> (CData &data); void Flush(); void Send(CData *pData); void Receive(CData *pData); void Init(CWnd *pWnd); public: virtual void OnReceive(int ErrorCode); virtual void OnClose(int nErrorCode); };
데이터 소켓(CDataSocket) 데이터 수신 CArchieve 클래스를 이용하여 Serialize 함수를 호출 데이터소켓 초기화 void CDataSocket::Init(CWnd *pWnd) { m_pWnd = pWnd; //pWnd는 메시지를 받을 윈도우 m_pFile = new CSocketFile(this); m_pArchiveIn = new CArchive(m_pFile, CArchive::load); m_pArchiveOut = new CArchive(m_pFile, CArchive::store); } 데이터 수신 CArchieve 클래스를 이용하여 Serialize 함수를 호출 void CDataSocket::Receive(CData * pData) { if(m_pArchiveIn != NULL) pData->Serialize(*m_pArchiveIn); } void CDataSocket::operator >>(CData & data) { Receive (&data); }
데이터 소켓(CDataSocket) 데이터 송신 void CDataSocket::Send(CData * pData) Serialize 함수를 이용 Send 함수:소켓이 관리하는 송신용 버퍼에 저장 Flush()사용 void CDataSocket::Send(CData * pData) { if(m_pArchiveOut != NULL) pData->Serialize(*m_pArchiveOut); } void CDataSocket::Flush(){ if(m_pArchiveOut != NULL) m_pArchiveOut->Flush(); } void CDataSocket::operator <<(CData & data){ Send (&data); Flush(); }
데이터 소켓(CDataSocket) 마무리 작업 연결된 접속이 끊어지면 OnClose 함수가 호출 동적으로 할당했던 CArchive 클래스와 CSocketFile 오브젝트를 삭제 void CDataSocket::OnClose(int nErrorCode) { if(m_pFile != NULL) { delete m_pFile; m_pFile = NULL; } if(m_pArchiveIn != NULL) { m_pArchiveIn->Abort(); delete m_pArchiveIn; m_pArchiveIn = NULL; } if(m_pArchiveOut != NULL) { m_pArchiveOut->Abort(); delete m_pArchiveOut; m_pArchiveOut = NULL; } CSocket::OnClose(nErrorCode); }
데이터 구조 클래스(CData) 전송될 데이터는 CData 클래스에 캡슐화 문자열 하나를 전송 시 class CData : public CObject { public: CData(); virtual ~CData(); virtual void Serialize(CArchive &ar); CString m_strData; }; void CData::Serialize(CArchive &ar) { if(ar.IsStoring()) // 네트워크로 송신 ar << m_strData; } else // 네트워크에서 수신 ar >> m_strData;
Note & Q/A 강의 일정 프로젝트 제안서 제출 프로젝트 최종 보고서 제출 및 최종 결과물 시연 평가 1주: OOP Concept, C++ 개요 2주: SOCKET API, TCP/IP, MFC Programming Overview 3주: Network 접속, MFC 소켓 클래스, 범용 소켓 소개 4주: 예제 서버, 클라이언트 채팅 소스 분석 및 관련 API Overview 프로젝트 제안서 제출 중간고사기간 이후 공고 프로젝트 최종 보고서 제출 및 최종 결과물 시연 평가 날짜: 2010년 12월 초/중 오후 6시경 컴퓨터실(예정, 차후공고) 29
Note & Q/A 강의 일정 프로젝트 제안서 제출 ( 2인 1조 ) 프로젝트 최종 보고서 제출 및 최종 결과물 시연 평가 10월 27일 11월 3일 프로젝트 제안서 제출 ( 2인 1조 ) 중간고사기간 (10월 17일 ~ 1주간)이후 11월 10일까지(실습 3 진행한 다음주까지) 프로젝트 최종 보고서 제출 및 최종 결과물 시연 평가 날짜: 2010년 12월 초/중 오후 6시경 컴퓨터실(예정, 차후공고)