Chapter 3. 비동기 I/O 방식의 TCP/IP Echo 서버, 클라이언트
카테고리: Cpp Network
태그: Network Programming Cpp
최흥배님의 책 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;
}
🔔 전반적인 흐름 : 서버-클라이언트
🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄
댓글 남기기