티스토리 뷰
책 : TCP/IP 윈도우 소켓 프로그래밍
간단하게 WINAPI를 활용하여 GUI 환경에서 TCP 소켓 프로그래밍에 대한 코드를 작성하였습니다. 물론 예제에 있는 코드입니다.
작동형식은 아래의 사진과 같이 작동하고, 각 서버와 클라이언트 프로젝트 파일에서 리소스 Dialog를 생성하여야 합니다.
추가 -> 새항목 -> 리소스 -> 리소스 파일 ->Dialog 선택 하시면 생성하실 수 있습니다.
클라코드
더보기
//Client
#pragma warning(disable:4996)
#pragma comment(lib, "ws2_32.lib")
#include <winsock2.h>
#include <stdio.h>
#include <stdlib.h>
#include "resource.h"
#define SERVERIP "127.0.0.1"
#define SERVERPORT 9000
#define BUFSIZE 512
//윈도우 프로시저
BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
//편집 함수
void DisplayText(const char* fmt, ...);
//오류 출력 함수
void err_quit(const char* msg);
//사용자 정의 데이터 수신 함수
int recvn(SOCKET s, char* buf, int len, int flags);
//소켓 통신 스레드 함수
DWORD WINAPI ClientMain(LPVOID arg);
SOCKET sock;
char buf[BUFSIZE + 1];
//이벤트 영역
HANDLE hReadEvent, hWriteEvent;
HWND hSendButton;
HWND hEdit1, hEdit2;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
//이벤트 생성
hReadEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
if (hReadEvent == NULL) return 1;
hWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hWriteEvent == NULL) return 1;
//소켓 통신 스레드 생성
CreateThread(NULL, 0, ClientMain, NULL, 0, NULL);
//대화 상자 생성
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, (DLGPROC)&DlgProc);
//이벤트 제거
CloseHandle(hReadEvent);
CloseHandle(hWriteEvent);
closesocket(sock);
WSACleanup();
return 0;
}
BOOL CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParm, LPARAM lParm) {
switch (uMsg) {
case WM_INITDIALOG:
hEdit1 = GetDlgItem(hDlg, IDC_EDIT1);
hEdit2 = GetDlgItem(hDlg, IDC_EDIT2);
hSendButton = GetDlgItem(hDlg, IDOK);
SendMessage(hEdit1, EM_SETLIMITTEXT, BUFSIZE, 0);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParm)) {
case IDOK:
//확인 버튼 끄기(충돌 방지)
EnableWindow(hSendButton, FALSE);
//이벤트 기다림
WaitForSingleObject(hReadEvent, INFINITE);
GetDlgItemText(hDlg, IDC_EDIT1, buf, BUFSIZE + 1);
SetEvent(hWriteEvent);
SetFocus(hEdit1);
SendMessage(hEdit1, EM_SETSEL, 0, -1);
return TRUE;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
return TRUE;
}
return FALSE;
}
return FALSE;
}
// 편집 컨트롤 출력 함수
void DisplayText(const char* fmt, ...) {
va_list arg;
va_start(arg, fmt);
char cbuf[256];
vsprintf(cbuf, fmt, arg);
int nLength = GetWindowTextLength(hEdit2);
SendMessage(hEdit2, EM_SETSEL, nLength, nLength);
SendMessage(hEdit2, EM_REPLACESEL, FALSE, (LPARAM)cbuf);
va_end(arg);
}
void err_quit(const char* msg) {
...
}
int recvn(SOCKET s, char* buf, int len, int flag) {
...
}
DWORD WINAPI ClientMain(LPVOID arg) {
int retVal;
WSAData wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
return 1;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET)
{
printf("socket create error\n");
return 1;
}
SOCKADDR_IN sockaddr;
ZeroMemory(&sockaddr, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(SERVERPORT);
sockaddr.sin_addr.s_addr = inet_addr(SERVERIP);
if (::connect(sock, (SOCKADDR*)&sockaddr, sizeof(sockaddr))) err_quit("Connet");
DisplayText("연결 완료\r\n");
while (1) {
WaitForSingleObject(hWriteEvent, INFINITE);
//문자열 길이가 0이면 보내지 않음
if (strlen(buf) == 0) {
EnableWindow(hSendButton, TRUE);
SetEvent(hReadEvent);
continue;
}
//데이터 보내기
retVal = send(sock, buf, strlen(buf), 0);
if (retVal == SOCKET_ERROR) {
err_quit("Send(()");
break;
}
DisplayText("[TCP 클라이언트] %d바이트를 보냈습니다.\r\n", retVal);
retVal = recvn(sock, buf, retVal, 0);
if (retVal == SOCKET_ERROR) {
err_quit("recv()");
break;
}
else if (retVal == 0)
break;
//받은 데이터 출력
buf[retVal] = '\0';
DisplayText("[TCP 클라이언트] %d바이트를 받았습니다.\r\n", retVal);
DisplayText("[받은 데이터] %s\r\n", buf);
EnableWindow(hSendButton, TRUE);
SetEvent(hReadEvent);
}
return 0;
}
서버코드
더보기
//Server
#pragma comment(lib, "ws2_32")
#pragma warning(disable:4996)
#include<WinSock2.h>
#include<stdlib.h>
#include<Windows.h>
#include<stdio.h>
#define SERVERPORT 9000
#define BUFSIZE 512
//윈도우 프로시저
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParm, LPARAM lParm);
//편집 컨트롤 출력 함수
void DisplayText(const char* fmt, ...);
//오류 출력 함수
void err_quit(const char* msg);
//소켓 통신 함수
DWORD WINAPI ServerMain(LPVOID arg);
DWORD WINAPI ProcessClient(LPVOID arg);
HINSTANCE hInst; // 인스턴스 핸들
HWND hEdit; // 편집 컨트롤
CRITICAL_SECTION cs; // 임계영역
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
WNDCLASS wndclass;
InitializeCriticalSection(&cs);
hInst = hInstance;
//윈도우 초기화
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = "MyWndClass";
if (!RegisterClass(&wndclass)) return 1;
//윈도우 생성
HWND hWnd = CreateWindow("MyWndClass", "WinApp", WS_OVERLAPPEDWINDOW, 0, 0, 600, 200, NULL, NULL, hInstance, NULL);
if (hWnd == NULL) return 1;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
//소켓 통신 쓰레드 생성
CreateThread(NULL, 0, ServerMain, NULL, 0, NULL);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParm, LPARAM lParm) {
//윈도우 프로시저로 GUI 작동하는 실질적 코드
switch (uMsg) {
//처음 생성시
case WM_CREATE:
hEdit = CreateWindow("edit", NULL, WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | ES_READONLY,
0, 0, 0, 0, hWnd, (HMENU)100, hInst, (LPVOID)TRUE);
DisplayText("간단한 GUI 응용 프로그램이니다.\r\n");
DisplayText("인스턴스 핸들 값은 %#x입니다.\r\n", hInst);
return 0;
//사이즈 변경시
case WM_SIZE:
MoveWindow(hEdit, 0, 0, LOWORD(lParm), HIWORD(lParm), TRUE);
return 0;
case WM_SETFOCUS:
SetFocus(hEdit);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParm, lParm);
}
// 편집 컨트롤 출력 함수
void DisplayText(const char* fmt, ...) {
va_list arg;
va_start(arg, fmt);
char cbuf[BUFSIZE+256];
//임계영역 설정
EnterCriticalSection(&cs);
vsprintf(cbuf, fmt, arg);
int nLength = GetWindowTextLength(hEdit);
//마지막끝으로 포인터 이동
SendMessage(hEdit, EM_SETSEL, nLength, nLength);
//글쓰기
SendMessage(hEdit, EM_REPLACESEL, FALSE, (LPARAM)cbuf);
LeaveCriticalSection(&cs);
va_end(arg);
}
void err_quit(const char* msg) {
,,,
}
DWORD WINAPI ServerMain(LPVOID arg) {
int retVal;
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
return 1;
SOCKET listenSock = socket(AF_INET, SOCK_STREAM, 0);
if (listenSock == INVALID_SOCKET) err_quit("socket()");
//연결과정은 같음.
SOCKADDR_IN serveraddr;
ZeroMemory(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(SERVERPORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
retVal = bind(listenSock, (SOCKADDR*)&serveraddr, sizeof(serveraddr));
if (retVal == SOCKET_ERROR) err_quit("listen()");
retVal = listen(listenSock, 10);
if (retVal == SOCKET_ERROR) err_quit("listen()");
//데이터 통신에 사용할 변수
SOCKET clientsock;
SOCKADDR_IN clientaddr;
int addrlen;
HANDLE hThread;
while (1) {
//accept
addrlen = sizeof(clientaddr);
clientsock = ::accept(listenSock, (SOCKADDR*)&clientaddr, &addrlen);
if (clientsock == INVALID_SOCKET) {
err_quit("accept()");
break;
}
//클라 정보 출력
DisplayText("[TCP서버] 클라이언트 접속 IP : %s %d\r\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
//연결된 클라를 상대할 쓰레드 Client 생성
hThread = CreateThread(NULL, 0, ProcessClient, (LPVOID)clientsock, 0, NULL);
if (hThread == NULL) closesocket(clientsock);
else CloseHandle(hThread);
}
closesocket(listenSock);
WSACleanup();
return 0;
}
DWORD WINAPI ProcessClient(LPVOID arg) {
SOCKET clientfd = (SOCKET)arg;
SOCKADDR_IN clientaddr;
int addrlen;
int len;
char buf[BUFSIZE + 1];
//클라 정보 얻기
addrlen = sizeof(clientaddr);
getpeername(clientfd, (SOCKADDR*)&clientaddr, &addrlen);
while (1) {
//데이터받기(고정길이)
int retVal = recv(clientfd, buf, BUFSIZE, 0);
if (retVal == SOCKET_ERROR) {
err_quit("Recv");
break;
}
else if (retVal == 0) break;
//받은 데이터 출력
buf[retVal] = '\0';
DisplayText("[TCP/%s : %d] %s\r\n", inet_ntoa(clientaddr.sin_addr),
ntohs(clientaddr.sin_port), buf);
retVal = send(clientfd, buf, retVal, 0);
if (retVal == SOCKET_ERROR) {
err_quit("Send");
break;
}
}
closesocket(clientfd);
DisplayText("[TCP 서버] 클라이언트 종료 : IP 주소 = %s, 포트번호 = %d\r\n",
inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
return 0;
}
'서버 공부 > 네트워크' 카테고리의 다른 글
비동기 형식 TCP/IP 서버 (0) | 2022.07.08 |
---|---|
GUI 멀티쓰레드 소켓 프로그래밍(TCP) 2 - 호스트이름 구하기 추가 (0) | 2022.07.07 |
TCP/IP 윈도우 소켓 프로그래밍 7장 연습문제 (0) | 2022.07.05 |
멀티 쓰레드를 활용한 서버/클라이언트 기본 통신 (0) | 2022.07.05 |
소켓 네트워크 프로그래밍) 데이터 전송 방식 -> 고정 + 가변 길이 데이터 전송 (0) | 2022.07.04 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 드림핵
- STL
- c++
- Select모델
- 스레드풀
- 컨퍼런스
- 알고리즘
- 보안
- Dreamhack
- 자료구조
- 레지스터
- 지뢰찾기
- 링크드 리스트
- 워셜알고리즘
- 개발
- 고양이
- 멀티쓰레드
- 시스템보안
- 정보보안
- 인제대학교
- 백준
- queue
- 더블버퍼링
- BFS
- 학교
- 야경
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함