C++/C++ 적용 예제

C++ Sleep() 및 SetTimer() 함수를 사용하여 TCP/IP 네트워크에서 안정적인 시간(Time) 지정 통신 구현을 알아보자

쉽코딩 2023. 3. 12.

 

 

 

 

 

 

 

 

프로그램 작업에는 여러 가지 이유로 sleep() 및 settimer()와 같은 컨트롤이 필요합니다.

1. Timing control : 경우에 따라 프로그램은 다음 작업을 실행하기 전에 일정 시간 동안 대기해야 합니다. 예를 들어 메인 메뉴를 표시하기 전에 몇 초 동안 시작 화면을 표시하는 프로그램입니다.

2. Asynchronous execution : 경우에 따라 프로그램은 여러 작업을 동시에 또는 특정 순서로 실행해야 합니다. 예를 들어 백그라운드에서 장기 실행 작업을 수행하는 동안 진행률 표시줄을 업데이트하는 프로그램입니다.

3. User experience : 경우에 따라 프로그램은 사용자가 메시지나 프롬프트를 읽거나 반응할 수 있도록 일정 시간을 제공해야 합니다. 예를 들어 자동으로 닫히기 전에 몇 초 동안 메시지를 표시하는 프로그램입니다.

4. Resource management : 경우에 따라 프로그램은 리소스를 사용하기 전에 리소스를 사용할 수 있을 때까지 기다려야 합니다. 예를 들어 쿼리를 실행하기 전에 데이터베이스 연결이 설정되기를 기다리는 프로그램입니다.

전반적으로 sleep() 및 settimer()와 같은 컨트롤을 사용하면 프로그램이 실행 흐름을 조절하여 보다 유연하고 응답성이 뛰어나며 사용자 친화적(user-friendly)으로 만들 수 있습니다.

 

sleep() 및 settimer()는 프로그램의 흐름을 제어하기 위해 C++에서 사용되는 두 가지 함수입니다.

 

Sleep() function이란?

sleep()은 unistd.h 헤더 파일에 정의된 함수이며 지정된 시간(초) 동안 프로그램 실행을 일시 중지하는 데 사용됩니다. sleep() 구문은 다음과 같습니다.

#include <unistd.h>

unsigned int sleep(unsigned int seconds);

 

함수의 인수는 프로그램을 일시 중지할 시간(초)입니다. 이 지정된 시간이 지나면 프로그램은 일시 중지된 지점부터 실행을 계속합니다.

 

SetTimer() function이란?

반면에 settimer()는 표준 C++ 함수가 아닙니다. 프로그래머가 구현한 사용자 지정 함수이거나 타사 라이브러리의 함수일 수 있습니다. settimer()의 동작과 구문은 구현 방법에 따라 다릅니다.

그러나 일반적으로 "타이머"라는 용어는 지정된 시간이 경과한 후 프로그램이 특정 작업을 실행할 수 있도록 하는 메커니즘을 의미합니다. settimer() 함수는 지정된 시간이 지난 후 이벤트를 트리거하는 타이머를 설정하는 데 사용됩니다.

 

앞서 언급했듯이 settimer()는 표준 C++ 함수가 아니며 구현은 라이브러리 또는 사용자 정의 구현에 따라 다를 수 있습니다. 그러나 다음은 chrono 및 스레드 라이브러리를 사용하여 C++에서 settimer() 함수를 구현하는 방법에 대한 가능한 예입니다.

#include <iostream>
#include <chrono>
#include <thread>

void settimer(int seconds) {
  std::cout << "Timer started for " << seconds << " seconds." << std::endl;
  std::chrono::seconds duration(seconds);
  std::this_thread::sleep_for(duration);
  std::cout << "Time's up!" << std::endl;
}

int main() {
  settimer(5);
  std::cout << "Main function continues to execute." << std::endl;
  return 0;
}

