boost::asio 连接管理10

来源:互联网 发布:联通软件研究院年终奖 编辑:程序博客网 时间:2024/05/22 01:27

本篇应该是最后一篇了。给出一个完整的可以用于产品环境的代码。

现在修改一下代码逻辑,让server变成一个echo服务器。收到16字节长度后,再接受字符串。然后返回16字节长度给客户端,再返回字符串。

16字节长度类型是unsigned short,在网络传输的时候采用big-endian 字节顺序。字符串采用utf-8编码格式。

先来看一下我的newLISP客户端模拟程序:

(define (quit-for-error)  ((println (net-error)) (exit)))(define (send-test)  (set 'socket (net-connect "localhost" 8889))  (set 'hello (utf8 (unicode "hello, my name is 陈抒")))  (set 'size (pack ">u" (length hello)))  (if (net-send socket size) (println "send size succeeded") (quit-for-error))  (if (net-send socket hello) (println "send string succeeded") (quit-for-error))  (if (net-receive socket size-buffer 2) (println "receive size succeeded") (quit-for-error))  (set 'size2 ((unpack ">u" size-buffer) 0))  (println "received size is: " size2)  (if (net-receive socket str-buffer size2) (println "receive string succeeded, str: " str-buffer) (quit-for-error))  (exit))(dotimes (i 2000) (spawn 'ri (send-test)))(until (sync 1000))(exit)

说明:

1. 如果对使用newLISTP进行TCP通信感兴趣,可以参考我的另一篇文章:http://blog.csdn.net/sheismylife/article/details/8521748

2. dotimes是一个循环,i取值范围是:[0, 2000) (左闭右开区间),不断的创建进程,每个进程都运行函数send-test。


现在看一下改动后的server代码:

1.为了在多线程环境下输出一些信息,cout由于不是线程安全,所以不适用。这里引入了booster::log,可以参考我的文章:

http://blog.csdn.net/sheismylife/article/details/8248663

2.为了证明线程池在asio中的作用,日志将记录线程id

3.因为涉及到big-endian,所以自己实现了相关算法。


先看一下src/CMakeLists.txt文件, 里面加入了booster库。

cmake_minimum_required(VERSION 2.8)set(CMAKE_BUILD_TYPE Debug)set(PROJECT_INCLUDE_DIR ../include)find_package(Boost COMPONENTS system filesystem thread REQUIRED)include_directories(${Boost_INCLUDE_DIR} ${PROJECT_INCLUDE_DIR})AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src CPP_LIST1)AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src/core CPP_LIST2)AUX_SOURCE_DIRECTORY(${CMAKE_SOURCE_DIR}/src/business CPP_LIST3)add_executable(service ${CPP_LIST1} ${CPP_LIST2} ${CPP_LIST3})target_link_libraries(service ${Boost_LIBRARIES} booster)add_definitions(-Wall)


然后看一下main.cc,里面使用了log

#include <iostream>#include "core/server.h"#include "business/client.h"#include <booster/log.h>#include <booster/shared_ptr.h>using namespace std;void init_log() {  booster::shared_ptr<booster::log::sinks::file> f(new booster::log::sinks::file());  f->append();  f->max_files(10);  f->open("/opt/tcp_template.log");  booster::log::logger::instance().add_sink(f);  booster::log::logger::instance().set_default_level(booster::log::debug);}int main(int argc,char ** argv) {  try {    init_log();    io_service iosev;    tcp::endpoint listen_endpoint(tcp::v4(), 8889);    Server<Client> server(iosev, listen_endpoint, 10);    server.Run();  } catch(std::exception const& ex) {    BOOSTER_ERROR("main") << "thread id: " << this_thread::get_id() << "Caught an exception: " << ex.what();  }}

现在看一下server.h文件,里面也使用了log:

