소켓 03 : 클라이언트 1
클라이언트 프로그래밍은 자기자신만 신경쓰면 되기 때문에 서버프로그래밍보다 간단합니다.
우선 서버프로그래밍을 한 것 처럼 프로젝트를 생성해줍니다.
[대화상자 프로젝트 생성]->[프로젝트속성-언어설정-사용안함]->[헤더파일 추가]->.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;
}
에러 처리를 조건문 걸었습니다.
두가지 인자가 존재합니다.
현재 이 상태에서 실행하면 서버가 실행되어있지 않아 접속되지 않았다고 뜹니다.
서버를 실행한 뒤 클라이언트를 실행하면 접속에 성공하였다고 뜹니다.
이상으로 클라이언트가 서버에 붙는 것까지 완료하였습니다.