Boost: ASIO connection management 10

Source: Internet
Author: User

This article should be the last one. Provide a complete code that can be used in the product environment.

Modify the code logic to make the server an echo server. After receiving the 16-byte length, the string is accepted. Then return the 16-byte length to the client, and then return the string.

The 16-byte length type is unsigned short, which adopts the big-Endian byte sequence during network transmission. The string is encoded in UTF-8 format.

Let's take a look at my newlisp client simulation program:

(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 Chen Jing "))) (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)

Note:

1. If you are interested in using newlistp for TCP communication, you can refer to my another article: http://blog.csdn.net/sheismylife/article/details/8521748

2. dotimes is a loop. The value range of I is [0, 2000) (Left closed and right open). A process is continuously created and each process runs the send-test function.

Now let's take a look at the changed server code:

1. to output some information in a multi-threaded environment, cout is not applicable because it is not thread-safe. Booster: log is introduced here. For details, refer to my article:

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

2. To prove the role of the thread pool in ASIO, the log records the thread ID.

3. Because big-Endian is involved, related algorithms are implemented by yourself.

Let's take a look at the src/cmakelists.txt file, which contains the booster library.

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)

Then let's take a look at main. CC, which uses 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();  }}

Now let's take a look at the server. h file, which also uses 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

The connection. h file is also changed. logs are added and exceptions of socket closure are blocked.

#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

Now let's take a look at the new util/endian. h file, which contains the big-Endian algorithm:

#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

Finally, let's take a look at the code of the client. CC file to truly implement the business of the echo server:

#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)));  }

For completeness, the client. h file code is also pasted:

#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

The running result does not fail. The concurrent test result proves that the server is reliable.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.