이 예제에서 settimer() 함수는 프로그램을 일시 중지할 시간(초)을 지정하는 정수 인수 초를 사용합니다. chrono 라이브러리는 초의 기간을 정의하는 데 사용되며 스레드 라이브러리는 지정된 기간 동안 sleep_for()하는 데 사용됩니다.

프로그램이 실행되면 타이머가 시작되고 프로그램이 5초 동안 일시 중지된 다음 "Time's up!" 메시지가 표시됩니다. 주 함수는 계속 실행되고 "주 함수가 계속 실행됩니다."라는 메시지가 표시됩니다.

 

 

Sleep()과 SetTimer의 차이점

sleep()과 settimer()의 주요 차이점은 sleep()은 지정된 시간 동안 전체 프로그램을 일시 중지하는 반면 settimer()는 지정된 시간이 지난 후 이벤트를 트리거하는 타이머를 설정하여 타이머가 실행되는 동안 다른 작업을 계속 실행하는 프로그램입니다. 두 함수 사이의 한 가지 유사점은 둘 다 지연을 도입하여 프로그램의 흐름을 제어할 수 있다는 것입니다. 그러나 이러한 지연을 도입하는 방식은 다릅니다.

 

 

SetTimer를 적용한 TCP/IP 통신 네트워크 → SetTimer를 통해 Client를 1000ms으로 호출

 

▣ 소켓 오류 핸들링

SOCKET_ERROR는 일부 소켓 함수에 의해 반환된 오류 코드를 나타내는 C++의 Winsock2 라이브러리에 있는 매크로 상수입니다.

Winsock2 라이브러리는 네트워크 응용 프로그램에서 소켓을 생성, 구성 및 사용하는 데 사용되는 소켓, 바인드, 청취, 수락, 전송 및 recv와 같은 여러 소켓 기능을 제공합니다. 이러한 함수 중 하나에서 오류가 발생하면 SOCKET_ERROR 상수를 반환하여 오류를 나타냅니다.

예를 들어 소켓에서 데이터를 받기 위해 recv 함수를 호출할 때 데이터 수신 중 오류가 발생하면 이 함수는 SOCKET_ERROR를 반환합니다. 특정 오류를 확인하기 위해 코드는 WSAGetLastError 함수를 사용하여 오류와 관련된 오류 코드를 검색할 수 있습니다.

다음은 Winsock2 라이브러리를 사용하여 C++ 프로그램에서 SOCKET_ERROR를 처리하는 방법의 예입니다.

int recv_result = recv(hClientSocket, (char*)&ServerRequest, PACKET_SIZE, 0);
if (recv_result == SOCKET_ERROR) {
    int error_code = WSAGetLastError();
    cout << "Error receiving data from client. Error code: " << error_code << endl;
    closesocket(hClientSocket);
    continue;
}

이 예제에서 코드는 recv 함수를 호출하여 클라이언트로부터 데이터를 수신하고 그 결과를 recv_result 변수에 저장합니다. 함수가 SOCKET_ERROR를 반환하면 코드는 WSAGetLastError 함수를 호출하여 오류 코드를 검색하고 콘솔에 인쇄합니다. 그런 다음 소켓이 닫히고 코드는 루프의 다음 반복으로 계속됩니다.

 

 

더보기

[참고]

 

Define 된 PORT와 SERVER_IP는 본인의 환경설정에 맞게 입력해야 됩니다.

 

▶ PORT : 프로그램의 고유 입력 번호라고 생각하면 쉽습니다. 그렇기 때문에 임의의 숫자로 입력하면 됩니다. (6000번대 이상의 값을 입력하는 것을 추천합니다.)

 

▶ SERVER_IP : 각 WiFi 또는 랜선에 따른 IP가 부여됩니다. CMD 창에서 ipconfig를 통해 현재 사용하고 있는 IP를 확인할 수 있습니다. https://easycode.tistory.com/19 포스팅  [목차] TCP/IP 네트워크 구성 클라이언트(client) 기초 지식 → It's like taking a restaurant order에서 자세히 확인할 수 있습니다.

 

 

 