#ifndef CORE_SERVER_H_#define CORE_SERVER_H_#include <boost/asio.hpp>#include <boost/bind.hpp>#include <booster/log.h>#include <boost/thread/thread.hpp>#include <vector>using namespace std;using namespace boost;using boost::system::error_code;using namespace boost::asio;using ip::tcp;// Crate a thread pool for io_service.// Run the io_service to accept new incoming TCP connection and handle the I/O events// You should provide your class as template argument here// Your class must inherit from Connection class.template<class T>class Server { public:  typedef T ClientType; Server(io_service& s, tcp::endpoint const& listen_endpoint, size_t threads_number)   : io_(s),    signals_(s),    acceptor_(io_, listen_endpoint),    thread_pool_size_(threads_number) {      signals_.add(SIGINT);      signals_.add(SIGTERM);#if defined(SIGQUIT)      signals_.add(SIGQUIT);#endif      signals_.async_wait(bind(&Server::Stop, this));      shared_ptr<ClientType> c(new ClientType(io_));                acceptor_.async_accept(c->socket, bind(&Server::AfterAccept, this, c, _1));    }  void AfterAccept(shared_ptr<ClientType>& c, error_code const& ec) {    // Check whether the server was stopped by a signal before this completion    // handler had a chance to run.    if (!acceptor_.is_open()) {      cout << "acceptor is closed" << endl;      return;    }            if (!ec) {      c->StartJob();      shared_ptr<ClientType> c2(new ClientType(io_));      acceptor_.async_accept(c2->socket, bind(&Server::AfterAccept, this, c2, _1));    }  }  // Create a thread pool for io_service  // Launch io_service  void Run() {    // Create a pool of threads to run all of the io_services.    vector<shared_ptr<thread> > threads;    for (size_t i = 0; i < thread_pool_size_; ++i) {      shared_ptr<thread> t(new thread(bind(&io_service::run, &io_)));      threads.push_back(t);    }    // Wait for all threads in the pool to exit.    for (std::size_t i = 0; i < threads.size(); ++i) {      threads[i]->join();    }  } private:  void Stop() {    BOOSTER_INFO("Server") << "thread id: " << this_thread::get_id() << "stopping" << endl;    acceptor_.close();    io_.stop();  } private:  io_service& io_;  boost::asio::signal_set signals_;  tcp::acceptor acceptor_;  size_t thread_pool_size_;};#endif

connection.h文件也有改变,添加了日志,并且拦截了关闭socket的异常。

#ifndef CORE_CONNECTION_H_#defineCORE_CONNECTION_H_#include <boost/asio.hpp>#include <boost/enable_shared_from_this.hpp>#include <booster/log.h>#include <boost/thread/thread.hpp>using namespace boost::asio;using ip::tcp;using boost::system::error_code;using namespace boost;using namespace std;template<class T>class Connection: public boost::enable_shared_from_this<T> { public: Connection(io_service& s)   : socket(s), strand_(s) {  }  ~Connection() {  }  // You must override it yourself  // Default implementation closes the socket using shutdonw&cloes methods  // You could override it if want change it  // Or resue it with Connection::CloseSocket() format  void CloseSocket() {    try {      socket.shutdown(tcp::socket::shutdown_both);      socket.close();    } catch (std::exception& e) {      BOOSTER_INFO("Connection") << "thread id: " << this_thread::get_id() << e.what() << endl;    }  }      // You must override it yourself  virtual void StartJob() = 0;  tcp::socket socket;      // Strand to ensure the connection's handlers are not called concurrently.  boost::asio::io_service::strand strand_;};#endif

好,现在看一下新的util/endian.h文件,里面是关于big-endian的算法:

#ifndef UTIL_ENDIAN_H_#define UTIL_ENDIAN_H_#include <boost/cstdint.hpp>#include <vector>#include <sstream>using namespace std;// Get the bit value specified by the index// index starts with 0template<class T>int Bit_Value(T value, uint8_t index) {  return (value & (1 << index)) == 0 ? 0 : 1;}// T must be one of integer typetemplate<class T>string PrintIntAsBinaryString(T v) {  stringstream stream;  int i = sizeof(T) * 8 - 1;  while (i >= 0) {    stream << Bit_Value(v, i);    --i;  }      return stream.str();}bool IsLittleEndian() {  short int x = 0x00ff;  char* p = (char*)&x;  return (short int)p[0] == -1;}static union {  char c[4];  unsigned char l;} endian_test = {{'l','?','?','b'}};#define IsLittleEndian2() (endian_test.l == 'l')// Convert the following integer values to big-endian if necessarytemplate<class T>T Int16ToBigEndian(T value) {  if (IsLittleEndian2()) {    uint8_t* p = reinterpret_cast<uint8_t*> (&value);    T v1 = static_cast<T> (p[0]);    T v2 = static_cast<T> (p[1]);    return (v1 << 8) | v2;  } else {    return value;  }}template<class T>T Int32ToBigEndian(T value) {  if (IsLittleEndian2()) {    uint8_t* p = reinterpret_cast<uint8_t*> (&value);    T v1 = static_cast<T> (p[0]);    T v2 = static_cast<T> (p[1]);    T v3 = static_cast<T> (p[2]);    T v4 = static_cast<T> (p[3]);    return (v1 << 24) | (v2 << 16) << (v3 << 8) | v4;  } else {    return value;  }}// The following functions convert the byte arrays // that has big-endian into integers on local platformtemplate<class T>T BigEndianBytesToInt16(vector<uint8_t> const& value) {  if (IsLittleEndian2()) {    T h = static_cast<T> (value[0]);    T l = static_cast<T> (value[1]);    return (h << 8) | l;  } else {    T tmp = 0;    memcpy(&tmp, &value[0], 2);    return tmp;  }}template<class T>T BigEndianBytesToInt32(uint8_t value[4]) {  if (IsLittleEndian2()) {    T a = static_cast<T> (value[0]);    T b = static_cast<T> (value[1]);    T c = static_cast<T> (value[2]);    T d = static_cast<T> (value[3]);    return (a << 24) | (b << 16) | (c << 8) | d;  } else {    T tmp = 0;    memcpy(&tmp, &value[0], 4);    return tmp;  }}#endif

