티스토리 뷰
기존 프로젝트에 소켓 추가하기
MFC에서 소켓을 지정했을경우
첫번째
APP의 InitInstance에서 소켓 라이브러리 초기화
BOOL CMyApp::InitInstance() // 다이얼로그 방식이면 ~Dlg::OnInitDialog() 에서 추가
{
if (!AfxSocketInit())
{
AfxMessageBox("Windows 소켓 초기화에 실패하였습니다.");
return FALSE;
}
....
}
두번째
stdafx.h에 소켓헤더 추가
#include <afxsock.h> // MFC socket extensions
아래 윈속 프로그래밍 순서대로 하면 된다.
=================================================
윈속 프로그래밍
A. 서버 구현
프로젝트 만들때 옵션에서 Windows 소켓 을 선택해주고 아래처럼 MFC 클래스 2개 추가한다.
1. CListenSocket 추가 및 함수와 변수 추가
class CListenSocket : public CAsyncSocket
{
......
public:
CPtrList m_ptrClientSocketList; // 연결된 Client Socket List 저장하는 변수
virtual void OnAccept(int nErrorCode); // Client에서 연결 시도 치 자동 호출되는 함수
}
2. CClientSocket 추가 및 함수와 변수 추가
class CClientSocket : public CSocket
{
........
public:
void SetListenSocket(CAsyncSocket* pSocket);
CAsyncSocket* m_pListenSocket; // 소켓 리스트에 접근하기 위해서 Listen 소켓의 주소를 저장 해둔다.
}
3. 가상함수 OnAccept() 추가 및 코드 추가
#include "ClientSocket.h"
void CListenSocket::OnAccept(int nErrorCode) // 새로운 클라이언트의 연결을 받을때 자동 호출됨.
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CClientSocket* pClient = new CClientSocket;
if(Accept(*pClient))
{
pClient->SetListenSocket(this);
m_ptrClientSocketList.AddTail(pClient);
}
else
{
delete pClient;
AfxMessageBox("Accept 실패.");
}
CAsyncSocket::OnAccept(nErrorCode);
}
4. CloseClientSocket() 추가 및 코드 추가
void CListenSocket::CloseClientSocket(CClientSocket* pClient) // 프로그램 종료 전에 연결된 모든 클라이언트 소켓을 모두 닫고 할당된 메모리를 해제하고 네트워크 연결을 모두 종료한다.
{
POSITION pos;
pos = m_ptrClientSocketList.Find(pClient);
if(pos != NULL)
{
if(pClient != NULL)
{
pClient->ShutDown();
pClient->Close();
}
m_ptrClientSocketList.RemoveAt(pos);
delete pClient;
}
}
5. SendOrderResultDataAll() 추가 및 코드 추가
void CListenSocket::SendOrderResultDataAll(char* pszBuffer) // 한 클라이언트에서 받은 메시지를 연결된 모든 클라이언트에게 전송한다.
{
POSITION pos;
pos = m_ptrClientSocketList.GetHeadPosition();
CClientSocket* pClient = NULL;
while(pos != NULL)
{
pClient = (CClientSocket*)m_ptrClientSocketList.GetNext(pos);
if(pClient != NULL)
pClient->Send(pszBuffer, lstrlen(pszBuffer));
}
}
6. SetListenSocket() 코드 추가
#include "ListenSocket.h"
void CClientSocket::SetListenSocket(CAsyncSocket* pSocket)
{
m_pListenSocket = pSocket; // 소켓 리스트에 접근하기 위해서 Listen 소켓의 주소를 저장해둔다.
}
7. CClientSocket 클래스에 가상함수 OnClose() 추가 및 코드 추가
void CClientSocket::OnClose(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CSocket::OnClose(nErrorCode);
CListenSocket* pServerSocket = (CListenSocket*)m_pListenSocket;
pServerSocket->CloseClientSocket(this);
}
8. CClientSocket 클래스에 가상함수 OnReceive() 추가 및 코드 추가
#define BUFFER_SIZE 512 // 코드추가
// 클라이언트로부터 메시를 받으면 자동 호출된다.
void CClientSocket::OnReceive(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
CString tmp = "", strIPAddress = "";
UINT uPortNumber = 0;
char szBuffer[BUFFER_SIZE];
::ZeroMemory(szBuffer, BUFFER_SIZE);
GetPeerName(strIPAddress, uPortNumber);
if(Receive(szBuffer, BUFFER_SIZE) > 0)
{
//CServerDemoDlg* pMain = (CServerDemoDlg*)AfxGetMainWnd(); // 해당 다이얼로그 설정
tmp.Format("[%s:%d] : %s", strIPAddress, uPortNumber, szBuffer);
//pMain->m_List.AddString(tmp); // 리스트컨트롤에 출력
AfxMessageBox(tmp);
CListenSocket* pServerSocket = (CListenSocket*)m_pListenSocket;
pServerSocket->SendOrderResultDataAll(szBuffer); // 한 클라이언트로 부터 받은 메시지를 모든 클라이언트에게 전송한다.
}
CSocket::OnReceive(nErrorCode);
}
9. 프로젝트가 다이얼로그 방식이라면 Dialog 클래스에, 아니면 ~App 클래스에 변수 추가
#include "ListenSocket.h"
public:
CListenSocket* m_pListenSocket;
10. 다이얼로그 방식이라면 Dialog 클래스의 OnInitDialog()에, 아니면 ~App 클래스의 InitInstance() 에 코드 추가
// 소켓 생성, 소켓에 바인드되는 포트번호와 TCP 를 의미하는 SOCK_STREAM 인수를 사용
m_pListenSocket = new CListenSocket;
if(m_pListenSocket->Create(21000, SOCK_STREAM)) // 포트번호, TCP
{
if(!m_pListenSocket->Listen()) // 포트 충돌인지 검사
{
CString tmp;
tmp.Format("Listen 실패!!!\r\n다른 프로그램에서 [포트번호 : 21000] 를 사용 중인지 확인해주세요.");
}
}
else
AfxMessageBox("소켓 생성 실패");
11. 프로젝트가 Dialog 방식이라면 WM_DESTROY 메시지 핸들러 추가 및 코드 추가
아니면 ~App클래스의 ExitInstance() 에 코드 추가
// 연결된 모든 클라이언트와의 연결을 종료하고 객체를 삭제한 다음 서버 소켓도 삭제한다.
if(m_pListenSocket != NULL)
{
POSITION pos;
pos = m_pListenSocket->m_ptrClientSocketList.GetHeadPosition();
CClientSocket* pClient = NULL;
// 프로그램 종료 전에 메모리 해제
while(pos != NULL)
{
pClient = (CClientSocket*)m_pListenSocket->m_ptrClientSocketList.GetNext(pos);
if(pClient != NULL)
{
pClient->ShutDown();
pClient->Close();
delete pClient;
}
}
m_pListenSocket->ShutDown();
m_pListenSocket->Close();
delete m_pListenSocket;
}
B. 클라이언트 프로그로그래밍
1. Dialog 방식으로 프로그램을 만든다.
2. CSocket 클래스를 public 으로 상속받는 CConnectSocket 클래스 추가
class CConnectSocket : public CSocket
{
public:
CConnectSocket();
virtual ~CConnectSocket();
};
3. OnReceive() 가상함수를 오버드라이브하고 코드 추가
// 서버에서 메시지를 받을때 자동 호출되는 함수
void CConnectSocket::OnReceive(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
char szBuffer[BUFFER_SIZE];
::ZeroMemory(szBuffer, BUFFER_SIZE);
if(Receive(szBuffer, BUFFER_SIZE))
{
CStockOrderClientDlg* pMain = (CStockOrderClientDlg*)AfxGetMainWnd();
//Main->m_List.AddString(szBuffer);
CString tmp;
tmp.Format("%s", szBuffer);
AfxMessageBox(tmp);
}
CSocket::OnReceive(nErrorCode);
}
4. OnClose() 가상함수를 오버드라이브하고 코드 추가
// 서버와 연결이 종료되면 PostQuitMessage()를 호출해서 프로그램을 종료한다.
void CConnectSocket::OnClose(int nErrorCode)
{
// TODO: 여기에 특수화된 코드를 추가 및/또는 기본 클래스를 호출합니다.
ShutDown();
Close();
CSocket::OnClose(nErrorCode);
AfxMessageBox("서버와 연결이 종료되었습니다.");
::PostQuitMessage(0);
}
5. ~Dlg 클래스의 OnInitDialog() 에 코드 추가
// 소켓 만들고 서버와 연결한다
m_Socket.Create();
if(m_Socket.Connect("127.0.0.1", 90000) == FALSE) // 서버 연결 실패하면 프로그램 종료한다.
{
AfxMessageBox("서버 연결 실패");
PostQuitMessage(0);
return FALSE;
}
6. 전송버튼 누르면 메시를 서버에 전송한다.
void CStockOrderClientDlg::OnBnClickedButtonSend()
{
// TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
UpdateData(TRUE);
m_Socket.Send((const char*)m_csMsg, m_csMsg.GetLength());
m_csMsg = "";
UpdateData(FALSE);
}