728x90
반응형

기본 셋팅

1. 프로젝트 -> 속성 -> C/C++ -> 코드생성 -> 런타임 라이브러리: 다중 스레드 디버그(/MTd) 변경

2. 프로젝트 -> 속성 -> 링커 -> 입력 -> 추가 종속성 -> 편집 -> wsock32.lib, ws2_32.lib 두개 추가

 

[서버 프로그램]

// 서버 프로그램
// 0128_3.cpp : 애플리케이션에 대한 진입점을 정의합니다.

#include <winsock2.h>
#include <process.h>
#include <atlstr.h>

#include "framework.h"
#include "0128_3.h"

#define MAX_LOADSTRING 100
#pragma warning(disable:4996)

// 전역 변수:
HINSTANCE hInst;                                // 현재 인스턴스입니다.
WCHAR szTitle[MAX_LOADSTRING];                  // 제목 표시줄 텍스트입니다.
WCHAR szWindowClass[MAX_LOADSTRING];            // 기본 창 클래스 이름입니다.

// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

// 서버측 프로그램 전역 변수
HWND hWnd;
bool LoopConnectThread = true;
SOCKET MainSocket;
SOCKET hSocket; // 메인 소켓 아님

#define WM_USER_SOCKET (WM_USER + 1)
struct MOUSEDATA {
	short mx;
	short my;
};
struct PACKDATA {
	short sum;
};
PACKDATA packData;
MOUSEDATA mouseData;

unsigned _stdcall ClientThread(void *pArg) {
	// 이벤트 관리할 이벤트 핸들 생성
	WSAEVENT hRecvEvent = WSACreateEvent(); // 발생한 이벤트를 가져오는 객체를 생성한다.
	
	// 감지할 이벤트 설정한다. (어떤 이벤트를 설정할 것인가...)
	WSAEventSelect(hSocket, hRecvEvent, FD_READ | FD_CLOSE); // 상대방이 접속을 종료할 수 있으니
	while (true) 
	{
		// 이 함수는 Blocking 함수이다.(scanf)
		// WSAEventSelect << 함수에서 설정한 이벤트가 발생하면 Blocking이 풀린다.
		// >>>> 여기 Blocking
		WSAWaitForMultipleEvents(1, &hRecvEvent, false, WSA_INFINITE, false);
		// Blocking을 푼 이벤트가 어떤 이벤트인지 확인...
		//MessageBox(hWnd, L"WSAEventSelect", L"WSAEventSelect", MB_OK);
		WSANETWORKEVENTS WhatEvent;
		WSAEnumNetworkEvents(hSocket, hRecvEvent, &WhatEvent);
		char recvBuffer[4096] = { 0, };
		if ((WhatEvent.lNetworkEvents & FD_READ) == FD_READ) 
		{
			recv(hSocket, recvBuffer, 4096, 0);
		}
		// 데이터 가공 한다.
		short mx = ((MOUSEDATA*)recvBuffer)->mx;
		short my = ((MOUSEDATA*)recvBuffer)->my;
		CString s;
		s.Format(L"mx:%d mx:%d", mx, my);
		SetWindowText(hWnd, s);
		packData.sum = mx + my;
		char sendBuffer[512] = { 0, };
		((PACKDATA*)sendBuffer)->sum = packData.sum;
		send(hSocket, sendBuffer, 2, 0);
	}
	_endthreadex(0);
	return true;
}
unsigned _stdcall ConnectThread(void * pArg) 
{
	//OutputDebugString();
	SetWindowText(hWnd, L"Start");
	// 네트워크 관련 초기화 작업
	WORD version = MAKEWORD(2, 2);  // 네트워크 버전 설정
	WSADATA wsaData;
	// 네트워크와 관련된 (함수)범용 동적라이브러리를 로드
	int err;
	err = WSAStartup(version, &wsaData); //윈도우가 가지고 네트워크 라이브러리(.DLL)을 끌어오는 함수
											 // 비용 절감 효과
											 // 단, 초기 로딩 시간이 길다
	if (err) {
		SetWindowText(hWnd, L"Error : WSAStartup");
		return false;
	}
	//0x3456; LOBYTE << 56
	//wsaData.wVersion != 0x0202 아것도 가능
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		SetWindowText(hWnd, L"Error : Version");
		WSACleanup();
		return false;
	}
	
	// 소켓 생성
	MainSocket = socket(AF_INET, SOCK_STREAM, 0); // TCP(데이터 손실 없음), UDP(손실이 있을 수 있다.)
	if (MainSocket == INVALID_SOCKET) {
		SetWindowText(hWnd, L"Error : Socket Create");
		WSACleanup();
		return false;
	}
	// 윈도우에게 현재 시스템의 통신 상태를 등록한다.
	struct sockaddr_in sa;
	ZeroMemory(&sa, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_addr.s_addr = htonl(INADDR_ANY); // inet_addr("192.168.1.39);
	sa.sin_port = htons(3000);    // 데이터 송수신 통로 => port
	// 바인딩: 윈도우에게 정보를 묶어 접수하는 것;
	err = bind(MainSocket, (struct sockaddr*)&sa, sizeof(struct sockaddr));
	if (err == SOCKET_ERROR) {
		SetWindowText(hWnd, L"Error : Bind");
		return false;
	}
	// 대기 인원을 설정한다.
	err = listen(MainSocket, 10);
	if (err == SOCKET_ERROR) {
		SetWindowText(hWnd, L"Error : Listen");
		return false;
	}
	SetWindowText(hWnd, L"OK!!");
	static int num = 0;
	while (LoopConnectThread) 
	{
		ZeroMemory(&sa, sizeof(sa));
		int len = sizeof(sa);
		// 클라이언트의 접속을 기다림
		// Blocking 함수이다. scanf()함수와 같다
		// 프로그램이 진행하지 않는다.
		// 누군가가 접속하면 풀린다.
		//SetWindowText(hWnd, L"100");
		//SetWindowText(hWnd, L"200");
		hSocket = accept(MainSocket, (struct sockaddr*)&sa, &len);
		num++;
		CString s;
		s.Format(L"%04d", num);
		SetWindowText(hWnd, s);
		_beginthreadex(NULL, 0, ClientThread, NULL, 0, 0);
	}
	_endthreadex(0);
	return true;
}
void InitData() {
	UINT threadID;
	_beginthreadex(NULL, 0, ConnectThread, NULL, 0, &threadID);
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 여기에 코드를 입력합니다.

    // 전역 문자열을 초기화합니다.
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_MY01283, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 애플리케이션 초기화를 수행합니다:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MY01283));

    MSG msg;

	InitData();
    // 기본 메시지 루프입니다:
    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}


ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MY01283));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_MY01283);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}


BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.

   hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
	   0, 0, 400, 300, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hWnd, &ps);
		// TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
		EndPaint(hWnd, &ps);
	}
	break;
	case WM_DESTROY:
		PostQuitMessage(0);
		LoopConnectThread = false;
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}
// 정보 대화 상자의 메시지 처리기입니다.
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
	UNREFERENCED_PARAMETER(lParam);
	switch (message)
	{
	case WM_INITDIALOG:
		return (INT_PTR)TRUE;
	case WM_COMMAND:
		if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
		{
			EndDialog(hDlg, LOWORD(wParam));
			return (INT_PTR)TRUE;
		}
		break;
	}
	return (INT_PTR)FALSE;
}

[클라이언트 프로그램]

// 클라이언트 프로그램
// WindowsProject2.cpp : 애플리케이션에 대한 진입점을 정의합니다.

#pragma warning(disable:4996)
#include <process.h>
#include <atlstr.h>
#include <winsock2.h>
#include "framework.h"
#include "WindowsProject2.h"

#define MAX_LOADSTRING 100


// 전역 변수:
HINSTANCE hInst;                                // 현재 인스턴스입니다.
WCHAR szTitle[MAX_LOADSTRING];                  // 제목 표시줄 텍스트입니다.
WCHAR szWindowClass[MAX_LOADSTRING];            // 기본 창 클래스 이름입니다.

HWND hWnd;
// 클라이언트 프로그램 측 전역변수
SOCKET ClientSocket;
WORD mx, my;
#define WM_USER_SOCKET ((WM_USER) + 1)

// 이 코드 모듈에 포함된 함수의 선언을 전달합니다:
ATOM                MyRegisterClass(HINSTANCE hInstance);
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    About(HWND, UINT, WPARAM, LPARAM);

struct PACKDATA {
	short mx;
	short my;
};
PACKDATA packData;