最后啊看一下Client.cc文件的代码,真正实现了echo服务器的业务:

#include "business/client.h"#include <boost/bind.hpp>#include "util/endian.h"#include <booster/log.h>using namespace boost;Client::Client(io_service& s):  Connection(s), size_buffer_(2, 0), string_buffer_(100, 0) {}void Client::StartJob() {  BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " start job" << endl;  async_read(socket, buffer(size_buffer_),     strand_.wrap(bind(&Client::AfterReadSize, shared_from_this(), _1)));}void Client::CloseSocket() {  BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " close socket" << endl;  Connection::CloseSocket();}Client::~Client() {  BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " ~client" << endl;  CloseSocket();}void Client::AfterReadString(error_code const& ec, uint16_t size) {  BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " enter AfterReadString" << endl;  if (ec) {    BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << ec.message() << endl;    return;  }  string str(string_buffer_.begin(), string_buffer_.begin() + size);  BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " str:" << str << endl;  size_buffer_.assign(2, 0);  uint16_t s = Int16ToBigEndian(size);  memcpy(&size_buffer_[0], &s, 2);    async_write(socket, buffer(size_buffer_),      strand_.wrap(bind(&Client::AfterSendSize, shared_from_this(), _1, s)));}void Client::AfterReadSize(error_code const& ec) {  BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " enter AfterReadSize" << endl;  if (ec) {    BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << ec.message() << endl;    return;  }  uint16_t size = BigEndianBytesToInt16<uint16_t>(size_buffer_);  if (size > 0) {    BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " correct size received, size:" << size << endl;    string_buffer_.assign(100, 0);    async_read(socket, buffer(string_buffer_, size),           strand_.wrap(bind(&Client::AfterReadString, shared_from_this(), _1, size)));  } else {    BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " wrong size received, size:" << size << endl;    CloseSocket();  }}void Client::AfterSendSize(error_code const& ec, uint16_t size) {  BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " enter AfterSendSize" << endl;  if (ec) {    BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << ec.message() << endl;    return;  }  async_write(socket, buffer(string_buffer_, size),           strand_.wrap(bind(&Client::AfterSendString, shared_from_this(), _1, size)));  }void Client::AfterSendString(error_code const& ec, uint16_t size) {  BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << " enter AfterSendString" << endl;  if (ec) {    BOOSTER_INFO("Client") << "thread id: " << this_thread::get_id() << ec.message() << endl;    return;  }  size_buffer_.assign(2, 0);  async_read(socket, buffer(size_buffer_),     strand_.wrap(bind(&Client::AfterReadSize, shared_from_this(), _1)));  }

为完整起见,client.h文件代码也贴一下:

#ifndef BUSINESS_CLIENT_H_#define BUSINESS_CLIENT_H_#include "core/connection.h"#include <vector>using namespace std;class Client: public Connection<Client> { public:  Client(io_service& s);  ~Client();  void StartJob();  void CloseSocket();  void AfterReadSize(error_code const& ec);  void AfterReadString(error_code const& ec, uint16_t size);  void AfterSendSize(error_code const& ec, uint16_t size);  void AfterSendString(error_code const& ec, uint16_t size); private:  vector<uint8_t> size_buffer_;  vector<uint8_t> string_buffer_;};#endif

运行结果,没有失败,并发测试结果证明server是可靠的。







原创粉丝点击