동기화된 교체형 TCP 서버 구현

1. 관련 정의

  • 교체형 서버: 서버는 매번 클라이언트만 처리하고 클라이언트의 요청을 완전히 처리해야만 다음 클라이언트를 처리한다
  • 동기화: 해당 작업이 완료되거나 오류가 발생할 때까지 중단된 스레드에서 수행되는 입출력 및 제어 작업을 사용합니다
  • 2. 기본 프로세스

  • acceptor 플러그인을 할당하고 이를 특정한 TCP 포트에 연결합니다.
  • 서버가 중지될 때까지 다음 순환을 수행합니다.
  • 클라이언트의 연결 요청 대기 (connect)
  • 클라이언트의 요청이 도착했을 때 수락(accept)
  • 클라이언트가 보낸 요청 정보를 기다립니다
  • 요청 정보 읽기
  • 요청 정보 처리
  • 응답 정보를 클라이언트에게 반환(처리됨)
  • 클라이언트와의 연결을 닫고 관련 소켓을 제거

  • 3. 소스 코드 구현

    //Compile: g++ -std=c++11 -o server Server.cc -lboost_system -lpthread 
    
    #include <boost/asio.hpp>
    
    #include <thread>
    #include <atomic>
    #include <memory>
    #include <iostream>
    
    using namespace boost;
    
    class Service {
     public:
      Service() = default;
    
      void HandleClient(asio::ip::tcp::socket &sock) {
        try {
          asio::streambuf request;
          asio::read_until(sock, request, '
    '
    ); // Emulate request processing int i = 0; while (i != 1000000) ++i; std::this_thread::sleep_for(std::chrono::milliseconds(500)); // Sending response std::string response = "Response
    "
    ; asio::write(sock, asio::buffer(response)); } catch (const system::system_error &e) { std::cout << "Error occured! Error code = " << e.code() << ". Message: " << e.what(); } } }; class Acceptor { public: Acceptor(asio::io_service &ios, unsigned short port_num) : ios_(ios), acceptor_(ios_, asio::ip::tcp::endpoint( asio::ip::address_v4::any(), port_num)) { acceptor_.listen(); } void Accept() { asio::ip::tcp::socket sock(ios_); acceptor_.accept(sock); Service srv; srv.HandleClient(sock); } private: asio::io_service &ios_; asio::ip::tcp::acceptor acceptor_; }; class Server { public: Server() : stop_(false) { } void Start(unsigned short port_num) { thread_.reset(new std::thread([this, port_num] { Run(port_num); })); } void Stop() { stop_.store(true); thread_->join(); } private: void Run(unsigned short port_num) { Acceptor acc(ios_, port_num); while (!stop_.load()) { acc.Accept(); } } std::unique_ptr<std::thread> thread_; std::atomic<bool> stop_; asio::io_service ios_; }; int main() { unsigned short port_num = 2333; try { Server srv; srv.Start(port_num); std::this_thread::sleep_for(std::chrono::seconds(600)); srv.Stop(); } catch (const system::system_error &e) { std::cout << "Error occured! Error code = " << e.code() << ". Message: " << e.what(); } return 0; }

    4. 결함 및 해결 방안

  • 서버 클래스에서 Stop () 메서드를 호출하면 마지막 연결 요청이 올 때까지 서버가 계속 막힐 수 있습니다.(E.g. 서버의 스레드가 acc.Accept () 에 막히면 Stop () 메서드를 호출하면 서버는 이 차단 작업이 끝날 때까지 기다리며 멈추지 않습니다.)
    솔루션:
  • Stop() 메서드에서 가상 액티브 연결을 생성하여 가상 요청을 전송할 수 있음
  • 특수한 클라이언트를 만들고 특수한 서비스 종료 메시지를 보냅니다(E.g.stop
  • 서버는 클라이언트에게 악의적인 공격을 받기 쉽다(E.g. 클라이언트는 연결만 하고 요청을 보내지 않으면 서버가 계속 막힐 것이다)
    솔루션:
  • 비차단 플러그인 사용하기 (우리 서버를reactive 모드로 전환할 것)
  • 비동기식 I/O 사용
  • 좋은 웹페이지 즐겨찾기