[Server Part] 

한 줄 요약 : 소켓 오류 핸들링 SOCKET_ERROR + While()

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>
#include<iostream>
#include<string>
#include<sstream>
#include<fstream>
#include<windows.h>

#pragma comment (lib, "Ws2_32.lib")
#define PORT 8000
#define PACKET_SIZE 1000
#define SERVER_IP "xxx.xxx.xxx.xxx" // 서버 아이피
#define CAMERA_BUFFER_Value2 100
#define MAX_RETRIES 5
#define RETRY_INTERVAL 1000

using namespace std;

typedef struct _EXAMPLE_SEND_PACKET
{
	int32_t Counter;
	char Value1[16];
	int32_t Value2;
	int32_t Value3;
	int32_t Value4;
}_EXAMPLE_SEND_PACKET;

typedef struct _EXAMPLE_RECV_PACKET {
	int32_t Counter;
	bool Value5;
	bool Value6;
	bool Value7;
	bool Value8;
}_EXAMPLE_RECV_PACKET;

struct _EXAMPLE_SEND_PACKET clientRecv;
struct _EXAMPLE_RECV_PACKET ServerRequest;

int main(int argc, char* argv[], char* envp[])
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	SOCKET hServerSocket;
	hServerSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

	SOCKADDR_IN tAddr = {};
	tAddr.sin_family = AF_INET;
	tAddr.sin_port = htons(PORT);
	tAddr.sin_addr.s_addr = htonl(INADDR_ANY);

	int bind_result = bind(hServerSocket, (SOCKADDR*)&tAddr, sizeof(tAddr));
	if (bind_result == SOCKET_ERROR) {
		// Handle error
		cout << "Error binding socket" << endl;
		closesocket(hServerSocket);
		return 1;
	}

	int listen_result = listen(hServerSocket, 5);
	if (listen_result == SOCKET_ERROR) {
		// Handle error
		cout << "Error listening on socket" << endl;
		closesocket(hServerSocket);
		return 1;
	}

	while (true) {
		SOCKET hClientSocket;
		SOCKADDR_IN clientAddr;
		int clientAddrSize = sizeof(clientAddr);

		hClientSocket = accept(hServerSocket, (SOCKADDR*)&clientAddr, &clientAddrSize);

		if (hClientSocket == INVALID_SOCKET) {
			// Handle error
			cout << "Error accepting connection" << endl;
			closesocket(hServerSocket);
			return 1;
		}

		int recv_result = recv(hClientSocket, (char*)&ServerRequest, PACKET_SIZE, 0);
		if (recv_result == SOCKET_ERROR) {
			// Handle error
			cout << "Error receiving data from client" << endl;
			closesocket(hClientSocket);
			continue;
		}

		printf("----------------------------------------------------------------\n");
		printf("-------------------------- Server Part -------------------------\n");
		printf("----------------------------------------------------------------\n");
		printf("Server_RecvData Counter : %d\n", ServerRequest.Counter);
		printf("Server_RecvData Value5 : %d\n", ServerRequest.Value5);
		printf("Server_RecvData Value6 : %d\n", ServerRequest.Value6);
		printf("Server_RecvData Value7 : %d\n", ServerRequest.Value7);
		printf("Server_RecvData Value8 : %d\n", ServerRequest.Value8);

		clientRecv.Counter = clientRecv.Counter + 1;
		strcpy(clientRecv.Value1, "TEST GOOD");
		clientRecv.Value2 = 123456;
		clientRecv.Value3 = 654321;
		clientRecv.Value4 = 1;

		int send_result = send(hClientSocket, (char*)&clientRecv, sizeof(struct _EXAMPLE_SEND_PACKET), 0);
		if (send_result == SOCKET_ERROR) {
			// Handle error
			cout << "Error sending data to client" << endl;
			closesocket(hClientSocket);
			continue;
		}

		closesocket(hClientSocket);
	}

	closesocket(hServerSocket);
	WSACleanup();

	return 0;
}

 

