본문 바로가기
-----ETC-----/C++ 네트워크 프로그래밍 시리즈

[C++ 네트워크 프로그래밍] Day 2: 소켓 프로그래밍 기초 (TCP)

by cogito21_cpp 2024. 8. 1.
반응형

소켓 프로그래밍 기초

소켓 프로그래밍은 네트워크 통신을 위해 소켓을 사용하는 프로그래밍 기법입니다. 소켓은 네트워크 통신의 끝점을 나타내는 추상화된 개념으로, IP 주소와 포트 번호로 식별됩니다.

 

TCP 소켓 프로그래밍

TCP(Transmission Control Protocol)는 신뢰성 있는 데이터 전송을 보장하는 프로토콜입니다. 연결 지향적이며, 데이터의 순서와 무결성을 보장합니다. TCP 소켓 프로그래밍에서는 클라이언트와 서버 간의 연결을 설정하고, 데이터를 주고받는 과정을 구현합니다.

 

TCP 소켓 프로그래밍의 주요 함수

소켓 생성

  • socket() 함수: 소켓을 생성합니다.
    • domain: 주소 체계 (예: AF_INET - IPv4)
    • type: 소켓 타입 (예: SOCK_STREAM - TCP)
    • protocol: 프로토콜 (일반적으로 0으로 설정)
  • int socket(int domain, int type, int protocol);

서버 측 소켓 프로그래밍

  • bind() 함수: 소켓에 IP 주소와 포트 번호를 바인딩합니다.
  • int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • listen() 함수: 소켓을 수신 대기 상태로 설정합니다.
  • int listen(int sockfd, int backlog);
  • accept() 함수: 클라이언트 연결을 수락합니다.
  • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
  • send() 함수: 데이터를 전송합니다.
  • ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • recv() 함수: 데이터를 수신합니다.
  • ssize_t recv(int sockfd, void *buf, size_t len, int flags);

클라이언트 측 소켓 프로그래밍

  • connect() 함수: 서버에 연결을 요청합니다.
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

TCP 서버 구현

TCP 서버 코드 예제

#include <iostream>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main() {
    try {
        // io_context 객체 생성
        boost::asio::io_context io_context;

        // TCP 수신자 생성, 포트 12345에서 수신 대기
        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 12345));

        std::cout << "서버가 포트 12345에서 대기 중입니다..." << std::endl;

        while (true) {
            // 새로운 소켓 객체 생성
            tcp::socket socket(io_context);

            // 클라이언트 연결 수락
            acceptor.accept(socket);

            // 클라이언트에게 보낼 메시지
            std::string message = "서버에서 보낸 메시지입니다!";
            boost::system::error_code ignored_error;

            // 클라이언트에게 메시지 전송
            boost::asio::write(socket, boost::asio::buffer(message), ignored_error);
        }
    } catch (std::exception& e) {
        std::cerr << "예외 발생: " << e.what() << std::endl;
    }

    return 0;
}

 

TCP 클라이언트 구현

TCP 클라이언트 코드 예제

#include <iostream>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main() {
    try {
        // io_context 객체 생성
        boost::asio::io_context io_context;

        // TCP 리졸버 생성
        tcp::resolver resolver(io_context);
        tcp::resolver::results_type endpoints = resolver.resolve("127.0.0.1", "12345");

        // TCP 소켓 생성
        tcp::socket socket(io_context);

        // 서버에 연결 요청
        boost::asio::connect(socket, endpoints);

        // 서버로부터 메시지 수신
        for (;;) {
            boost::asio::streambuf buf;
            boost::asio::read_until(socket, buf, "\n");

            std::istream is(&buf);
            std::string message;
            std::getline(is, message);
            std::cout << "서버로부터 수신한 메시지: " << message << std::endl;
        }
    } catch (std::exception& e) {
        std::cerr << "예외 발생: " << e.what() << std::endl;
    }

    return 0;
}

 

실습 문제

문제 1: 간단한 에코 서버와 클라이언트 작성하기

TCP 에코 서버와 클라이언트를 작성하여, 클라이언트가 보낸 메시지를 서버가 다시 클라이언트에게 보내도록 구현하세요.

 

해설:

TCP 에코 서버

#include <iostream>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main() {
    try {
        // io_context 객체 생성
        boost::asio::io_context io_context;

        // TCP 수신자 생성, 포트 12345에서 수신 대기
        tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), 12345));

        std::cout << "서버가 포트 12345에서 대기 중입니다..." << std::endl;

        while (true) {
            // 새로운 소켓 객체 생성
            tcp::socket socket(io_context);

            // 클라이언트 연결 수락
            acceptor.accept(socket);

            // 클라이언트로부터 데이터 수신
            boost::asio::streambuf buf;
            boost::asio::read_until(socket, buf, "\n");

            std::istream is(&buf);
            std::string message;
            std::getline(is, message);
            std::cout << "수신한 메시지: " << message << std::endl;

            // 클라이언트에게 수신한 메시지 그대로 전송 (에코)
            boost::asio::write(socket, boost::asio::buffer(message + "\n"));
        }
    } catch (std::exception& e) {
        std::cerr << "예외 발생: " << e.what() << std::endl;
    }

    return 0;
}

 

TCP 에코 클라이언트

#include <iostream>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

int main() {
    try {
        // io_context 객체 생성
        boost::asio::io_context io_context;

        // TCP 리졸버 생성
        tcp::resolver resolver(io_context);
        tcp::resolver::results_type endpoints = resolver.resolve("127.0.0.1", "12345");

        // TCP 소켓 생성
        tcp::socket socket(io_context);

        // 서버에 연결 요청
        boost::asio::connect(socket, endpoints);

        // 사용자로부터 입력받은 메시지를 서버로 전송하고, 에코된 메시지를 수신
        std::string message;
        while (std::getline(std::cin, message)) {
            message += "\n";
            boost::asio::write(socket, boost::asio::buffer(message));

            boost::asio::streambuf buf;
            boost::asio::read_until(socket, buf, "\n");

            std::istream is(&buf);
            std::string reply;
            std::getline(is, reply);
            std::cout << "서버로부터 에코된 메시지: " << reply << std::endl;
        }
    } catch (std::exception& e) {
        std::cerr << "예외 발생: " << e.what() << std::endl;
    }

    return 0;
}

 

설명

위의 코드는 간단한 TCP 에코 서버와 클라이언트를 구현한 예제입니다. 클라이언트는 사용자가 입력한 메시지를 서버로 전송하고, 서버는 수신한 메시지를 다시 클라이언트로 전송합니다.

  • TCP 에코 서버:
    • 서버는 tcp::acceptor를 사용하여 포트 12345에서 연결을 수락합니다.
    • 클라이언트로부터 데이터를 수신한 후, 동일한 데이터를 다시 클라이언트로 전송합니다.
  • TCP 에코 클라이언트:
    • 클라이언트는 서버에 연결을 요청하고, 사용자로부터 입력받은 메시지를 서버로 전송합니다.
    • 서버로부터 에코된 메시지를 수신하여 출력합니다.

이제 두 번째 날의 학습을 마쳤습니다. TCP 소켓 프로그래밍의 기본 개념과 구현 방법을 학습했습니다.

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "소켓 프로그래밍 기초 (UDP)"에 대해 학습하겠습니다.

반응형