728x90
반응형
[클라이언트에서 서버로 데이터 전송]
[서버]
// 서버 프로그램
// 0128_3.cpp : 애플리케이션에 대한 진입점을 정의합니다.
#include <winsock2.h>
#include <process.h>
#include <atlstr.h>
#include "framework.h"
#include "0128_3.h"
#define MAX_LOADSTRING 100
#define WM_USER_SOCKET (WM_USER + 1)
#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; // 메인 소켓 아님
// 0x0401
SOCKET hSocket[100];
struct USERINFO
{
SOCKET hSocket;
int id;
int x, y;
};
struct FORMAT1 {
short mx;
short my;
};
#define FORMATID1 1234
#define FORMATID2 2
#define FORMATID3 3
#define FORMATID4 4
unsigned _stdcall ClientThread(void *pArg)
{
USERINFO* userInfo = (USERINFO*)pArg;
// 이벤트 관리할 이벤트 핸들 생성
WSAEVENT hRecvEvent = WSACreateEvent(); // 발생한 이벤트를 가져오는 객체를 생성한다.
// 감지할 이벤트 설정한다. (어떤 이벤트를 설정할 것인가...)
WSAEventSelect(userInfo->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(userInfo->hSocket, hRecvEvent, &WhatEvent);
int index = 0;
char recvBuffer[4096] = { 0, };
int RecvSize;
if ((WhatEvent.lNetworkEvents & FD_READ) == FD_READ)
{
while (true) // recv: 4byte 모두 수신한다는 보장이 없다.
{
RecvSize = recv(userInfo->hSocket, &recvBuffer[index], 4096, 0);
if (RecvSize > 0)
{
index += RecvSize;
}else{break;}
}
}
//MOUSEDATA mouseData;
// 데이터 가공 한다.
int pos = 0;
short mx, my;
while (index)
{
int formatNum = *((int*)recvBuffer + pos); // 1. send를 한번 받을수도 있고
pos += sizeof(int); // 2. send를 한번 이상으로 받을수도 있다.
switch (formatNum)
{
case FORMATID1:
{
mx = ((FORMAT1*)(recvBuffer + pos))->mx;
my = ((FORMAT1*)(recvBuffer + pos))->my;
pos += sizeof(FORMAT1);
index -= 8; // 받은 데이터가 8Byte
}break;
case FORMATID2: {}break;
case FORMATID3: {}break;
case FORMATID4: {}break;
}
CString s;
s.Format(L"formatNum: %d " L"mx:%d mx:%d", formatNum, mx, my);
SetWindowText(hWnd, s);
}
if ((WhatEvent.lNetworkEvents & FD_CLOSE) == FD_CLOSE)
{
closesocket(userInfo->hSocket);
// 메모리 해제 코드
delete userInfo;
// 접속자 수를 한명 감소시킨다.
MessageBox(hWnd, L"클라이언트 접속 종료", L"FD_CLOSE", MB_OK);
break;
}
struct PACKDATA {
short sum;
};
PACKDATA packData;
packData.sum = mx + my;
char sendBuffer[512] = { 0, };
((PACKDATA*)sendBuffer)->sum = packData.sum;
/*for (int i = 0; i < 반복횟수; i++)
{
send(받는 대상의 소켓번호, sendBuffer, 2, 0);
}*/
send(userInfo->hSocket, sendBuffer, 2, 0);
}
_endthreadex(0);
return true;
}
unsigned _stdcall ConnectThread(void * pArg)
{
//OutputDebugString();
SetWindowText(hWnd, L"Server_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");
SOCKET hSocket = accept(MainSocket, (struct sockaddr*) &sa, &len);
USERINFO* userInfo = new USERINFO;
userInfo->hSocket = hSocket;
userInfo->id = num;
userInfo->x = num * 3;
userInfo->y = num * 4;
num++;
CString s;
s.Format(L"%04d", num);
SetWindowText(hWnd, s.GetBuffer());
UINT threadID;
_beginthreadex(NULL, 0, ClientThread, userInfo, 0, &threadID);
}
_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;
}
[클라이언트]
// 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"Client_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("192.168.123.102"); // 통신 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;
}
struct FORMAT1
{
short mx;
short my;
};
//FORMAT1 format1;
struct FORMAT2
{
short data;
};
struct FORMAT3
{
short x, y, z;
};
struct FORMAT4
{
short ar[10];
};
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[512] = {0, }; // send()할 데이터.
int formatNum = 1234;
*((int*)sendBuffer + 0) = formatNum;
((FORMAT1*)(sendBuffer + 4))->mx = mx; // 수동 임의 data값: 0x5678;
((FORMAT1*)(sendBuffer + 4))->my = my; // 수동 임의 data값: 0x6666;
send(ClientSocket, sendBuffer, 4 + sizeof(FORMAT1), 0); // 소켓 id, 보낼 데이터, 사이즈, 0
//send(ClientSocket, sendBuffer, 4, 0);
}break;
case WM_RBUTTONDOWN:
{
if (ClientSocket != 0)
{
closesocket(ClientSocket);
ClientSocket = 0;
}
}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
반응형
'Education > Edu | .net' 카테고리의 다른 글
# 20) [Oracle] install 및 setting 방법 (0) | 2021.02.03 |
---|---|
# 19.3) [Windows Network Programming] 시작하기6 (0) | 2021.01.29 |
# 19.1) [Windows Network Programming] 시작하기4 (0) | 2021.01.29 |
# 18.2) [Windows Network Programming] 시작하기3 (0) | 2021.01.28 |
# 18.1) [Windows Network Programming] 시작하기2 (0) | 2021.01.28 |