Chapter 3. 비동기 I/O 방식의 TCP/IP Echo 서버, 클라이언트

Date:     Updated:

카테고리:

태그:

최흥배님의 책 Boost.Asio를 사용한 네트워크 프로그래밍 을 공부하고 정리한 필기입니다. 😀

Chapter 3. 비동기 I/O 방식의 TCP/IP Echo 서버, 클라이언트 프로그램 만들기

🔔 비동기 I/O 프로그래밍의 특징

A 라는 작업을 요청하면 A 작업이 완료될때까지 기다리지 않고 다음 작업을 진행하다가 A 작업이 끝나면 다시 돌아와 A 의 다음 작업을 수행하는 방식이다.

  • 작업 흐름에 대기가 없이 신속하고 효율적으로 작업을 처리할 수 있다.
  • 비동기 기능을 사용하는 함수에는 앞에 async을 표기한다.
    async_땡땡 (요청 작업에 사용할 인수, 요청한 작업이 끝나면 호출할 함수 포인터)
    
    • 예를 들어 아래와 같은 경우 데이터를 읽는 async_read 함수는 비동기적으로 실행이 된다. async_read 함수를 처리하는데 Buffer 인수가 필요하며 (읽은 데이터를 여기에 담는다) 그리고 함수 실행이 끝나면 Session::OnRead 멤버 함수를 호출한다.
    • `async_read` 함수를 호출하면 이 함수 실행이 끝날 때 까지 기다리지 않고 다른 작업을 하러 간다. 다른 작업을 하던 도중 `async_read` 함수 작업이 완료되면 `Session::OnRead` 멤버 함수를 실행한다.
      async_read(Buffer, Session::OnRead)
      


🔔 서버

#include <SDKDDKVer.h>
#include <iostream>
#include <algorithm>
#include <string>
#include <list>
#include <boost/bind.hpp>
#include <boost/asio.hpp>



class Session
{
private:
	boost::asio::ip::tcp::socket m_Socket;
	std::string m_WriteMessage;
	std::array<char, 128> m_ReceiveBuffer;

public:
	Session(boost::asio::io_service& io_service)
		: m_Socket(io_service)
	{
	}

	boost::asio::ip::tcp::socket& Socket()
	{
		return m_Socket;
	}

	void PostReceive()
	{
		memset(&m_ReceiveBuffer, '\0', sizeof(m_ReceiveBuffer));

		m_Socket.async_read_some
		(boost::asio::buffer(m_ReceiveBuffer), boost::bind(&Session::handle_receive, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

	}


private:
	void handle_write(const boost::system::error_code& /*error*/, size_t /*bytes_transferred*/)
	{}

	void handle_receive(const boost::system::error_code& error, size_t bytes_transferred)
	{
		if (error)
		{
			if (error == boost::asio::error::eof)
				std::cout << "클라이언트와 연결이 끊어졌습니다" << std::endl;
			else
				std::cout << "error No: " << error.value() << " error Message: " << error.message() << std::endl;
		}
		else
		{
			const std::string strRecvMessage = m_ReceiveBuffer.data();
			std::cout << "클라이언트에서 받은 메시지: " << strRecvMessage << ", 받은 크기: " << bytes_transferred << std::endl;

			char szMessage[128] = { 0, };
			sprintf_s(szMessage, 128 - 1, "Re:%s", strRecvMessage.c_str());
			m_WriteMessage = szMessage;

			boost::asio::async_write(m_Socket, boost::asio::buffer(m_WriteMessage),boost::bind(&Session::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

			PostReceive();
		}
	}
};

const unsigned short PORT_NUMBER = 31400;

class TCP_Server
{
private:
	int m_nSeqNumber;
	boost::asio::ip::tcp::acceptor m_acceptor;
	boost::asio::io_service& m_io_service;
	Session* m_pSession;

public:
	TCP_Server(boost::asio::io_service& io_service)
		: m_io_service(io_service), m_acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), PORT_NUMBER))
	{
		m_pSession = nullptr;
		StartAccept();
	}

	~TCP_Server()
	{
		if (m_pSession != nullptr)
			delete m_pSession;
	}

private:
	void StartAccept()
	{
		std::cout << "클라이언트 접속 대기....." << std::endl;

		m_pSession = new Session(m_io_service);

		m_acceptor.async_accept(m_pSession->Socket(), boost::bind(&TCP_Server::handle_accept, this, m_pSession, boost::asio::placeholders::error));
	}

	void handle_accept(Session* pSession, const boost::system::error_code& error)
	{
		if (!error)
		{
			std::cout << "클라이언트 접속 성공" << std::endl;

			pSession->PostReceive();
		}
	}
};

int main()
{
	boost::asio::io_service io_service;

	TCP_Server server(io_service);

	io_service.run();


	std::cout << "네트웍 접속 종료" << std::endl;

	getchar();
	return 0;
}


🔔 클라이언트

#include <SDKDDKVer.h>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>


const char SERVER_IP[] = "127.0.0.1";
const unsigned short PORT_NUMBER = 31400;


class TCP_Client
{
private:
	boost::asio::io_service& m_io_service;
	boost::asio::ip::tcp::socket m_Socket;

