본문 바로가기

프로그램언어/C++

윈도우 소켓1

윈도우 소켓을 이용하여 프로그램을 작성하기 위해서는 ws2_32.lib 라이브러리를 추가해야한다.

 

WSAData 구조체 : 윈도우 소켓 초기화 정보를 가지고 있는 구조체

struct WSAData {

    WORD                wVersion;                     // 윈도우 소켓의 버전

    WORD                wHighVersion;               // 지원되는 소켓의 상위 버전으로 wVersion과 일치

    char                   szDescription[WSADESCRIPTION_LEN+1];     // NULL로 끝나는 아스키 스트링 값, 윈속 설명

    char                   szSystemStatus[WSASYS_STATUS_LEN+1]; // NULL로 끝나는 아스키 스트링 값. 상태 문자열

    unsigned short     iMaxSockets;                // 사용할 소켓의 최대 소켓 수, version 2부터는 무시

    unsigned short     iMaxUdpDg;                 //  전송할 수 있는 데이터 그램의 최대 크기, version 2부터는 무시

    char FAR *           lpVendorInfo;               //벤더 정보( 의미 없음)
};

 

sockaddr_in 구조체 : 소켓을 연결할 대상의 주소를 쓰는 구조체

struct sockaddr_in { 
    short                sin_family;         // ipv4 에서 사용할 주소 체계: AF_INET 
    u_short             sin_port;           
// 포트 번호
    struct in_addr    sin_addr;           // 32 비트 IP 주소
    char                sin_zero[8];         // 전체 크기를 16 비트로 맞추기 위한 dummy, 사용되지 않음( 0 ) 
};

struct in_addr {
    u_long             s_addr;               // 32비트 IP 주소를 저장 할 구조체
};

 

 

소켓 통신을 위한 함수

 

WSAStartup() 함수 : 원속 사용 시작하기

int WSAStartup(
    WORD             wVersionRequired,      // 사용할 윈도우 소켓 버전 
    LPWSADATA     lpWSAData               // 소켓 정보 저장 공간
)
;

 

socket() 함수 : 소켓 생성하기

SOCKET WSAAPI socket (

    int         af,               // address family를 명시, AF_INET 이용

    int         type,            // 소켓의 유형으로 SOCK_STREAM

    int         protocol       // 0 입력
);

 

bind() 함수 : 주소와 소켓 연결하기

int bind(
    SOCKET                      s,                           // 연결할 소켓
    const SOCKADDR*        ipSockAddr,             // 소켓에 지정할 주소와 포트 번호를 포함하는 ,SOCKADDR의 크기
    int                             nSockAddrLen         // 주소를 저장하는 구조체 SOCKADDR의 크기
);

 

listen() 함수 : 연결 요구 기다리기

BOOL listen(
    SOCKET        s,                                   // 기다릴 소켓
    int               nConnectionBacklog          // 대기할 수 있는 요구 최대 개수로 1 ~ 5
);

 

connect() 함수 : 연결하기

BOOL connect(
    SOCKET                   s,                    // 연결에 사용할 소켓
    const SOCKADDR*     lpSockAddr,       // 상대의 주소와 포트 번호
    int                          nSockAddrLen    // 주소를 저장하는 구조체 SOCKADDR의 크기 
);

 

accept() 함수 : 연결 요구 받아들이기

SOCKET accept(
    SOCKET            s,                       // 소켓에 대한 요구를 받아들임
    SOCKADDR*      lpSockAddr          // 접속하는 상대방의 주소를 저장할 SOCKADDR 구조체 주소
    int*                  lpSockAddrLen     // 주소를 저장하는 구조체 SOCKADDR의 크기
);

 

closesocket() 함수 : 소켓과 관련된 리소스 해제하기

closesocket(
    SOCKET    s           // 닫을 소켓
);

 

WSACleanup() 함수 : 원속 사용 끝내기

int WSACleanup();

 

 

소켓 통신 프로그램 작성하기(서버)

#include <windows.h>

#include <TCHAR.H>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)

