소켓 기초 사용하기(TCP)

강보석·2024년 3월 26일

서버 만들기

//GameServer.cpp
#include <WinSock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

int main() {
	WSADATA wsaData;
    if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        return 0;
    }

    SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == SOCKET_ERROR) {
        int32 errorCode = ::WSAGetLastError();
        cout << "Error Code : " << errorCode << endl;
        return 0;
    }

    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 errorCode = ::WSAGetLastError();
        cout << "Error Code : " << errorCode << endl;
        return 0;
    }

    if (::listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
        int32 errorCode = ::WSAGetLastError();
        cout << "Error Code : " << errorCode << endl;
        return 0;
    }

    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 errorCode = ::WSAGetLastError();
          cout << "Error Code : " << errorCode << endl;
          return 0;
      }

      char ipAddress[16];
      ::inet_ntop(AF_INET, &clientAddr.sin_addr, ipAddress, sizeof(ipAddress));
      cout << "Client Connected! IP = " << ipAddress << endl;
    }
}
//DummyClient.cpp
#include <winsock2.h>
#include <MSWSock.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")

int main() {
	this_thread::sleep_for(1s);
	WSADATA wsaData;
    if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        return 0;
    }

    SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == SOCKET_ERROR) {
        int32 errorCode = ::WSAGetLastError();
        cout << "Error Code : " << errorCode << endl;
        return 0;
    }

    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);

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

  	cout << "Connected to Server!" << endl;
}

Recv, Send

//GameServer.cpp
int main() {
	WSADATA wsaData;
    if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        return 0;
    }

    SOCKET listenSocket = ::socket(AF_INET, SOCK_STREAM, 0);
    if (listenSocket == SOCKET_ERROR) {
        int32 errorCode = ::WSAGetLastError();
        cout << "Error Code : " << errorCode << endl;
        return 0;
    }

    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 errorCode = ::WSAGetLastError();
        cout << "Error Code : " << errorCode << endl;
        return 0;
    }

    if (::listen(listenSocket, SOMAXCONN) == SOCKET_ERROR) {
        int32 errorCode = ::WSAGetLastError();
        cout << "Error Code : " << errorCode << endl;
        return 0;
    }

    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 errorCode = ::WSAGetLastError();
            cout << "Error Code : " << errorCode << endl;
            return 0;
        }

        char ipAddress[16];
        ::inet_ntop(AF_INET, &clientAddr.sin_addr, ipAddress, sizeof(ipAddress));
        cout << "Client Connected! IP = " << ipAddress << endl;

        while (true) {
            char recvBuffer[1000];

            int32 recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
            if (recvLen <= 0) {
                int32 errorCode = ::WSAGetLastError();
                cout << "Recv Error : " << errorCode << endl;
                return 0;
            }

            cout << "Recv Data! Data = " << recvBuffer << endl;
            cout << "Recv Data! Len = " << recvLen << endl;

            int32 resultCode = ::send(clientSocket, recvBuffer, recvLen, 0);
            if (resultCode == SOCKET_ERROR) {
                int32 errorCode = ::WSAGetLastError();
                cout << "Send Error : " << errorCode << endl;
                return 0;
            }
        }
    }

    ::WSACleanup();
}
//DummyClient.cpp
int main() {
	this_thread::sleep_for(1s);
    WSADATA wsaData;
    if (::WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
        return 0;
    }

    SOCKET clientSocket = ::socket(AF_INET, SOCK_STREAM, 0);
    if (clientSocket == SOCKET_ERROR) {
        int32 errorCode = ::WSAGetLastError();
        cout << "Error Code : " << errorCode << endl;
        return 0;
    }

    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);

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

    cout << "Connected to Server!" << endl;

    while (true) {
        char sendBuffer[100] = "Hello World!";

        int32 resultCode = ::send(clientSocket, sendBuffer, sizeof(sendBuffer), 0);
        if (resultCode == SOCKET_ERROR) {
            int32 errorCode = ::WSAGetLastError();
            cout << "Send Error : " << errorCode << endl;
            return 0;
        }

        cout << "Send Data! Len = " << sizeof(sendBuffer) << endl;

        char recvBuffer[1000];
        int32 recvLen = ::recv(clientSocket, recvBuffer, sizeof(recvBuffer), 0);
        if (recvLen <= 0) {
            int32 errorCode = ::WSAGetLastError();
            cout << "Recv Error : " << errorCode << endl;
            return 0;
        }
        cout << "Recv Data! Data = " << recvBuffer << endl;
        cout << "Recv Data! Len = " << recvLen << endl;

        this_thread::sleep_for(1s);
    }

    ::WSACleanup();
}

작동원리

  1. 서버가 소켓을 생성합니다. 그리고 서버 본인의 주소를 소켓에 Bind합니다. 그 후 Listen을 통해 접속할 클라이언트를 기다려줍니다.
  2. 클라이언트는 본인의 소켓을 만들고 접속할 서버의 주소에 Connect를 합니다.
  3. 클라이언트가 Connect를 하면 서버는 accept 함수를 통해 접속한 클라이언트와 상호작용을 할 전용 클라이언트용 소켓을 만듭니다.
  4. 클라이언트용 소켓은 접속하는 클라이언트가 늘어날수록 똑같이 늘어나며 1:1 대응하기에 앞으로 해당 소켓을 통해 클라이언트와 Recv, Send 등을 할 수 있습니다.

Little Endian, Big Endian

0x12345678을 저장할 때 Little Endian인지 Big Endian인지에 따라 저장하는 방식이 다릅니다
낮은 주소 [0x78][0x56][0x34][0x12] 높은 주소 -> Little Endian
낮은 주소 [0x12][0x34][0x56][0x78] 높은 주소 -> Big Endian

저장하는 방식이 다르기 때문에 만약 어떤 방식으로 데이터를 읽을지 제대로 지정해주지 않으면 의도한 데이터를 읽지 못합니다.

보통 인텔 CPU를 사용할 경우는 Little Endian 방식을 사용합니다.
그러나 네트워크에서는 보통 Big Endian을 사용하기 때문에

htons()  //host to network
ptons()  //protocol to network

해당 명령어로 어떤 방식을 사용할지 지정해줍니다.

TCP vs UDP

추가적으로 TCP와 UDP에 대해 알아봅시다.

TCP

  1. 연결할 때 논리적 통로를 만들어 지속적으로 상호작용한다.
  2. 지속적으로 연결한 상태이기 때문에 상대방의 상황을 고려하여 데이터를 보냄. 그렇기 때문에 패킷의 순서를 보장하고 손실이 일어나지 않는다.
  3. 다만 보낸 패킷간에 경계가 존재하지 않는다. -> 따로 처리하지 않으면 데이터를 합쳐 사용할 수도 있음.
    => 만약 recv를 할 때 패킷들이 쌓여있다면 한꺼번에 데이터를 받아들일 것입니다.

UDP

  1. 따로 연결이라는 개념이 존재하지 않는다.
  2. 연결이라는 개념이 없기에 상대방의 상황을 고려하지 않고 일단 데이터를 보냄. 그러는 과정에서 패킷의 순서가 섞일 수 있고 손실이 일어날 수 있음.
  3. 패킷간의 경계가 존재한다.
  4. 기본적으로 아무한테나 데이터를 주고받을 수 있어 보안이 좋지 않을 수 있다.
profile
안녕하세요. 컴퓨터를 공부하는 학생입니다.

0개의 댓글