Socket Programming

Date:     Updated:

카테고리:

태그:

인프런에 있는 루키스님의 게임 서버 강의를 듣고 정리한 내용입니다.


Client Side

1. Socket 초기화

#include <winsock2.h>
#include <mswsock.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    return 0;

// ...

::WSACleanup();
  • Window Socket API 초기화
  • Startup 함수로 wsaData의 버전과 Descriptor 명시
  • return 값으로 성공 여부 확인 가능
  • 사용이 끝나면 WSACleanUp 함수로 소켓 종료


2. Client Socket 생성

SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET)
{
    int32 errCode = ::WSAGetLastError();
    cout << "Socket ErrorCode : " << errCode << endl;
    return 0;
}

// ...

::closesocket(clientSocket);

thread19

  • af : Address Family (AF_INET = IPv4, AF_INET^ = IPv6)
  • type : TCP(SOCK_STREAM) vs UDP(SOCK_DGRAM)
  • protocol : 0
  • 사용 후 closesocket으로 소켓 리소스 반환


3. Socket 연결

// 소켓에 주소 연결
SOCKADDR_IN serverAddr;
::memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
::inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
serverAddr.sin_port = ::htons(7777);
// host to network short
// Little-Endian vs Big-Endian###
// ex) 0x12345678 4바이트 정수
// low [0x78][0x56][0x34][0x12] high < little
// low [0x12][0x34][0x56][0x78] high < big = network

if (::connect(clientSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
    int32 errorCode = ::WSAGetLastError();
    cout << "Connect ErrorCode : " << errorCode << endl;
    return 0;
}

// 연결 성공! 이제부터 데이터 송수신 가능
cout << "Connected To Server!" << endl;

while (true)
{
    // TODO
    this_thread::sleep_for(1s);
}
  • SOCKADDR_IN 구조체에 소켓의 목적지 성정 (IP주소 + 포트)
  • 이때 시스템마다 표기법 이슈(Endian)가 있어서 별도 표기법으로 사용 (라우터, 공유기, …)
    • inet_pton : Internet Presentation to Network
    • htons : Host to Network Short
  • connect 함수로 Address에 지정한 서버와 연결 요청


Server Side

Socket 초기화 & 생성

WSAData wsaData;
if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    return 0;

SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
if (listenSocket == INVALID_SOCKET)
{
    int32 errCode = ::WSAGetLastError();
    cout << "Socket ErrorCode : " << errCode << endl;
    return 0;
}

// ...

::WSACleanup();
  • 소켓 초기화, 생성은 Client와 동일
  • 일반적으로 서버에서 요청을 받는 소켓을 listener(listen socket)이라 함


Socket 연결

SOCKADDR_IN serverAddr;
::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;
}
  • client와 마찬가지로 SOCKADDR_IN을 이용해 소켓의 주소 설정
    • 서버에서는 목적지가 아닌 자신의 주소
  • serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);에서 INADDR_ANY 은 모든 IP를 허용
  • 서버 소켓에서는 connect가 아닌 bind 함수를 통해 소켓과 주소를 결합
  • listen 함수를 실행하면 이때부터 수신 가능. backlog값을 통해 대기열 요청 수 설정 가능
    • 대기 큐가 가득 찾을 때 추가로 들어오는 연결 요청은 거부 -> 클라에서 적절한 대응


while (true)
{
    SOCKADDR_IN clientAddr;
    ::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
}
  • accept 함수를 이용해 클라에서 요청이 들어온 소켓 받을 수 있음
  • 특정 아이피의 차단이 필요하다면 클라 소켓의 주소를 까보면 나옴
    • 표기법 문제가 마찬가지로 존재하기 때문에 inet_ntop 함수를 통해 받아와야 함


thread20



맨 위로 이동하기

Server 카테고리 내 다른 글 보러가기

댓글 남기기