Asio:AsyncEcho

비동기 ECHO 서버 코드는 아래와 같다.

#include "config.h"

#include <iostream>
#include <algorithm>
#include <array>
#include <string>
#include <list>
#include <functional>

#include <boost/asio.hpp>

#define SERVER_IP    "127.0.0.1"
#define PORT_NUMBER  9999

#define CHAR_BUFFER_SIZE 128

class Session
{
private:
    boost::asio::ip::tcp::socket m_socket;
    std::string m_writeMessage;
    std::array<char, CHAR_BUFFER_SIZE> 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()
    {
        m_receiveBuffer.fill(0);
        m_socket.async_read_some(boost::asio::buffer(m_receiveBuffer),
                std::bind(&Session::handle_receive, this, std::placeholders::_1,
                        std::placeholders::_2));
    }

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

    void handle_receive(boost::system::error_code const & error, size_t bytes_transferred)
    {
        using namespace std;

        if (error) {
            if (error == boost::asio::error::eof) {
                cout << "Disconnect client.\n";
            } else {
                cout << "error no: " << error.value() << ", error message: " << error.message()
                        << endl;
            }
        } else {
            string const recv_message = m_receiveBuffer.data();
            cout << "Recv: " << recv_message << ", size: " << bytes_transferred << endl;

            m_writeMessage = "Re: ";
            m_writeMessage += recv_message;

            m_socket.async_write_some(boost::asio::buffer(m_writeMessage),
                    std::bind(&Session::handle_write, this, std::placeholders::_1,
                            std::placeholders::_2));

            PostReceive();
        }
    }
};

class TcpServer
{
private:
    int m_seqNumber;
    boost::asio::ip::tcp::acceptor m_acceptor;
    Session * m_session;

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

    virtual ~TcpServer()
    {
        if (m_session != nullptr) {
            delete m_session;
        }
    }

public:
    void StartAccept()
    {
        using namespace std;

        cout << "Wait for client...\n";
        m_session = new Session(m_acceptor.get_io_service());
        m_acceptor.async_accept(m_session->Socket(),
                std::bind(&TcpServer::handle_accept, this, m_session, std::placeholders::_1));
    }

    void handle_accept(Session * session, boost::system::error_code const & error)
    {
        using namespace std;
        if (!error) {
            cout << "Acept client.\n";
            session->PostReceive();
        }
    }

};

int main(int argc, char ** argv)
{
    using namespace std;

    cout << "echo-server\n";

    boost::asio::io_service io_service;
    TcpServer server(io_service);
    io_service.run();

    cout << "END...\n";

    return 0;
}

클라이언트 코드는 아래와 같다.

#include "config.h"

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

#define SERVER_IP    "127.0.0.1"
#define PORT_NUMBER  9999

#define CHAR_BUFFER_SIZE 128

class TcpClient
{
private:
    boost::asio::io_service & m_io_service;
    boost::asio::ip::tcp::socket m_socket;
    int m_seq_number;
    std::array<char, CHAR_BUFFER_SIZE> m_receiveBuffer;
    std::string m_writeMessage;

public:
    TcpClient(boost::asio::io_service & io_service) :
            m_io_service(io_service), m_socket(io_service), m_seq_number(0)
    {
    }

    virtual ~TcpClient()
    {
    }

public:
    void Connect(boost::asio::ip::tcp::endpoint & endpoint)
    {
        m_socket.async_connect(endpoint,
                std::bind(&TcpClient::handle_connect, this, std::placeholders::_1));
    }

    void PostWrite()
    {
        if (m_socket.is_open() == false) {
            return;
        }

        if (m_seq_number > 7) {
            m_socket.close();
            return;
        }

        ++m_seq_number;

        std::cin >> m_writeMessage;

        m_socket.async_write_some(boost::asio::buffer(m_writeMessage),
                std::bind(&TcpClient::handle_write, this, std::placeholders::_1,
                        std::placeholders::_2));

        PostReceive();
    }

    void PostReceive()
    {
        m_receiveBuffer.fill(0);

        m_socket.async_read_some(boost::asio::buffer(m_receiveBuffer),
                std::bind(&TcpClient::handle_receive, this, std::placeholders::_1,
                        std::placeholders::_2));
    }

public:
    void handle_connect(boost::system::error_code const & error)
    {
        using namespace std;
        if (error) {
            cout << "Connection fail." << error.value() << ", message: " << error.message() << endl;
        } else {
            cout << "Connection success.\n";
            PostWrite();
        }
    }

    void handle_write(boost::system::error_code const & error, size_t byte_transferred)
    {
    }

    void handle_receive(boost::system::error_code const & error, size_t byte_transferred)
    {
        using namespace std;

        if (error) {
            if (error == boost::asio::error::eof) {
                cout << "Disconnect server.\n";
            } else {
                cout << "error no: " << error.value() << ", error message: " << error.message()
                        << endl;
            }
        } else {
            string const recvMessage = m_receiveBuffer.data();
            cout << "RecvMessage: " << recvMessage << ", size: " << byte_transferred << endl;
            PostWrite();
        }
    }
};

int main(int argc, char ** argv)
{
    using namespace std;

    cout << "echo-client\n";

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

    TcpClient client(io_service);
    client.Connect(endpoint);
    io_service.run();

    cout << "END...\n";

    return 0;
}

MinGW를 사용한 컴파일은 아래와 같이 진행한다.

g++ -o async-echo-server.o -c async-echo-server.cpp -std=c++11 -DWINDOWS
g++ -o async-echo-server async-echo-server.o -lboost_system -lWS2_32 -lmswsock
g++ -o async-echo-client.o -c async-echo-client.cpp -std=c++11 -DWINDOWS
g++ -o async-echo-client async-echo-client.o -lboost_system -lWS2_32 -lmswsock