{

    WSADATA wsadata;

    SOCKET s;

    SOCKADDR_IN addr = { 0 };

    WSAStartup(MAKEWORD(2, 2), &wsadata);    // 윈속 2.2로 초기화
// MAKEWORD(2, 2)는 WORD타입을 만들 때 사용하는 매크로로, 여기서는 원속의 버전2.2를 지정하기 위해 사용

    s = socket(AF_INET, SOCK_STREAM, 0);         // 소켓을 만듬

    addr.sin_family = AF_INET;                         // IPv4 주소체제

    addr.sin_port = 20;                                   // 포트번호

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");  // ip 주소

    bind(s,(LPSOCKADDR)&addr, sizeof(addr));      // 만들어진 주소를 소켓에 부여

    if (listen(s, 5) == -1)                   // 소켓이 클라이언트의 접속을 기다림

        return 0;

    do

    {

        SOCKADDR_IN c_addr;                              // ipv4 주소체제

        int size = sizeof(c_addr);

        accept(s, (LPSOCKADDR)&c_addr, &size);       // 클라이언트의 접속을 기다림

    } while (MessageBox(NULL, _T("클라이언트 접속을 확인했습니다. 서버를 종료하시겠습니까?"), _T("Server 메시지"),
        MB_YESNO) == IDNO);

    closesocket(s);                                             // 소켓 해제

    WSACleanup();                                            // 윈속 종료

    return 1;

}

실행결과]

프로그램을 실행하면 서버가 작동중인 상태로 됨

아래 소스코드를 작성하여 클라이언트 프로그램을 실행해 보자.

 

클라이언트 소켓 통신 프로그램

#include <windows.h>

#include <TCHAR.H>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)

{

    WSADATA wsadata;

    SOCKET s;

    SOCKADDR_IN addr = { 0 };

    WSAStartup(MAKEWORD(2, 2), &wsadata);    // 윈속 2.2로 초기화

    s = socket(AF_INET, SOCK_STREAM, 0);         // 소켓을 만듬

    addr.sin_family = AF_INET;

    addr.sin_port = 20;

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    connect(s, (LPSOCKADDR)&addr, sizeof(addr));   // 소켓을 통해 만들어진 주소에 접속 시도

    closesocket(s);                                            // 소켓 해제

    WSACleanup();                                           // 원속 종료

    return 0;

}

실행결과]

 

 

send() 함수 : 메시지 전송하기

int send(
    SOCKET        s,                 // 연결된 소켓
    const void*    lpBuf,           // 보낼 메시지가 저장된 버퍼
    int               nBufLen,        // 메시지의 길이
    int               nFlags           // 0을 이용
);

 

recv() 함수 : 메시지 수신하기

int recv(
    SOCKET        s,                 // 연결된 소켓
    void*           lpBuf,            // 받는 메시지가 저장될 버퍼
    int               nBufLen,        // 메시지의 길이
    int               nFlags           // 0을 이용
);

 

 

멀티바이트에서 유니코드로 변환

int MultiByteToWideChar(
    _In_           UINT        CodePage,         // ANSI 문자열에 대한 언어, CP_ACP(시스템 기본 Windows ANSI 코드 페이지) 
    _In_           DWORD    dwFlags,           // 사용하지 않음, 0
    _In_           LPCSTR     lpMultiByteStr,   // 변환하려는 멀티바이트 문자열
    _In_           int           cbMultiByte,      // 문자열의 길이, -14이면 함수가 자동 계산
    _Out_opt_   LPWSTR    lpWideCharStr,  // 유니코드로 변환 후 저장될 공간
    _In_           int           cchWideChar    // 유니코드 문자열의 길이
)
;

예]

int msgLen;
char buffer[] = "멀티바이트 문자열";
msgLen = MultiByteToWideChar(CP_ACP, 0, buffer, strlen(buffer), NULL, NULL);   // 멀티바이트 변화없음

WCHAR wbuffer[100];

MultiByteToWideChar(CP_ACP, 0, buffer, strlen(buffer), wbuffer, msgLen);   // 유니코드 문자열로 반환

 

 

유니코드에서 멀티바이트로 변환

