Programming/Win Socket

소켓 03 : 클라이언트 1

kdkk57 2015. 8. 27. 01:23

 클라이언트 프로그래밍은 자기자신만 신경쓰면 되기 때문에 서버프로그래밍보다 간단합니다. 

 우선 서버프로그래밍을 한 것 처럼 프로젝트를 생성해줍니다.


[대화상자 프로젝트 생성]->[프로젝트속성-언어설정-사용안함]->[헤더파일 추가]->.cpp에서 init함수 수정



이 설정을 해주어야 오류가 나지 않습니다.


#include <WinSock2.h>//stdafx.h

#pragma comment(lib,"ws2_32.lib")//stdafx.cpp

 헤더파일은 .h에 라이브러리는 .cpp에 선언하는 것이 일반적



사용하지 않는 코드는 지워줍니다.


BOOL CMyClientApp::InitInstance()

{

CWinApp::InitInstance();


WSADATA temp;

WSAStartup(0x0202, &temp);


CMyClientDlg dlg;

m_pMainWnd = &dlg;

dlg.DoModal();


WSACleanup();

return FALSE;

}

Startup Cleanup 함수를 써줍니다.


헤더로 가서 소켓을 선언해줍니다.

private :

SOCKET mh_socket;


객체 생성자로 가서 초기화해줍니다.

객체 생성자 가서 초기화

CMyClientDlg::CMyClientDlg(CWnd* pParent /*=NULL*/)

: CDialogEx(CMyClientDlg::IDD, pParent)

{

m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

mh_socket = INVALID_SOCKET;//소켓 값이 0이 존재할 수 있기 때문에 0으로 초기화하지 않음

}


기존에 해왔던 대로 쭉 진행합니다. 소켓 설정을 해줍니다.

BOOL CMyClientDlg::OnInitDialog()

{

CDialogEx::OnInitDialog();


// 대화 상자의 아이콘을 설정합니다응용 프로그램의 창이 대화 상자가 아닐 경우에는

//  프레임워크가 작업을 자동으로 수행합니다.

SetIcon(m_hIcon, TRUE); // 아이콘을 설정합니다.

SetIcon(m_hIcon, FALSE); // 작은 아이콘을 설정합니다.


mh_socket = socket(AF_INET, SOCK_STREAM, 0);


struct sockaddr_in srv_addr;//추가

memset(&srv_addr, 0, sizeof(struct sockaddr_in));

srv_addr.sin_family = AF_INET;

srv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

srv_addr.sin_port = htons(18000);//여기까지


return TRUE// 포커스를 컨트롤에 설정하지 않으면 TRUE 반환합니다.

}

다음에 서버는 bind하지만 클라이언트는 connect를 사용합니다. bind와 인자가 같습니다. bind는 발신만 하지만(수신 위해 listen필요) connect는 바로 접속 시도 합니다. connect함수의 문제는 connect가 되었을 때 서버가 접속가능한 상태면 바로 받아주지만 정상이 아닐 경우 프로그램이 최대 28초간 응답없음에 빠질 수 있습니다. 따라서 여기에 비동기를 걸어줍니다.


WSAAsyncSelect(mh_socket, m_hWnd, 28001, FD_CONNECT);//FD_CONNECT가 발생하면 이 대화상자에 28001번 메세지를 달라

connect(mh_socket, (LPSOCKADDR)&srv_addr, sizeof(srv_addr));//bind와 인자 같다. bind

 비동기를 하면 접속을 성공하든 실패하든 무조건 28001메세지가 발생합니다. 비동기를 안하고 connect를 쓰면 성공하면 바로 뜨고 실패하면 최대 28초간 응답없음이 발생합니다.


 클래스마법사를 통해 28001 메세지를 추가합니다.

afx_msg LRESULT CMyClientDlg::On28001(WPARAM wParam, LPARAM lParam)

{

return 0;

}

 위 코드처럼 처리하면 접속이 실패하든 성공하든 이 함수로 접속합니다. wParam에 현재 접속 시도한 소켓의 핸들값이 들어옵니다. (mh_socket) 클라이언트 소켓은 서버와 달리 소켓 하나만 들어오기 때문에 wParam안쓰고 소켓값을 그냥 써도됩니다. lParam은 두가지 정보가 들어오는데 어떤 메세지인지, 에러가 있는지 여부입니다. 메세지는 FD_CONNECT 메세지 하나만 쓰기 때문에 에러를 신경씁니다.


afx_msg LRESULT CMyClientDlg::On28001(WPARAM wParam, LPARAM lParam)

{

if (WSAGETSELECTERROR(lParam)){//에러가 있으면

AfxMessageBox("서버에 접속할 없습니다.");

}

else//에러가 없으면

AfxMessageBox("서버에 접속하였습니다.");

return 0;

}

러 처리를 조건문 걸었습니다.

두가지 인자가 존재합니다.



 현재 이 상태에서 실행하면 서버가 실행되어있지 않아 접속되지 않았다고 뜹니다.




 서버를 실행한 뒤 클라이언트를 실행하면 접속에 성공하였다고 뜹니다.




이상으로 클라이언트가 서버에 붙는 것까지 완료하였습니다.