	int m_nSeqNumber;
	std::array<char, 128> m_ReceiveBuffer;
	std::string m_WriteMessage;

public:
	TCP_Client(boost::asio::io_service& io_service)
		: m_io_service(io_service),
		m_Socket(io_service),
		m_nSeqNumber(0)
	{}

	void Connect(boost::asio::ip::tcp::endpoint& endpoint)
	{
		m_Socket.async_connect(endpoint,
			boost::bind(&TCP_Client::handle_connect, this, boost::asio::placeholders::error));
	}


private:
	void PostWrite()
	{
		if (m_Socket.is_open() == false)
			return;

		if (m_nSeqNumber > 7)
		{
			m_Socket.close();
			return;
		}


		++m_nSeqNumber;

		char szMessage[128] = { 0, };
		sprintf_s(szMessage, 128 - 1, "%d - Send Message", m_nSeqNumber);

		m_WriteMessage = szMessage;

		boost::asio::async_write(m_Socket, boost::asio::buffer(m_WriteMessage), boost::bind(&TCP_Client::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

		PostReceive();
	}

	void PostReceive()
	{
		memset(&m_ReceiveBuffer, '\0', sizeof(m_ReceiveBuffer));

		m_Socket.async_read_some(boost::asio::buffer(m_ReceiveBuffer), boost::bind(&TCP_Client::handle_receive, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));

	}


	void handle_connect(const boost::system::error_code& error)
	{
		if (error)
			std::cout << "connect failed : " << error.message() << std::endl;
		else
		{
			std::cout << "connected" << std::endl;

			PostWrite();
		}
	}

	void handle_write(const boost::system::error_code& /*error*/, size_t /*bytes_transferred*/)
	{}

	void handle_receive(const boost::system::error_code& error, size_t bytes_transferred)
	{
		if (error)
		{
			if (error == boost::asio::error::eof)
				std::cout << "서버와 연결이 끊어졌습니다" << std::endl;
			else
				std::cout << "error No: " << error.value() << " error Message: " << error.message() << std::endl;
		}
		else
		{
			const std::string strRecvMessage = m_ReceiveBuffer.data();
			std::cout << "서버에서 받은 메시지: " << strRecvMessage << ", 받은 크기: " << bytes_transferred << std::endl;

			PostWrite();
		}
	}
};



int main()
{
	boost::asio::io_service io_service;

	boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(SERVER_IP), PORT_NUMBER);

	TCP_Client client(io_service);

	client.Connect(endpoint);

	io_service.run();


	std::cout << "네트웍 접속 종료" << std::endl;

	getchar();
	return 0;
}


🔔 전반적인 흐름 : 서버-클라이언트

image



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

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

댓글 남기기