[C++] Session 다중 접속

2024. 4. 5. 01:03·Study/C++ & C#

이전에 에코서버를 만들어 봤었는데, 그건 세션 당 하나의 소켓만 처리할 수 있었다.

이걸 하나의 세션에서 여러 개의 소켓을 처리할 수 있게 수정했다.

하물며 채팅에서도 1:1 채팅만 하는 것은 아니지 않은가.

 

 

1. Server

Server 클래스 자체는 거의 원형을 유지하고 있다.

 

class Server {
public:
	Server(boost::asio::io_context& io_context, short port)
		: io_context_(io_context),
		acceptor_(io_context, tcp::endpoint(tcp::v4(), port)),
		session_(std::make_shared<Session>(io_context)) {
		DoAccept();
	}

private:
	boost::asio::io_context& io_context_;
	tcp::acceptor acceptor_;
	std::shared_ptr<Session> session_;

	void DoAccept() {
		acceptor_.async_accept([this](boost::system::error_code ec, tcp::socket socket) {
			if (!ec) {
				session_->AddSocket(std::move(socket));
			}
			DoAccept();
			});
	}
};

 

Session을 make_shared로 바로 넘겨주는 것이 아니라 클래스 내에 shared_ptr로 갖고 있게 해, 시각적으로 클래스가 이러한 포인터를 갖고 있음을 확인할 수 있게 했다.

 

 

2. Session

Session 클래스에 많은 수정이 있었다.

 

  1. 소켓을 갖고 있을 집합
  2. 소켓 전체에 뿌리기 위한 Broadcast 함수
  3. 경쟁 예방을 위한 mutex
  4. 에러 처리
  5. 소켓 정리

등의 기능이 추가됐다.

 

아래는 클래스 헤더이다.

 

class Session : public std::enable_shared_from_this<Session> {
public:
	explicit Session(boost::asio::io_context& io_context)
		: io_context_(io_context),
		data_{ 0 } {
	}

	void AddSocket(tcp::socket socket) {
		std::lock_guard<std::mutex> lock(sockets_mutex_);
		sockets_.push_back(std::make_shared<tcp::socket>(std::move(socket)));
		DoRead(sockets_.back());
	}

	int GetCount() {
		return sockets_.size();
	}

private:
	boost::asio::io_context& io_context_;
	std::vector<std::shared_ptr<tcp::socket>> sockets_;
	std::mutex sockets_mutex_;

	enum { kMaxLength = 1024 };
	std::array<char, kMaxLength> data_;

	void DoRead(std::shared_ptr<tcp::socket> socket);

	void Broadcast(std::size_t length, std::shared_ptr<tcp::socket> sender);

	void DoWrite(std::shared_ptr<tcp::socket> socket, std::size_t length);

	void HandleError(const boost::system::error_code& ec,
		std::shared_ptr<tcp::socket> socket);

	void CloseSocket(std::shared_ptr<tcp::socket> socket);
};

 

 

DoRead/Write에 추가된 건 새로 추가된 함수들로 예외처리를 한 것뿐이니 넘어가고...

새 함수들을 보자.

 

1. Broadcast()

void Broadcast(std::size_t length, std::shared_ptr<tcp::socket> sender) {
	std::lock_guard<std::mutex> lock(sockets_mutex_);
	for (auto& socket_ptr : sockets_) {
		if (socket_ptr != sender) {
			DoWrite(socket_ptr, length);
		}
	}
}

 

어떤 소켓으로부터 받은 데이터를 세션에 있는 전체 소켓에 뿌린다.

현재의 서버 코드는 여러 스레드에 의한 경합 상태가 벌어질 일이 전혀 없지만 mutex로 락을 걸어줬다.

미래엔 어떻게 코드를 바꿔나갈지 알 수 없고, 결국 다중 스레드를 활용하는 방향으로 갈 것이기 때문에 습관을 들이는 겸 해서 추가했다.

안일하게 있다가 문제가 발생하는 것 보단 낫지 않겠는가.

 

2. HandleError()

void HandleError(const boost::system::error_code& ec,
	std::shared_ptr<tcp::socket> socket) {
	if (ec == boost::asio::error::eof) {
		std::cout << "Successfully Disconnected\n";
	}
	else {
		std::cerr << "Disconnected with Error: " << ec.message() << "\n";
	}
	CloseSocket(socket);
}

 

클라이언트가 연결을 끊거나 비동기 읽기/쓰기 과정에서 문제가 있을 때 호출하도록 했다.

eof 이외의 에러들에 대해서 이유를 출력하고 문제가 생긴 소켓을 닫는다.

 

3. CloseSocket()

void CloseSocket(std::shared_ptr<tcp::socket> socket) {
	std::lock_guard<std::mutex> lock(sockets_mutex_);
	auto it = std::find_if(sockets_.begin(), sockets_.end(),
		[&socket](const std::shared_ptr<tcp::socket>& s) {
			return s == socket;
		});
	if (it != sockets_.end()) {
		socket->close();
		sockets_.erase(it);
	}
}

 

컨테이너를 순회하며 해당 소켓을 찾고 닫는다.

 

 

 

일단 이렇게 하나씩 추가하며 채팅 서버를 완성해 보려고 한다.

Sapphire에 사용된 boost.asio를 활용한 네트워크 처리를 이해하기 위함인데...

계속해 나가면 가능할 것이리라...

'Study > C++ & C#' 카테고리의 다른 글

[C#] ###Clicker 동작 개선  (0) 2024.05.05
[C++] ChatRoom 구현  (0) 2024.04.09
[C++] Boost.Asio 에코 서버  (0) 2024.03.29
[C#] ###Clicker 개선판  (0) 2024.03.18
[C#] 심플한 게임 런처  (0) 2024.03.06
'Study/C++ & C#' 카테고리의 다른 글
  • [C#] ###Clicker 동작 개선
  • [C++] ChatRoom 구현
  • [C++] Boost.Asio 에코 서버
  • [C#] ###Clicker 개선판
BVM
BVM
  • BVM
    E:\
    BVM
  • 전체
    오늘
    어제
    • 분류 전체보기 (173)
      • Thoughts (14)
      • Study (75)
        • Japanese (3)
        • C++ & C# (50)
        • Javascript (3)
        • Python (14)
        • Others (5)
      • Play (1)
        • Battlefield (1)
      • Others (10)
      • Camp (73)
        • T.I.L. (57)
        • Temp (1)
        • Standard (10)
        • Challenge (3)
        • Project (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • 본 블로그 개설의 목적
  • 인기 글

  • 태그

    discord py
    discord
    클라우드
    네트워크
    docker
    c#
    스타필드
    bot
    C++
    Network
    boost
    Asio
    서버
    FF14
    포인터
    Selenium
    7계층
    cloudtype
    Python
    프로그래머스
    Dalamud
    db
    암호화
    IOCP
    베데스다
    네트워크 프로그래밍
    Server
    로깅
    OSI
    JS
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
BVM
[C++] Session 다중 접속
상단으로

티스토리툴바