bool InitData()
{
	SetWindowText(hWnd, L"Start");
	// 네트워크 관련 초기화 작업.
	WORD version = MAKEWORD(2, 2);          // 버전
	// 네트워크와 관련된 범용(동적) 라이브러리(DLL)를 로드.
	WSADATA wsaData;
	int err;
	err = WSAStartup(version, &wsaData);    // 네트워크 관련 DLL을 로드해주는 함수. // 단, 초기 로딩 시간이 길다
	
	if (err) 
	{ 
		SetWindowText(hWnd, L"Error : WSAStartup"); 
		return false; 
	}
	
	// 버전 확인.
	if (LOBYTE(wsaData.wVersion) != 2 ||
		HIBYTE(wsaData.wVersion) != 2)
	{
		SetWindowText(hWnd, L"Error : version"); WSACleanup(); return false;
	}

	// 소켓 생성.
	ClientSocket = socket(AF_INET, SOCK_STREAM, 0);       // 인터넷, TCP, 사용하지 않음.
	
	if (ClientSocket == INVALID_SOCKET) // 실패 시 -1 리턴.
	{                 
		SetWindowText(hWnd, L"Error : socket create");
		WSACleanup();
		return false;
	}

	// 소켓 id와 윈도우 핸들을 연결
	WSAAsyncSelect(ClientSocket, hWnd, WM_USER_SOCKET, FD_CONNECT);
	SOCKADDR_IN SocketAddressIn;
	ZeroMemory(&SocketAddressIn, sizeof(SocketAddressIn));
	SocketAddressIn.sin_family = AF_INET;                            // 인터넷 통신을 사용하겠다는 설정.
	SocketAddressIn.sin_addr.s_addr = inet_addr("175.209.38.184");             // 통신 IP를 얻어온다.
	SocketAddressIn.sin_port = htons(3000);
	// 접속.
	// 비동기 함수.
	err = connect(ClientSocket, (LPSOCKADDR)&SocketAddressIn, sizeof(SocketAddressIn));
	
	if (err == SOCKET_ERROR)
	{
		int ErrCode = WSAGetLastError();    // 현재 네트워크 상에서 가장 최근의 에러.
		
		if (ErrCode != WSAEWOULDBLOCK)       // 에러 코드가 우드 블록이 아니면 에러.
		{
			SetWindowText(hWnd, L"Error : connect");
			return false;
		}
	}
	return true;
}

int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // TODO: 여기에 코드를 입력합니다.

    // 전역 문자열을 초기화합니다.
    LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
    LoadStringW(hInstance, IDC_WINDOWSPROJECT2, szWindowClass, MAX_LOADSTRING);
    MyRegisterClass(hInstance);

    // 애플리케이션 초기화를 수행합니다:
    if (!InitInstance (hInstance, nCmdShow))
    {
        return FALSE;
    }

    HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_WINDOWSPROJECT2));

    MSG msg;

	InitData();

    // 기본 메시지 루프입니다:

    while (GetMessage(&msg, nullptr, 0, 0))
    {
        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}


ATOM MyRegisterClass(HINSTANCE hInstance)
{
    WNDCLASSEXW wcex;

    wcex.cbSize = sizeof(WNDCLASSEX);

    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;
    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_WINDOWSPROJECT2));
    wcex.hCursor        = LoadCursor(nullptr, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCEW(IDC_WINDOWSPROJECT2);
    wcex.lpszClassName  = szWindowClass;
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));

    return RegisterClassExW(&wcex);
}


BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   hInst = hInstance; // 인스턴스 핸들을 전역 변수에 저장합니다.

   hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      1100, 300, 400, 300, nullptr, nullptr, hInstance, nullptr);

   if (!hWnd)
   {
      return FALSE;
   }

   ShowWindow(hWnd, nCmdShow);
   UpdateWindow(hWnd);

   return TRUE;
}


LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_USER_SOCKET:
	{
		UINT event = WSAGETSELECTEVENT(lParam);    // 네트워크에서 발생한 이벤트를 얻는 함수.
		switch (event)
		{
		case FD_CONNECT:
		{
			WSAAsyncSelect(ClientSocket, hWnd, WM_USER_SOCKET, FD_READ);
			// MessageBox(hWnd, L"WM_USER_SOCKET", L"", MB_OK);
		}break;
		
		case FD_READ:
		{
			// 데이터를 받는다.
			char recvBuffer[4096] = { 0, };
			recv(ClientSocket, recvBuffer, 4096, 0);
			short sum = (short)*(short*)recvBuffer;
			
			// 데이터를 출력한다.
			CString s;
			s.Format(L"%d", sum);
			SetWindowText(hWnd, s);
		}break;
		
		default: 
			break;
		}
	}break;
	
	case WM_MOUSEMOVE:
	{
		mx = LOWORD(lParam);
		my = HIWORD(lParam);
		CString s;
		s.Format(L"mx: %d my: %d", mx, my);
		SetWindowText(hWnd, s.GetBuffer());
	}break;
	
	case WM_LBUTTONDOWN:
	{
		// 통신 시 데이터가 4096 byte일 때 가장 안정적이다.
		const char sendBuffer[4096] = { 0, };    // send()할 데이터.
		((PACKDATA*)sendBuffer)->mx = mx;
		((PACKDATA*)sendBuffer)->my = my;
		send(ClientSocket, sendBuffer, 4, 0);   // 소켓 id, 보낼 데이터, 사이즈, 0
	}break;
	
	case WM_RBUTTONDOWN:
	{
	}break;
	
	case WM_PAINT:
	{
		PAINTSTRUCT ps;
		HDC hdc = BeginPaint(hWnd, &ps);
		// TODO: 여기에 hdc를 사용하는 그리기 코드를 추가합니다...
		EndPaint(hWnd, &ps);
	}break;
	
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

[최종 클라이언트에서 서버로 전송]

728x90
반응형

+ Recent posts