드디어 뭔가 통신같아 보이는 파트로 넘어왔다. 바로 코드부터 살펴보자.
DummyClient.cpp
#include "pch.h"
#include <iostream>
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
// 윈속 라이브러리 초기화 (ws2_32 라이브러리 초기화)
// 관련 정보가 wsaData에 채워짐
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
// af : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
// protocol : 0 (알아서 골라줌)
// return : descriptor
SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET) // 실패
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// 연결할 목적지 (IP주소 + Port)
SOCKADDR_IN serverAddr; // IPv4
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
//serverAddr.sin_addr.s_addr = ::inet_addr("127.0.0.1");
::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777); // 엔디안 이슈, 네트워크는 주로 빅 엔디안
if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Connect ErrorCode : " << errCode << endl;
return 0;
}
//----------------------------------
// 연결 성공, 데이터 송수신 가능
cout << "Connected to Server" << endl;
while (true)
{
// TODO
this_thread::sleep_for(1s);
}
//----------------------------------
// 소켓 리소스 반환
::closesocket(clientSocket);
// 윈속 종료, WSAStartup이 호출된 수만큼 실행해줘야 함
::WSACleanup();
}
임시로 만든 서버와의 통신을 위한 클라이언트의 코드이다. 클라이언트가 해야할 일은 통신하고자하는 서버의 주소와 포트를 알고 연결 요청을 보내는 것이다. 물론 프로토콜 등을 고려해야 하지만 큰 흐름은 이렇다.
Server.cpp
#include "pch.h"
#include "CorePch.h"
#include <Windows.h>
#include "ThreadManager.h"
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
// 윈속 라이브러리 초기화 (ws2_32 라이브러리 초기화)
// 관련 정보가 wsaData에 채워짐
WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return 0;
// af : Address Family (AF_INET = IPv4, AF_INET6 = IPv6)
// type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
// protocol : 0 (알아서 골라줌)
// return : descriptor
SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET) // 실패
{
int32 errCode = ::WSAGetLastError();
cout << "Socket ErrorCode : " << errCode << endl;
return 0;
}
// 나의 주소 (IP주소 + Port)
SOCKADDR_IN serverAddr; // IPv4
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = ::htonl(INADDR_ANY); // 알아서 해라
serverAddr.sin_port = ::htons(7777);
// 소켓과 서버 주소 연동
if (::bind(listenSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Bind ErrorCode : " << errCode << endl;
return 0;
}
// 연결 요청 받기 시작
if (::listen(listenSocket, 10) == SOCKET_ERROR)
{
int32 errCode = ::WSAGetLastError();
cout << "Listen ErrorCode : " << errCode << endl;
return 0;
}
//-------------------------------
while (true)
{
SOCKADDR_IN clientAddr; // IPv4
::memset(&clientAddr, 0, sizeof(clientAddr));
int32 addrLen = sizeof(clientAddr);
SOCKET clientSocket = ::accept(listenSocket, (SOCKADDR*)&clientAddr, &addrLen);
if (clientSocket == INVALID_SOCKET)
{
int32 errCode = ::WSAGetLastError();
cout << "Accept ErrorCode : " << errCode << endl;
return 0;
}
// 클라이언트 접속 완료
char ipAddress[16];
::inet_ntop(AF_INET, &clientAddr.sin_addr, ipAddress, sizeof(ipAddress));
cout << "Client Connected! IP : " << ipAddress << endl;
//TODO
}
//-------------------------------
// 윈속 종료, WSAStartup이 호출된 수만큼 실행해줘야 함
::WSACleanup();
}
서버는 먼저 자신의 주소와 소켓을 바인드 하고 클라이언트의 연결 요청을 받을 준비를 한 후 요청을 받기 시작한다.
정상적으로 동작하는 것을 볼 수 있다.
굉장히 많은 처음보는 함수들이 등장한다. 하지만 분야 특성상 함수단위로 이해할 필요는 없고 전체적인 흐름만 이해하면 된다고 한다. 만약 헷갈리거나 모르는 함수들이 나타나면 마이크로소프트 공식 문서를 활용하자.
'서버 > 네트워크' 카테고리의 다른 글
6. Select 모델 (0) | 2024.09.05 |
---|---|
5. 논블로킹(Non-Blocking) 소켓 (2) | 2024.09.03 |
4. 소켓 옵션 (3) | 2024.09.01 |
3. UDP 서버 (2) | 2024.08.30 |
2. TCP 서버 (0) | 2024.08.29 |