[코드 설명]

이 코드는 Winsock2 라이브러리를 사용하여 C++에서 TCP 서버를 간단하게 구현한 것입니다. 서버는 포트 8000에서 들어오는 연결을 수신 대기하고, 연결된 클라이언트로부터 데이터를 수신하고, 수신된 데이터를 처리하고, 응답을 다시 클라이언트로 보냅니다. → while()

 

그리고 소켓 Error처리 구문이 포함되어 있습니다. → SOCKET_ERROR


포함 및 전처리기 지시문: 코드에는 stdio.h, stdlib.h, winsock2.h, iostream, string, sstream, fstream 및 windows.h와 같은 여러 헤더 파일이 포함되어 있습니다. #pragma 주석 지시문은 Winsock2 라이브러리를 프로젝트에 연결하는 데 사용됩니다. #define 전처리기 지시문은 포트 번호, 패킷 크기, 서버 IP 및 기타 값과 같은 상수를 정의하는 데 사용됩니다.

 구조체 정의: 두 개의 구조체 _EXAMPLE_SEND_PACKET 및 _EXAMPLE_RECV_PACKET이 정의되어 있으며 서버에서 보내고 받는 데이터를 저장하는 데 사용됩니다.

▶ Winsock2 Initialization : 코드는 WSAStartup 함수를 호출하여 Winsock2를 초기화하고 사용할 버전을 설정합니다.

 소켓 생성 : 코드는 소켓 함수를 호출하여 서버용 소켓을 생성합니다. 소켓은 IP 프로토콜(IPPROTO_TCP)을 사용하는 TCP 소켓(SOCK_STREAM)으로 정의됩니다.

 소켓 Binding : 코드는 bind 함수를 호출하여 소켓을 특정 IP 주소 및 포트에 바인딩합니다. 이 경우 주소는 INADDR_ANY이고 포트는 8000입니다.

 소켓 Listening : 코드는 소켓에서 들어오는 연결을 청취하기 위해 청취 함수를 호출합니다. 두 번째 인수는 소켓에 대기할 수 있는 최대 연결 수를 지정합니다.

 Connection Acceptance: 코드는 수락 함수를 호출하여 소켓에서 들어오는 연결을 수락합니다. 이 기능은 연결될 때까지 프로그램 실행을 차단합니다.

 데이터 Recieve : 코드는 recv 함수를 호출하여 연결된 클라이언트로부터 데이터를 수신합니다. 수신된 데이터는 ServerRequest 구조체에 저장됩니다.

 데이터 Processing : 수신된 데이터는 콘솔에 값을 인쇄하여 처리됩니다.

 데이터 Sending : 코드는 send 함수를 호출하여 응답을 클라이언트에 다시 보냅니다. 보낼 데이터는 clientRecv 구조체에 저장됩니다.

 Cleanup : 이 코드는 소켓을 닫기 위해 closesocket 함수를 호출하고 Winsock2 라이브러리를 정리하기 위해 WSACleanup 함수를 호출합니다.

주요 기능 반환: 코드는 프로그램의 성공적인 실행을 나타내기 위해 0을 반환합니다.

 

더보기

Client Part - 1 & 2 둘 중에 한 가지를 선택해서 사용하시면 됩니다. 

두 가지 모두 동일한 기능을 가지고 있으며,

SetTimer() + While()

"OR"

소켓 오류 핸들링 SOCKET_ERROR +SetTimer()+ While()

와 같이 오류 처리에 관한 차이만 있을 뿐입니다.

 

 

더보기

[참고]

 

Define 된 PORT와 SERVER_IP는 본인의 환경설정에 맞게 입력해야 됩니다.

 