int WideCharToMultiByte(

    _In_          UINT          uCodePage,      // ANSI 문자열에 대한 언어, CP_ACP(시스템 기본 Windows ANSI 코드 페이지)

    _In_          DWORD      dwFlags,           // 사용하지 않음, 0

    _In_          PCWSTR      pWideCharStr,   // 변환하려는 유니코드 문자열

    _In_          int             cchWideChar,     // 문자열의 길이, -1이면 함수가 자동 계산

    _Out_ipt_   LPSTR         lpMultiByteStr,   // 멀티바이트로 변환 후 저장될 공간

    _In_          int             cchMultiByte,      // 변환 후 문자열의 길이

    _Out_ipt_   LPCSTR       pDefaultChar,     // 디폴트 캐릭터, NULL

    _Out_ipt_   LPBOOL      pUsedDefaultChar  // 플래그를 격납하는 주소, NULL

);

예]

int msgLen;
WCHAR str[] = _T("유니코드 문자열");
msgLen = WideCharToMultiByte(CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); 

char buffer[100];

WideCharToMultiByte(CP_ACP, 0, str, -1, buffer, msgLen, NULL, NULL);     // 멀티바이트로 변환

 

 

메시지 수신 프로그램 작성(서버)

#include <windows.h>

#include <TCHAR.H>

#include <stdio.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)

{

    WSADATA wsadata;

    SOCKET s;

    TCHAR message[300];

    SOCKADDR_IN addr = { 0 };

    WSAStartup(MAKEWORD(2, 2), &wsadata);

    s = socket(AF_INET, SOCK_STREAM, 0);

    addr.sin_family = AF_INET;

    addr.sin_port = 20;

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    bind(s,(LPSOCKADDR)&addr, sizeof(addr));

    if (listen(s, 5) == -1)

        return 0;

    do

    {

        SOCKADDR_IN c_addr;

        char buffer[100];

#ifdef _UNICODE

        TCHAR wbuffer[100];

#endif

        int msgLen;

        int size = sizeof(c_addr);

        SOCKET cs = accept(s, (LPSOCKADDR)&c_addr, &size);

        msgLen = recv(cs, buffer, 100, 0);

        buffer[msgLen] = NULL;

#ifdef _UNICODE

        msgLen = MultiByteToWideChar(CP_ACP, 0, buffer, strlen(buffer), NULL, NULL);

        MultiByteToWideChar(CP_ACP, 0, buffer, strlen(buffer), wbuffer, msgLen);

        wbuffer[msgLen] = NULL;

        _stprintf_s(message, _T("클라이언트 메시지: %s, 서버를 종료하시겠습니까?"), wbuffer);

#else

        sprintf_s(message, _T("클라이언트 메시지: %s, 서버를 종료하시겠습니까?"), buffer);

#endif

    } while (MessageBox(NULL, message, _T("Server 메시지"), MB_YESNO) == IDNO);

    closesocket(s);

    WSACleanup();

    return 1;

}

 

메시지 수신 프로그램 작성(클라이언트)

#include <windows.h>

#include <TCHAR.H>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)

{

    WSADATA wsadata;

    SOCKET s;

    SOCKADDR_IN addr = { 0 };

    WSAStartup(MAKEWORD(2, 2), &wsadata);

    s = socket(AF_INET, SOCK_STREAM, 0);

    addr.sin_family = AF_INET;

    addr.sin_port = 20;

    addr.sin_addr.s_addr = inet_addr("127.0.0.1");

    if(connect(s, (LPSOCKADDR)&addr, sizeof(addr)) == -1)

        return 0;

    send(s, "안녕하세요 Server!", 19, 0);

    closesocket(s);

    WSACleanup();

    return 1;

}

실행 결과]

 

메시지 수신 윈도우 프로그램 작성하기(서버)

#include <windows.h>

#include <TCHAR.H>

#include <stdio.h>

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)

