1. 程式人生 > >async_tcp_echo_server.cpp

async_tcp_echo_server.cpp

//echo伺服器是一個非常簡單的服務,並且在實際中沒有太多用處
//但echo服務相對在網路除錯程式時,可能存在用處,本文是boost.asio
//的示例程式,經過“簡單C++”註解版本,出於學習的目的,註解的
//過程可以加深對asio的理解,同時也加深了對網路程式的理解,相關
//基本概念的理解

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

//作為一個簡單的session,它只是把一個資訊發出去

//或者接受一個資訊,它實現上只是一個簡單做這些事,
//並沒有維護其它資訊或結構,作為現實應用的一個session可能需要維護的資訊很多
class session
{
public:
  session(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }

  tcp::socket& socket()
  {
    return socket_;
  }

  void start()
  {
    socket_.async_read_some(boost::asio::buffer(data_, max_length)
,
        boost::bind(&session::handle_read, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }

  void handle_read(const boost::system::error_code& error,
      size_t bytes_transferred)
  {
    if (!error)
    {
      boost::asio
::async_write(socket_,
          boost::asio::buffer(data_, bytes_transferred),
          boost::bind(&session::handle_write, this,
            boost::asio::placeholders::error));
    }
    else
    {
      delete this;
    }
  }

  void handle_write(const boost::system::error_code& error)
  {
    if (!error)
    {
      socket_.async_read_some(boost::asio::buffer(data_, max_length),
          boost::bind(&session::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
    else
    {
      delete this;
    }
  }

private:
  //一個session為需要一個socket?一個網路連線session需要什麼樣的語義?這一點決定
  //一個session需要一個socket,它代表該session與連線的進行的通訊道路。
  tcp::socket socket_;
  enum { max_length = 1024 };
  char data_[max_length];
};

//一個伺服器的典型設計,它等待一個客戶連線
//當有一個客戶連線了,它會建立一個session,然後具體的與客戶之間的交流就交給session了
//當把交流任務交給session後,它繼續去等待其它客戶的連線
class server
{
public:
  server(boost::asio::io_service& io_service, short port)
    : io_service_(io_service),
      acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
  {
    //一啟動這一個物件,它就開始接受客戶的連線
    //或者監聽客戶連線
    session* new_session = new session(io_service_);
    acceptor_.async_accept(new_session->socket(),
        boost::bind(&server::handle_accept, this, new_session,
          boost::asio::placeholders::error));
  }

  void handle_accept(session* new_session,
      const boost::system::error_code& error)
  {
    if (!error)
    {
      new_session->start();
      new_session = new session(io_service_);
      //接受一個連線需要指出在那個socket上接受
      //所以自然需要一個socket引數
      acceptor_.async_accept(new_session->socket(),
          boost::bind(&server::handle_accept, this, new_session,
            boost::asio::placeholders::error));
    }
    else
    {
      delete new_session;
    }
  }

private:
  boost::asio::io_service& io_service_;
  //等待客戶連線在asio中,類accecptor已經做好了,
  //作為asio使用者只需要簡單指定埠號和IO Service就可以了
  //後者是asio的結構要求的,前者是應用本身的需要
  tcp::acceptor acceptor_;
};

int main(int argc, char* argv[])
{
  try
  {
    if (argc != 2)
    {
      std::cerr << "Usage: async_tcp_echo_server <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    using namespace std; // For atoi.
    server s(io_service, atoi(argv[1]));

    io_service.run();
  }
  //這裡看出來,asio可能沒有丟擲非std::exception異常
  //或者asio定製的異常都繼承自std::exception,作為一個異常處理構架
  //我們應該使用std::exception
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}