▶ PORT : 프로그램의 고유 입력 번호라고 생각하면 쉽습니다. 그렇기 때문에 임의의 숫자로 입력하면 됩니다. (6000번대 이상의 값을 입력하는 것을 추천합니다.)

 

▶ SERVER_IP : 각 WiFi 또는 랜선에 따른 IP가 부여됩니다. CMD 창에서 ipconfig를 통해 현재 사용하고 있는 IP를 확인할 수 있습니다. https://easycode.tistory.com/19 포스팅  [목차] TCP/IP 네트워크 구성 클라이언트(client) 기초 지식 → It's like taking a restaurant order에서 자세히 확인할 수 있습니다.

 

[Client Part - 1] 

한 줄 요약 : SetTimer() + While()

#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>
#include<iostream>
#include<string>
#include<sstream>
#include<fstream>
#include<windows.h>

#pragma comment (lib, "Ws2_32.lib")
#define PORT 8000
#define PACKET_SIZE 1000
#define SERVER_IP "xxx.xxx.xxx.xxx" // 서버 아이피
#define CAMERA_BUFFER_Value2 100
#define TIMER_ID 1

using namespace std;

typedef struct _EXAMPLE_SEND_PACKET
{
	int32_t Counter;
	char Value1[16];
	int32_t Value2;
	int32_t Value3;
	int32_t Value4;
}_EXAMPLE_SEND_PACKET;

typedef struct _EXAMPLE_RECV_PACKET 
{
	int32_t Counter;
	bool Value5;
	bool Value6;
	bool Value7;
	bool Value8;
}_EXAMPLE_RECV_PACKET;

struct _EXAMPLE_SEND_PACKET clientRecv;
struct _EXAMPLE_RECV_PACKET ServerRequest;

void TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	SOCKET hSocket;
	hSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

	SOCKADDR_IN tAddr = {};
	tAddr.sin_family = AF_INET;
	tAddr.sin_port = htons(PORT);
	tAddr.sin_addr.s_addr = inet_addr(SERVER_IP);

	ServerRequest.Counter = ServerRequest.Counter + 1;
	ServerRequest.Value5 = TRUE;
	ServerRequest.Value6 = TRUE;
	ServerRequest.Value7 = TRUE;
	ServerRequest.Value8 = TRUE;

	connect(hSocket, (SOCKADDR*)&tAddr, sizeof(tAddr));
	send(hSocket, (char*)&ServerRequest, sizeof(struct _EXAMPLE_RECV_PACKET), 0);
	recv(hSocket, (char*)&clientRecv, PACKET_SIZE, 0);

	printf("----------------------------------------------------------------\n");
	printf("-------------------------- Client Part -------------------------\n");
	printf("----------------------------------------------------------------\n");
	printf("Client_RecvData Counter : %d\n", clientRecv.Counter);
	printf("Client_RecvData Value1 : %s\n", clientRecv.Value1);
	printf("Client_RecvData Value2 : %d\n", clientRecv.Value2);
	printf("Client_RecvData Value3 : %d\n", clientRecv.Value3);
	printf("Client_RecvData Value4 : %d\n", clientRecv.Value4);

}

int main(int argc, char* argv[], char* envp[])
{
	ServerRequest.Counter = 0;
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	SOCKET hSocket;
	hSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

	SOCKADDR_IN tAddr = {};
	tAddr.sin_family = AF_INET;
	tAddr.sin_port = htons(PORT);
	tAddr.sin_addr.s_addr = inet_addr(SERVER_IP);

	SetTimer(NULL, TIMER_ID, 1000, (TIMERPROC)TimerProc);

	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	closesocket;
}

[코드 설명]

이 코드는 Winsock2 라이브러리를 사용하여 클라이언트-서버 통신을 구현하는 C++ 프로그램입니다. 프로그램의 클라이언트 부분은 매 초마다 서버에 요청을 보내고 응답을 받는 반면, 서버 부분은 들어오는 연결을 듣고 요청을 받을 때마다 클라이언트에 응답을 보냅니다.