{

    HWND hwnd;

    MSG msg;

    WNDCLASS WndClass;

    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 = _T("Window Class Name");

    RegisterClass(&WndClass);

    hwnd = CreateWindow(_T("Window Class Name"), _T("Server Window"), WS_OVERLAPPEDWINDOW,

        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );

    ShowWindow(hwnd, nCmdShow);

    UpdateWindow(hwnd);

    while(GetMessage(&msg, NULL, 0, 0))

    {

        TranslateMessage(&msg);

        DispatchMessage(&msg);

    }

    return (int)msg.wParam;

}

 

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

    HDC hdc;

    PAINTSTRUCT ps;

    static WSADATA wsadata;

    static SOCKET s, cs;

    static TCHAR msg[200];

    static SOCKADDR_IN addr = { 0 }, c_addr;

    int size, msgLen;

    char buffer[100];

 

    switch (iMsg)

    {

    case WM_CREATE:

        WSAStartup(MAKEWORD(2, 2), &wsadata);

        s = socket(AF_INET, SOCK_STREAM, 0);

        addr.sin_family = AF_INET;

        addr.sin_port = 20;

        addr.sin_addr.s_addr = inet_addr("127.0.0.1");

        bind(s,(LPSOCKADDR)&addr, sizeof(addr));

        if (listen(s, 5) == -1) return 0;

        size = sizeof(c_addr);

        do

        {

             cs = accept(s, (LPSOCKADDR)&c_addr, &size);

        } while (cs == INVALID_SOCKET);

        msgLen = recv(cs, buffer, 100, 0);

        buffer[msgLen] = NULL;

#ifdef _UNICODE

        msgLen = MultiByteToWideChar(CP_ACP, 0, buffer, strlen(buffer), NULL, NULL);

        MultiByteToWideChar(CP_ACP, 0, buffer, strlen(buffer), msg, msgLen);

        msg[msgLen] = NULL;

#else

        strcpy_s(msg, buffer);

#endif

         break;

    case WM_PAINT:

        hdc = BeginPaint(hwnd, &ps);

        TextOut(hdc, 0, 0, msg, (int)_tcslen(msg));

        EndPaint(hwnd, &ps);

        break;

    case WM_DESTROY:

        closesocket(s);

        WSACleanup();

        PostQuitMessage(0);

        break;

    }

    return DefWindowProc (hwnd, iMsg, wParam, lParam);

}

 

메시지 수신 윈도우 프로그램 작성하기(클라이언트)

#include <windows.h>

#include <TCHAR.H>

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)

{

    HWND hwnd;

    MSG msg;

    WNDCLASSWndClass;

    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 = _T("Window Class Name");

    RegisterClass(&WndClass);

    hwnd = CreateWindow(_T("Window Class Name"), _T("Client Window"), WS_OVERLAPPEDWINDOW,

        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );

    ShowWindow(hwnd, nCmdShow);

    UpdateWindow(hwnd);

    while(GetMessage(&msg, NULL, 0, 0))

    {

        TranslateMessage(&msg);

         DispatchMessage(&msg);

    }

    return (int)msg.wParam;

}

 

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

    static WSADATA wsadata;

    static SOCKET s;

    static SOCKADDR_IN addr = { 0 };

 

    switch (iMsg)

    {

    case WM_CREATE:

        WSAStartup(MAKEWORD(2, 2), &wsadata);

        s = socket(AF_INET, SOCK_STREAM, 0);

        addr.sin_family = AF_INET;

        addr.sin_port = 20;

        addr.sin_addr.s_addr = inet_addr("127.0.0.1");

        if(connect(s, (LPSOCKADDR)&addr, sizeof(addr)) == -1)

            return 0;

        break;

    case WM_KEYDOWN:

        send(s, "안녕 Server!", 13, 0);

        break;

    case WM_DESTROY:

        closesocket(s);

        WSACleanup();

        PostQuitMessage (0) ;

        break;

    }

    return DefWindowProc (hwnd, iMsg, wParam, lParam);

}

실행 결과]

 

'프로그램언어 > C++' 카테고리의 다른 글

스레드  (0) 2021.01.14
윈도우 소켓2  (0) 2021.01.13
파일 입출력2  (0) 2021.01.11
파일 입출력1  (0) 2021.01.05
컨트롤 윈도우 사용하기  (0) 2021.01.05