네트워크프로그래밍 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파일이랑 같은경로에 넣어놨습니다.

몇번 빌드했더니 이제 빌드했다고 밑에 창에 메시지도안나오고.. 멈추진않앗는데 아무동작을 안하네요 ㅜㅜㅜ

왜그런건가요

  • 잘못했겠지요....본인이 작성한 건데 본인이 모르면 되겠어요? 몇번 빌드는 되었다는 이야기네요? 그럼 에러 원인이 나타날겁니다. 에러보고 원인을 찾아 수정하면 됩니다. 정영훈 2018.12.1 18:11
  • 안녕하세요 :-) Winsock 프로그래밍을 하시는 것같은데 원인 없는 문제는 없습니다. 코드가 잘못됬다면 명백히 컴파일 과정에서 신택스 에러를 낼 것입니다. 개발환경이나 설정 문제로 감히 추측해보지만 이런 현상은 사실 도움을 받기 어렵고 또 실제로 아주 간단한 문제인데 어처구니없이 헤매는 경우도 많죠... 문제의 원인을 당장에 콘솔탭에 뜨는 Error / Warning로 인식해 주먹구구식으로 없애고 사라지면 끝내는 것보단... 어떤 게 문제인지 근원부터 차근차근 고민해서 해결해보시는 게 좋을 듯 합니다. 알 수 없는 사용자 2018.12.1 22:30

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)