_EXAMPLE_SEND_PACKET 및 _EXAMPLE_RECV_PACKET 구조는 클라이언트와 서버 간에 송수신되는 패킷을 나타내는 데 사용됩니다. 카운터, 부울 값 및 문자 배열과 같은 다양한 값을 포함합니다.

TimerProc 함수는 1초마다 호출되는 타이머 콜백 함수입니다. Winsock2 라이브러리를 초기화하고 소켓을 만들고 SERVER_IP 상수로 지정된 IP 주소로 서버에 연결합니다. 그런 다음 서버에 요청을 보내고 응답을 받아 받은 값을 콘솔에 인쇄합니다.

main 함수는 Winsock2 라이브러리를 초기화하고, 소켓을 생성하고, SetTimer 함수를 사용하여 타이머를 설정합니다. 타이머 ID는 TIMER_ID이고 타이머 간격은 1000밀리초(1초)로 설정됩니다. TimerProc 함수는 타이머 콜백 함수로 지정됩니다.

그런 다음 기본 기능은 GetMessage 및 DispatchMessage 기능을 사용하여 메시지 루프에 들어갑니다. 메시지 루프는 운영 체제의 메시지를 기다렸다가 적절한 핸들러로 보냅니다. 프로그램은 사용자가 창을 닫거나 GetMessage 함수가 0을 반환할 때까지 계속 실행됩니다. 마지막으로 소켓은 closesocket 함수를 사용하여 닫힙니다.

 

 

[Client Part - 2] 

한 줄 요약 : 소켓 오류 핸들링 SOCKET_ERROR + SetTimer() + While()

#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>
#include<iostream>
#include<string>
#include<sstream>
#include<fstream>
#include<windows.h>

#pragma comment (lib, "Ws2_32.lib")
#define PORT 8000
#define PACKET_SIZE 1000
#define SERVER_IP "xxx.xxx.xxx.xxx" // 서버 아이피
#define CAMERA_BUFFER_Value2 100
#define TIMER_ID 1
#define MAX_RETRIES 5
#define RETRY_INTERVAL 1000

using namespace std;

typedef struct _EXAMPLE_SEND_PACKET
{
	int32_t Counter;
	char Value1[16];
	int32_t Value2;
	int32_t Value3;
	int32_t Value4;
}_EXAMPLE_SEND_PACKET;

typedef struct _EXAMPLE_RECV_PACKET
{
	int32_t Counter;
	bool Value5;
	bool Value6;
	bool Value7;
	bool Value8;
}_EXAMPLE_RECV_PACKET;

struct _EXAMPLE_SEND_PACKET clientRecv;
struct _EXAMPLE_RECV_PACKET ServerRequest;

void TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	SOCKET hSocket;
	hSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

	SOCKADDR_IN tAddr = {};
	tAddr.sin_family = AF_INET;
	tAddr.sin_port = htons(PORT);
	tAddr.sin_addr.s_addr = inet_addr(SERVER_IP);

	int retries = 0;
	int connect_result = SOCKET_ERROR;

	while (connect_result == SOCKET_ERROR && retries < MAX_RETRIES) {
		connect_result = connect(hSocket, (SOCKADDR*)&tAddr, sizeof(tAddr));
		if (connect_result == SOCKET_ERROR) {
			retries++;
			Sleep(RETRY_INTERVAL);
		}
		else {}
	}

	if (connect_result == SOCKET_ERROR) {
		// Handle error
		cout << "Error connecting to server" << endl;
		closesocket(hSocket);
		return;
	}

	ServerRequest.Counter = ServerRequest.Counter + 1;
	ServerRequest.Value5 = TRUE;
	ServerRequest.Value6 = TRUE;
	ServerRequest.Value7 = TRUE;
	ServerRequest.Value8 = TRUE;

	int send_result = send(hSocket, (char*)&ServerRequest, sizeof(struct _EXAMPLE_RECV_PACKET), 0);
	if (send_result == SOCKET_ERROR) {
		// Handle error
		cout << "Error sending data to server" << endl;
		closesocket(hSocket);
		return;
	}

	int recv_result = recv(hSocket, (char*)&clientRecv, PACKET_SIZE, 0);
	if (recv_result == SOCKET_ERROR) {
		// Handle error
		cout << "Error receiving data from server" << endl;
		closesocket(hSocket);
		return;
	}

	printf("----------------------------------------------------------------\n");
	printf("-------------------------- Client Part -------------------------\n");
	printf("----------------------------------------------------------------\n");
	printf("Client_RecvData Counter : %d\n", clientRecv.Counter);
	printf("Client_RecvData Value1 : %s\n", clientRecv.Value1);
	printf("Client_RecvData Value2 : %d\n", clientRecv.Value2);
	printf("Client_RecvData Value3 : %d\n", clientRecv.Value3);
	printf("Client_RecvData Value4 : %d\n", clientRecv.Value4);
	printf("----------------------------------------------------------------\n");

	closesocket(hSocket);
}

int main(int argc, char* argv[], char* envp[])
{
	ServerRequest.Counter = 0;
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	SetTimer(NULL, TIMER_ID, 1000, (TIMERPROC)TimerProc);

	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return 0;
}

[코드 설명]

이 코드는 C++로 작성된 Windows 소켓 클라이언트 프로그램입니다. Winsock API를 사용하여 서버와의 연결을 설정하고 데이터를 교환합니다.

코드는 winsock2.h, iostream 및 windows.h와 같은 여러 헤더 파일을 포함하여 시작합니다. 헤더 winsock2.h는 Winsock API에 대한 액세스를 제공하는 기본 Winsock 헤더 파일입니다. 헤더 windows.h는 SetTimer 및 GetMessage와 같은 Windows API 함수에 사용됩니다.

그런 다음 코드는 포트 번호, 패킷 크기 및 서버 IP 주소와 같은 여러 상수를 정의합니다. _EXAMPLE_SEND_PACKET 및 _EXAMPLE_RECV_PACKET의 두 가지 구조도 정의되어 있습니다. 이러한 구조는 서버와 주고받을 데이터를 저장하는 데 사용됩니다.

TimerProc 함수는 1초마다 호출되는 타이머 콜백 함수입니다. Winsock을 초기화하고 소켓을 생성하는 것으로 시작합니다. 그런 다음 연결 기능을 사용하여 서버에 연결합니다. 연결이 실패하면 코드는 중간에 휴면 간격을 두고 서버에 몇 번 연결을 다시 시도합니다. 지정된 재시도 횟수 후에도 여전히 연결에 실패하면 오류 메시지가 표시되고 함수가 반환됩니다.

연결이 설정된 후 코드는 ServerRequest 구조를 데이터로 채우고 send 함수를 사용하여 서버로 보냅니다. 그런 다음 recv 함수를 사용하여 서버에서 데이터를 수신하고 clientRecv 구조에 저장합니다. 수신된 데이터는 콘솔에 인쇄됩니다.

주요 기능은 Winsock 초기화, 타이머 생성, 메시지 루프(loop) 입력으로 시작됩니다. 타이머는 1초마다 TimerProc 함수를 호출합니다. 메시지 루프(loop)는 Windows 메시지 및 이벤트를 처리하는 데 사용됩니다.

이 코드에는 소켓 함수 connect, send 및 recv에 대한 오류 처리가 포함되어 있습니다. 이러한 기능 중 하나라도 실패하면 오류 메시지가 표시되고 소켓이 닫힙니다. 이 코드는 또한 더 이상 필요하지 않을 때 소켓을 닫습니다.

 

[실행 결과]

[Server Part]와 [Client Part - 2] 실행 결과

댓글