네트워크프로그래밍 gui..
조회수 1551회
#pragma comment(lib, "ws2_32")
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include "resource.h"
#define SERVERIPV4 "127.0.0.1"
#define SERVERIPV6 "::1"
#define SERVERPORT 9000
#define BUFSIZE 256 // 전송 메시지 전체 크기
#define MSGSIZE (BUFSIZE-sizeof(int)) // 채팅 메시지 최대 길이
#define CHATTING 1000 // 메시지 타입: 채팅
#define DRAWLINE 1001 // 메시지 타입: 선 그리기
#define WM_DRAWIT (WM_USER+1) // 사용자 정의 윈도우 메시지
// 공통 메시지 형식
// sizeof(COMM_MSG) == 256
struct COMM_MSG
{
int type;
char dummy[MSGSIZE];
};
// 채팅 메시지 형식
// sizeof(CHAT_MSG) == 256
struct CHAT_MSG
{
int type;
char buf[MSGSIZE];
};
// sizeof(DRAWLINE_MSG) == 256
// 선 그리기 메시지 형식
struct DRAWLINE_MSG
{
int type;
int color;
int x0, y0;
int x1, y1;
char dummy[BUFSIZE - 6 * sizeof(int)];
};
static HINSTANCE g_hInst; // 응용 프로그램 인스턴스 핸들
static HWND g_hDrawWnd; // 그림을 그릴 윈도우
static HWND g_hButtonSendMsg; // '메시지 전송' 버튼
static HWND g_hEditStatus; // 받은 메시지 출력
static char g_ipaddr[64]; // 서버 IP 주소
static u_short g_port; // 서버 포트 번호
static BOOL g_isIPv6; // IPv4 or IPv6 주소?
static HANDLE g_hClientThread; // 스레드 핸들
static volatile BOOL g_bStart; // 통신 시작 여부
static SOCKET g_sock; // 클라이언트 소켓
static HANDLE g_hReadEvent, g_hWriteEvent; // 이벤트 핸들
static CHAT_MSG g_chatmsg; // 채팅 메시지 저장
static DRAWLINE_MSG g_drawmsg; // 선 그리기 메시지 저장
static int g_drawcolor; // 선 그리기 색상
// 대화상자 프로시저
BOOL CALLBACK DlgProc(HWND, UINT, WPARAM, LPARAM);
// 소켓 통신 스레드 함수
DWORD WINAPI ClientMain(LPVOID arg);
DWORD WINAPI ReadThread(LPVOID arg);
DWORD WINAPI WriteThread(LPVOID arg);
// 자식 윈도우 프로시저
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// 편집 컨트롤 출력 함수
void DisplayText(const char *fmt, ...);
// 사용자 정의 데이터 수신 함수
int recvn(SOCKET s, char *buf, int len, int flags);
// 오류 출력 함수
void err_quit(const char *msg);
void err_display(const char *msg);
// 메인 함수
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
// 윈속 초기화
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return 1;
// 이벤트 생성
g_hReadEvent = CreateEvent(NULL, FALSE, TRUE, NULL);
if (g_hReadEvent == NULL) return 1;
g_hWriteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (g_hWriteEvent == NULL) return 1;
// 변수 초기화(일부)
g_chatmsg.type = CHATTING;
g_drawmsg.type = DRAWLINE;
g_drawmsg.color = RGB(255, 0, 0);
// 대화상자 생성
g_hInst = hInstance;
DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
// 이벤트 제거
CloseHandle(g_hReadEvent);
CloseHandle(g_hWriteEvent);
// 윈속 종료
WSACleanup();
return 0;
}
// 대화상자 프로시저
BOOL CALLBACK DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static HWND hButtonIsIPv6;
static HWND hEditIPaddr;
static HWND hEditPort;
static HWND hButtonConnect;
static HWND hEditMsg;
static HWND hColorRed;
static HWND hColorGreen;
static HWND hColorBlue;
switch (uMsg) {
case WM_INITDIALOG:
// 컨트롤 핸들 얻기
hButtonIsIPv6 = GetDlgItem(hDlg, IDC_ISIPV6);
hEditIPaddr = GetDlgItem(hDlg, IDC_IPADDR);
hEditPort = GetDlgItem(hDlg, IDC_PORT);
hButtonConnect = GetDlgItem(hDlg, IDC_CONNECT);
g_hButtonSendMsg = GetDlgItem(hDlg, IDC_SENDMSG);
hEditMsg = GetDlgItem(hDlg, IDC_MSG);
g_hEditStatus = GetDlgItem(hDlg, IDC_STATUS);
hColorRed = GetDlgItem(hDlg, IDC_COLORRED);
hColorGreen = GetDlgItem(hDlg, IDC_COLORGREEN);
hColorBlue = GetDlgItem(hDlg, IDC_COLORBLUE);
// 컨트롤 초기화
SendMessage(hEditMsg, EM_SETLIMITTEXT, MSGSIZE, 0);
EnableWindow(g_hButtonSendMsg, FALSE);
SetDlgItemText(hDlg, IDC_IPADDR, SERVERIPV4);
SetDlgItemInt(hDlg, IDC_PORT, SERVERPORT, FALSE);
SendMessage(hColorRed, BM_SETCHECK, BST_CHECKED, 0);
SendMessage(hColorGreen, BM_SETCHECK, BST_UNCHECKED, 0);
SendMessage(hColorBlue, BM_SETCHECK, BST_UNCHECKED, 0);
// 윈도우 클래스 등록
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = g_hInst;
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;
// 자식 윈도우 생성
g_hDrawWnd = CreateWindow("MyWndClass", "그림 그릴 윈도우", WS_CHILD,
450, 38, 425, 415, hDlg, (HMENU)NULL, g_hInst, NULL);
if (g_hDrawWnd == NULL) return 1;
ShowWindow(g_hDrawWnd, SW_SHOW);
UpdateWindow(g_hDrawWnd);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDC_ISIPV6:
g_isIPv6 = SendMessage(hButtonIsIPv6, BM_GETCHECK, 0, 0);
if (g_isIPv6 == false)
SetDlgItemText(hDlg, IDC_IPADDR, SERVERIPV4);
else
SetDlgItemText(hDlg, IDC_IPADDR, SERVERIPV6);
return TRUE;
case IDC_CONNECT:
GetDlgItemText(hDlg, IDC_IPADDR, g_ipaddr, sizeof(g_ipaddr));
g_port = GetDlgItemInt(hDlg, IDC_PORT, NULL, FALSE);
g_isIPv6 = SendMessage(hButtonIsIPv6, BM_GETCHECK, 0, 0);
// 소켓 통신 스레드 시작
g_hClientThread = CreateThread(NULL, 0, ClientMain, NULL, 0, NULL);
if (g_hClientThread == NULL) {
MessageBox(hDlg, "클라이언트를 시작할 수 없습니다."
"\r\n프로그램을 종료합니다.", "실패!", MB_ICONERROR);
EndDialog(hDlg, 0);
}
else {
EnableWindow(hButtonConnect, FALSE);
while (g_bStart == FALSE); // 서버 접속 성공 기다림
EnableWindow(hButtonIsIPv6, FALSE);
EnableWindow(hEditIPaddr, FALSE);
EnableWindow(hEditPort, FALSE);
EnableWindow(g_hButtonSendMsg, TRUE);
SetFocus(hEditMsg);
}
return TRUE;
case IDC_SENDMSG:
// 읽기 완료를 기다림
WaitForSingleObject(g_hReadEvent, INFINITE);
GetDlgItemText(hDlg, IDC_MSG, g_chatmsg.buf, MSGSIZE);
// 쓰기 완료를 알림
SetEvent(g_hWriteEvent);
// 입력된 텍스트 전체를 선택 표시
SendMessage(hEditMsg, EM_SETSEL, 0, -1);
return TRUE;
case IDC_COLORRED:
g_drawmsg.color = RGB(255, 0, 0);
return TRUE;
case IDC_COLORGREEN:
g_drawmsg.color = RGB(0, 255, 0);
return TRUE;
case IDC_COLORBLUE:
g_drawmsg.color = RGB(0, 0, 255);
return TRUE;
case IDCANCEL:
if (MessageBox(hDlg, "정말로 종료하시겠습니까?",
"질문", MB_YESNO | MB_ICONQUESTION) == IDYES)
{
closesocket(g_sock);
EndDialog(hDlg, IDCANCEL);
}
return TRUE;
}
return FALSE;
}
return FALSE;
}
// 소켓 통신 스레드 함수
DWORD WINAPI ClientMain(LPVOID arg)
{
int retval;
if (g_isIPv6 == false) {
// socket()
g_sock = socket(AF_INET, SOCK_STREAM, 0);
if (g_sock == INVALID_SOCKET) err_quit("socket()");
// connect()
SOCKADDR_IN serveraddr;
ZeroMemory(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(g_ipaddr);
serveraddr.sin_port = htons(g_port);
retval = connect(g_sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr));
if (retval == SOCKET_ERROR) err_quit("connect()");
}
else {
// socket()
g_sock = socket(AF_INET6, SOCK_STREAM, 0);
if (g_sock == INVALID_SOCKET) err_quit("socket()");
// connect()
SOCKADDR_IN6 serveraddr;
ZeroMemory(&serveraddr, sizeof(serveraddr));
serveraddr.sin6_family = AF_INET6;
int addrlen = sizeof(serveraddr);
WSAStringToAddress(g_ipaddr, AF_INET6, NULL,
(SOCKADDR *)&serveraddr, &addrlen);
serveraddr.sin6_port = htons(g_port);
retval = connect(g_sock, (SOCKADDR *)&serveraddr, sizeof(serveraddr));
if (retval == SOCKET_ERROR) err_quit("connect()");
}
MessageBox(NULL, "서버에 접속했습니다.", "성공!", MB_ICONINFORMATION);
// 읽기 & 쓰기 스레드 생성
HANDLE hThread[2];
hThread[0] = CreateThread(NULL, 0, ReadThread, NULL, 0, NULL);
hThread[1] = CreateThread(NULL, 0, WriteThread, NULL, 0, NULL);
if (hThread[0] == NULL || hThread[1] == NULL) {
MessageBox(NULL, "스레드를 시작할 수 없습니다."
"\r\n프로그램을 종료합니다.",
"실패!", MB_ICONERROR);
exit(1);
}
g_bStart = TRUE;
// 스레드 종료 대기
retval = WaitForMultipleObjects(2, hThread, FALSE, INFINITE);
retval -= WAIT_OBJECT_0;
if (retval == 0)
TerminateThread(hThread[1], 1);
else
TerminateThread(hThread[0], 1);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
g_bStart = FALSE;
MessageBox(NULL, "서버가 접속을 끊었습니다", "알림", MB_ICONINFORMATION);
EnableWindow(g_hButtonSendMsg, FALSE);
closesocket(g_sock);
return 0;
}
// 데이터 받기
DWORD WINAPI ReadThread(LPVOID arg)
{
int retval;
COMM_MSG comm_msg;
CHAT_MSG *chat_msg;
DRAWLINE_MSG *draw_msg;
while (1) {
retval = recvn(g_sock, (char *)&comm_msg, BUFSIZE, 0);
if (retval == 0 || retval == SOCKET_ERROR) {
break;
}
if (comm_msg.type == CHATTING) {
chat_msg = (CHAT_MSG *)&comm_msg;
DisplayText("[받은 메시지] %s\r\n", chat_msg->buf);
}
else if (comm_msg.type == DRAWLINE) {
draw_msg = (DRAWLINE_MSG *)&comm_msg;
g_drawcolor = draw_msg->color;
SendMessage(g_hDrawWnd, WM_DRAWIT,
MAKEWPARAM(draw_msg->x0, draw_msg->y0),
MAKELPARAM(draw_msg->x1, draw_msg->y1));
}
}
return 0;
}
// 데이터 보내기
DWORD WINAPI WriteThread(LPVOID arg)
{
int retval;
// 서버와 데이터 통신
while (1) {
// 쓰기 완료 기다리기
WaitForSingleObject(g_hWriteEvent, INFINITE);
// 문자열 길이가 0이면 보내지 않음
if (strlen(g_chatmsg.buf) == 0) {
// '메시지 전송' 버튼 활성화
EnableWindow(g_hButtonSendMsg, TRUE);
// 읽기 완료 알리기
SetEvent(g_hReadEvent);
continue;
}
// 데이터 보내기
retval = send(g_sock, (char *)&g_chatmsg, BUFSIZE, 0);
if (retval == SOCKET_ERROR) {
break;
}
// '메시지 전송' 버튼 활성화
EnableWindow(g_hButtonSendMsg, TRUE);
// 읽기 완료 알리기
SetEvent(g_hReadEvent);
}
return 0;
}
// 자식 윈도우 프로시저
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hDC;
int cx, cy;
PAINTSTRUCT ps;
RECT rect;
HPEN hPen, hOldPen;
static HBITMAP hBitmap;
static HDC hDCMem;
static int x0, y0;
static int x1, y1;
static BOOL bDrawing = FALSE;
switch (uMsg) {
case WM_CREATE:
hDC = GetDC(hWnd);
// 화면을 저장할 비트맵 생성
cx = GetDeviceCaps(hDC, HORZRES);
cy = GetDeviceCaps(hDC, VERTRES);
hBitmap = CreateCompatibleBitmap(hDC, cx, cy);
// 메모리 DC 생성
hDCMem = CreateCompatibleDC(hDC);
// 비트맵 선택 후 메모리 DC 화면을 흰색으로 칠함
SelectObject(hDCMem, hBitmap);
SelectObject(hDCMem, GetStockObject(WHITE_BRUSH));
SelectObject(hDCMem, GetStockObject(WHITE_PEN));
Rectangle(hDCMem, 0, 0, cx, cy);
ReleaseDC(hWnd, hDC);
return 0;
case WM_LBUTTONDOWN:
x0 = LOWORD(lParam);
y0 = HIWORD(lParam);
bDrawing = TRUE;
return 0;
case WM_MOUSEMOVE:
if (bDrawing && g_bStart) {
x1 = LOWORD(lParam);
y1 = HIWORD(lParam);
// 선 그리기 메시지 보내기
g_drawmsg.x0 = x0;
g_drawmsg.y0 = y0;
g_drawmsg.x1 = x1;
g_drawmsg.y1 = y1;
send(g_sock, (char *)&g_drawmsg, BUFSIZE, 0);
x0 = x1;
y0 = y1;
}
return 0;
case WM_LBUTTONUP:
bDrawing = FALSE;
return 0;
case WM_DRAWIT:
hDC = GetDC(hWnd);
hPen = CreatePen(PS_SOLID, 3, g_drawcolor);
// 화면에 그리기
hOldPen = (HPEN)SelectObject(hDC, hPen);
MoveToEx(hDC, LOWORD(wParam), HIWORD(wParam), NULL);
LineTo(hDC, LOWORD(lParam), HIWORD(lParam));
SelectObject(hDC, hOldPen);
// 메모리 비트맵에 그리기
hOldPen = (HPEN)SelectObject(hDCMem, hPen);
MoveToEx(hDCMem, LOWORD(wParam), HIWORD(wParam), NULL);
LineTo(hDCMem, LOWORD(lParam), HIWORD(lParam));
SelectObject(hDC, hOldPen);
DeleteObject(hPen);
ReleaseDC(hWnd, hDC);
return 0;
case WM_PAINT:
hDC = BeginPaint(hWnd, &ps);
// 메모리 비트맵에 저장된 그림을 화면에 전송
GetClientRect(hWnd, &rect);
BitBlt(hDC, 0, 0, rect.right - rect.left,
rect.bottom - rect.top, hDCMem, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;
case WM_DESTROY:
DeleteObject(hBitmap);
DeleteDC(hDCMem);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
// 에디트 컨트롤에 문자열 출력
void DisplayText(const char *fmt, ...)
{
va_list arg;
va_start(arg, fmt);
char cbuf[1024];
vsprintf(cbuf, fmt, arg);
int nLength = GetWindowTextLength(g_hEditStatus);
SendMessage(g_hEditStatus, EM_SETSEL, nLength, nLength);
SendMessage(g_hEditStatus, EM_REPLACESEL, FALSE, (LPARAM)cbuf);
va_end(arg);
}
// 사용자 정의 데이터 수신 함수
int recvn(SOCKET s, char *buf, int len, int flags)
{
int received;
char *ptr = buf;
int left = len;
while (left > 0) {
received = recv(s, ptr, left, flags);
if (received == SOCKET_ERROR)
return SOCKET_ERROR;
else if (received == 0)
break;
left -= received;
ptr += received;
}
return (len - left);
}
// 소켓 함수 오류 출력 후 종료
void err_quit(const char *msg)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
MessageBox(NULL, (LPCTSTR)lpMsgBuf, msg, MB_ICONERROR);
LocalFree(lpMsgBuf);
exit(1);
}
// 소켓 함수 오류 출력
void err_display(const char *msg)
{
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, WSAGetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, NULL);
printf("[%s] %s", msg, (char *)lpMsgBuf);
LocalFree(lpMsgBuf);
}
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by PrjClient.rc
//
#define IDD_DIALOG1 101
#define IDC_ISIPV6 1001
#define IDC_IPADDR 1002
#define IDC_PORT 1003
#define IDC_CONNECT 1004
#define IDC_MSG 1005
#define IDC_STATUS 1006
#define IDC_SENDMSG 1007
#define IDC_COLORRED 1010
#define IDC_COLORGREEN 1011
#define IDC_COLORBLUE 1012
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 102
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1013
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
이파일이.. 오류는전부없어졌는데 실행을해도 콘솔창도안나오고.. 빌드는되는데 아무창도 뜨질않네요.. 비주얼스튜디오 2017입니다. 헤더파일도 cpp파일이랑 같은경로에 넣어놨습니다.
몇번 빌드했더니 이제 빌드했다고 밑에 창에 메시지도안나오고.. 멈추진않앗는데 아무동작을 안하네요 ㅜㅜㅜ
왜그런건가요
-
(•́ ✖ •̀)
알 수 없는 사용자 - 〉
댓글 입력