윈도우 소켓을 이용하여 프로그램을 작성하기 위해서는 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로 초기화 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 메시지"), 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; 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; 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); } |
실행 결과]