C++ 网络客户端模拟器设计

C++ 网络客户端模拟器设计,c++,multithreading,network-programming,boost-asio,C++,Multithreading,Network Programming,Boost Asio,我试图设计一个C++软件,它将发送请求字节(遵循标准**应用程序级别**协议,其字段将被从文本文件中提取)使用*UDP协议**。p> 现在,该客户端必须能够以非常高的速率发送这些请求。每秒最多**2000个事务**,并且如果在指定的超时时间内收到响应,则还应接收响应,否则不接收响应 我将使用boost库来实现所有套接字功能,但我不确定它在这种高速应用程序中的设计:( 我想我必须使用一个高度多线程的应用程序(再次使用Boost).我说的对吗?我必须为每个请求创建一个单独的线程吗?但我认为只有

我试图设计一个C++软件,它将发送请求字节(遵循标准**应用程序级别**协议,其字段将被从文本文件中提取)使用*UDP协议**。p> 现在,该客户端必须能够以非常高的速率发送这些请求。每秒最多**2000个事务**,并且如果在指定的超时时间内收到响应,则还应接收响应,否则不接收响应

我将使用boost库来实现所有套接字功能,但我不确定它在这种高速应用程序中的设计:(

我想我必须使用一个高度多线程的应用程序(再次使用Boost).我说的对吗?我必须为每个请求创建一个单独的线程吗?但我认为只有一个线程必须等待接收响应,否则如果许多线程都在等待响应,我们如何区分哪些线程请求得到了响应!!

希望这个问题是清楚的。我只是需要一些关于我可能面临的设计点和可疑问题的帮助。

目前,我自己的网络客户端已经完成了一半,因此,也许我可以提供一些建议和一些资源供大家参考。在这方面,有很多经验丰富的人,希望他们会插话:)

首先,你是关于boost的。一旦你习惯了它是如何结合在一起的,它就是一个很好的网络代码编写工具包。基本上,您可以创建一个
io\u服务
并调用
run
执行直到所有工作完成,或者调用
runOne
执行单个io操作。就他们自己而言,这没有多大帮助。当您在自己的循环中运行
runOne
时,电源就来了:

boost::asio::io_service myIOService;
while(true)
{
    myIOService.runOne();
}
,或在一个(或多个)线程上运行
run
函数:

然而,值得注意的是,
run
在没有工作要做时立即返回(因此您可以对该线程说再见)。正如我在Stackoverflow上发现的,诀窍是确保它总是有事情要做。解决方案位于
boost::asio::io\u service::work

boost::asio::io_service::work myWork(myIOService);   // just some abstract "work"
上面这一行确保了线程在没有任何事情发生时不会停止。我认为这是一种维持生命的手段:)

在某个时候,您可能会想要创建一个套接字并将其连接到某个地方。我创建了一个通用套接字类(并从中派生了一个文本套接字来创建缓冲输入)。我还想要一个非常像C#的基于事件的系统。我已经为您概述了以下内容:

第一步,我们需要一种传递参数的通用方法,因此,
EventArgs

eventArgs.h

 class EventArgs : boost::noncopyable
 {
 private:

 public:
  EventArgs();
  virtual ~EventArgs() = 0;
 }; // eo class EventArgs:
// STL
#include <functional>
#include <stack>

// Boost
#include <boost/bind.hpp>
#include <boost/thread/mutex.hpp>

 // class Event
 class Event : boost::noncopyable
 {
 public:
  typedef std::function<void(const EventArgs&)> DelegateType;
  typedef boost::shared_ptr<DelegateType> DelegateDecl;

 private:
  boost::mutex m_Mutex;
  typedef std::set<DelegateDecl> DelegateSet;
  typedef std::stack<DelegateDecl> DelegateStack;
  typedef DelegateSet::const_iterator DelegateSet_cit;
  DelegateSet m_Delegates;
  DelegateStack m_ToRemove;

 public:
  Event()
  {
  }; // eo ctor


  Event(Event&& _rhs) : m_Delegates(std::move(_rhs.m_Delegates))
  {
  }; // eo mtor

  ~Event()
  {
  }; // eo dtor

  // static methods
  static DelegateDecl bindDelegate(DelegateType _f)
  {
   DelegateDecl ret(new DelegateType(_f));
   return ret;
  }; // eo bindDelegate

  // methods
  void raise(const EventArgs& _args)
  {
   boost::mutex::scoped_lock lock(m_Mutex);

   // get rid of any we have to remove
   while(m_ToRemove.size())
   {
    m_Delegates.erase(m_Delegates.find(m_ToRemove.top()));
    m_ToRemove.pop();
   };

   if(m_Delegates.size())
   std::for_each(m_Delegates.begin(),
        m_Delegates.end(),
        [&_args](const DelegateDecl& _decl) { (*_decl)(_args); });
  }; // eo raise

  DelegateDecl addListener(DelegateDecl _decl)
  {
   boost::mutex::scoped_lock lock(m_Mutex);
   m_Delegates.insert(_decl);
   return _decl;
  }; // eo addListener

  DelegateDecl addListener(DelegateType _f)
  {
   DelegateDecl ret(bindDelegate(_f));
   return addListener(ret);
  }; // eo addListener


  void removeListener(const DelegateDecl _decl)
  {
   boost::mutex::scoped_lock lock(m_Mutex);
   DelegateSet_cit cit(m_Delegates.find(_decl));
   if(cit != m_Delegates.end())
    m_ToRemove.push(_decl);
  }; // eo removeListener

  // operators

  // Only use operator += if you don't which to manually detach using removeListener
  Event& operator += (DelegateType _f)
  {
   addListener(_f);
   return *this;
  }; // eo op +=

 }; // eo class Event
现在,我们需要一个人们可以订阅/取消订阅的事件类:

事件.h

 class EventArgs : boost::noncopyable
 {
 private:

 public:
  EventArgs();
  virtual ~EventArgs() = 0;
 }; // eo class EventArgs:
// STL
#include <functional>
#include <stack>

// Boost
#include <boost/bind.hpp>
#include <boost/thread/mutex.hpp>

 // class Event
 class Event : boost::noncopyable
 {
 public:
  typedef std::function<void(const EventArgs&)> DelegateType;
  typedef boost::shared_ptr<DelegateType> DelegateDecl;

 private:
  boost::mutex m_Mutex;
  typedef std::set<DelegateDecl> DelegateSet;
  typedef std::stack<DelegateDecl> DelegateStack;
  typedef DelegateSet::const_iterator DelegateSet_cit;
  DelegateSet m_Delegates;
  DelegateStack m_ToRemove;

 public:
  Event()
  {
  }; // eo ctor


  Event(Event&& _rhs) : m_Delegates(std::move(_rhs.m_Delegates))
  {
  }; // eo mtor

  ~Event()
  {
  }; // eo dtor

  // static methods
  static DelegateDecl bindDelegate(DelegateType _f)
  {
   DelegateDecl ret(new DelegateType(_f));
   return ret;
  }; // eo bindDelegate

  // methods
  void raise(const EventArgs& _args)
  {
   boost::mutex::scoped_lock lock(m_Mutex);

   // get rid of any we have to remove
   while(m_ToRemove.size())
   {
    m_Delegates.erase(m_Delegates.find(m_ToRemove.top()));
    m_ToRemove.pop();
   };

   if(m_Delegates.size())
   std::for_each(m_Delegates.begin(),
        m_Delegates.end(),
        [&_args](const DelegateDecl& _decl) { (*_decl)(_args); });
  }; // eo raise

  DelegateDecl addListener(DelegateDecl _decl)
  {
   boost::mutex::scoped_lock lock(m_Mutex);
   m_Delegates.insert(_decl);
   return _decl;
  }; // eo addListener

  DelegateDecl addListener(DelegateType _f)
  {
   DelegateDecl ret(bindDelegate(_f));
   return addListener(ret);
  }; // eo addListener


  void removeListener(const DelegateDecl _decl)
  {
   boost::mutex::scoped_lock lock(m_Mutex);
   DelegateSet_cit cit(m_Delegates.find(_decl));
   if(cit != m_Delegates.end())
    m_ToRemove.push(_decl);
  }; // eo removeListener

  // operators

  // Only use operator += if you don't which to manually detach using removeListener
  Event& operator += (DelegateType _f)
  {
   addListener(_f);
   return *this;
  }; // eo op +=

 }; // eo class Event
现在是实施。您会注意到它调用了另一个类来执行DNS解析。之后我会展示给大家看。此外,我还推荐了一些
EventArg
-衍生产品。当套接字事件发生时,它们只是作为EventArg参数传递

socket.cpp

#include "socket.h"


// boost
#include <boost/asio/placeholders.hpp>

namespace morse
{
 namespace net
 {
  // ctor
  Socket::Socket(ByteVector_sz _bufSize /* = 512 */) : m_bConnected(false)
  {
   m_Buffer.resize(_bufSize);
  }; // eo ctor

  // dtor
  Socket::~Socket()
  {
  }; // eo dtor


  // _handleRead
  void Socket::_handleRead(const boost::system::error_code& _errorCode,
            std::size_t _read)
  {
   if(!_errorCode)
   {
    if(_read)
    {
     onRead.raise(SocketReadEventArgs(*this, m_Buffer, _read));
     // read again
     m_SocketPtr->async_read_some(boost::asio::buffer(m_Buffer), boost::bind(&Socket::_handleRead, this, _1, _2));
    };
   }
   else
    close();
  }; // eo _handleRead


  // _handleConnect
  void Socket::_handleConnect(const boost::system::error_code& _errorCode,
         boost::asio::ip::tcp::resolver_iterator _rit)
  {
   m_bConnected = !_errorCode;
   bool _raise(false);
   if(!_errorCode)
   {
    m_RemoteEndPoint = *_rit;
    _raise = true;
    m_SocketPtr->async_read_some(boost::asio::buffer(m_Buffer), boost::bind(&Socket::_handleRead, this, _1, _2));
   }
   else if(++_rit != boost::asio::ip::tcp::resolver::iterator())
   {
    m_SocketPtr->close();
    m_SocketPtr->async_connect(*_rit, boost::bind(&Socket::_handleConnect, this, boost::asio::placeholders::error, _rit));
   }
   else
    _raise = true; // raise complete failure

   if(_raise)
    onConnect.raise(SocketConnectEventArgs(*this, _errorCode));

  }; // eo _handleConnect


  // connect
  void Socket::connect(boost::asio::ip::tcp::resolver_iterator _rit)
  {
   boost::asio::ip::tcp::endpoint ep(*_rit);
   m_SocketPtr.reset(new boost::asio::ip::tcp::socket(Root::instance().ioService()));
   m_SocketPtr->async_connect(ep, boost::bind(&Socket::_handleConnect, this, boost::asio::placeholders::error, _rit));
  };


  void Socket::connect(const String& _host, Port _port)
  {
   // Anon function for resolution of the host-name and asynchronous calling of the above
   auto anonResolve = [this](const boost::system::error_code& _errorCode, 
           boost::asio::ip::tcp::resolver_iterator _epIt)
   {
    // raise event
    onResolve.raise(SocketResolveEventArgs(*this, !_errorCode ? (*_epIt).host_name() : String(""), _errorCode));

    // perform connect, calling back to anonymous function
    if(!_errorCode)
     this->connect(_epIt);
   };

   // Resolve the host calling back to anonymous function
   Root::instance().resolveHost(_host, _port, anonResolve);

  }; // eo connect


  void Socket::close()
  {
   if(m_bConnected)
   {
    onClose.raise(SocketCloseEventArgs(*this));
    m_SocketPtr->close();
    m_bConnected = false;
   };
  } // eo close
最后,我需要一个基于文本的套接字,该套接字在每次接收到我的行(然后处理)时都会引发一个事件。这次我将省略头文件,只显示实现文件。不用说,它声明了一个名为
onLine
事件,每次接收到完整的行时都会触发该事件:

// boost
#include <boost/asio/buffer.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/placeholders.hpp>

namespace morse
{
 namespace net
 {
  String TextSocket::m_DefaultEOL("\r\n");

  // ctor
  TextSocket::TextSocket() : m_EOL(m_DefaultEOL)
  {
   onRead += boost::bind(&TextSocket::readHandler, this, _1);
  }; // eo ctor


  // dtor
  TextSocket::~TextSocket()
  {
  }; // eo dtor


  // readHandler
  void TextSocket::readHandler(const EventArgs& _args)
  {
   auto& args(static_cast<const SocketReadEventArgs&>(_args));
   m_LineBuffer.append(args.buffer().begin(), args.buffer().begin() + args.bytesRead());
   String::size_type pos;
   while((pos = m_LineBuffer.find(eol())) != String::npos)
   {
    onLine.raise(SocketLineEventArgs(*this, m_LineBuffer.substr(0, pos)));
    m_LineBuffer = m_LineBuffer.substr(pos + eol().length());
   };
  }; // eo readHandler


  // writeHandler
  void TextSocket::writeHandler(const boost::system::error_code& _errorCode, std::size_t _written)
  {
   if(!_errorCode)
   {
    m_Queue.pop_front();
    if(!m_Queue.empty()) // more to do?
     boost::asio::async_write(*socket().get(), boost::asio::buffer(m_Queue.front(), m_Queue.front().length()), boost::bind(&TextSocket::writeHandler, this, _1, _2));
   }
   else
    close();
  }; // eo writeHandler

  void TextSocket::sendLine(String _line)
  {
   Root::instance().ioService().post(boost::bind(&TextSocket::_sendLine, this, _line));
  }; // eo sendLine


  // _sendLine
  void TextSocket::_sendLine(String _line)
  {
   // copy'n'queue
   _line.append(m_EOL);
   m_Queue.push_back(_line);
   if(m_Queue.size() == 1) // previously an empty queue, must start write!
    boost::asio::async_write(*socket().get(), boost::asio::buffer(m_Queue.front(), m_Queue.front().length()), boost::bind(&TextSocket::writeHandler, this, _1, _2));
  }; // eo sendLine
//boost
#包括
#包括
#包括
名称空间莫尔斯
{
名称空间网
{
字符串TextSocket::m_DefaultEOL(“\r\n”);
//执行器
TextSocket::TextSocket():m_EOL(m_DefaultEOL)
{
onRead+=boost::bind(&TextSocket::readHandler,this,_1);
}//eo
//dtor
TextSocket::~TextSocket()
{
};//eo dtor
//读处理器
void TextSocket::readHandler(const EventArgs&_args)
{
自动参数(静态参数转换(_参数));
m_LineBuffer.append(args.buffer().begin(),args.buffer().begin()+args.bytesRead());
字符串::大小\类型pos;
while((pos=m_LineBuffer.find(eol())!=String::npos)
{
raise(SocketLineEventArgs(*this,m_LineBuffer.substr(0,pos));
m_LineBuffer=m_LineBuffer.substr(pos+eol().length());
};
};//eo readHandler
//书写工
void TextSocket::writeHandler(const boost::system::error\u code&\u errorCode,std::size\u t\u writed)
{
如果(!\u错误代码)
{
m_Queue.pop_front();
如果(!m_Queue.empty())//还要做什么?
boost::asio::async_write(*socket().get(),boost::asio::buffer(m_Queue.front(),m_Queue.front().length()),boost::bind(&TextSocket::writeHandler,this,_1,_2));
}
其他的
close();
};//eo writeHandler
void TextSocket::sendLine(字符串_行)
{
Root::instance().ioService().post(boost::bind(&TextSocket::_sendLine,this,_line));
};//eo发送线
//_发送线
void TextSocket::_sendLine(字符串_行)
{
//复制队列
_行。追加(m_EOL);
m_队列。推回(_行);
如果(m_Queue.size()==1)//以前是空队列,则必须开始写入!
boost::asio::async_write(*socket().get(),boost::asio::buffer(m_Queue.front(),m_Queue.front().length()),boost::bind(&TextSocket::writeHandler,this,_1,_2));
};//eo发送线
关于上面的类需要注意的一些事情…它使用
boost::asio::post
发送行。这允许它以线程安全的方式发生在asio管理的线程上,并允许我们将要发送的行排队。这使得它非常可伸缩


我肯定还有很多问题,也许我的代码没有什么帮助。我花了几天时间把它们拼凑在一起,弄清楚它们的意义,我怀疑它是否真的有用。希望一些更好的人会浏览一下它,然后说“天哪,这个”

我不确定你是否需要去”重磅的多线程。大多数高速应用程序都使用操作系统的轮询机制,这种机制通常比线程扩展得更好

体系结构在很大程度上取决于应用程序的反应性,即应用程序的组件
// boost
#include <boost/asio/buffer.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/placeholders.hpp>

namespace morse
{
 namespace net
 {
  String TextSocket::m_DefaultEOL("\r\n");

  // ctor
  TextSocket::TextSocket() : m_EOL(m_DefaultEOL)
  {
   onRead += boost::bind(&TextSocket::readHandler, this, _1);
  }; // eo ctor


  // dtor
  TextSocket::~TextSocket()
  {
  }; // eo dtor


  // readHandler
  void TextSocket::readHandler(const EventArgs& _args)
  {
   auto& args(static_cast<const SocketReadEventArgs&>(_args));
   m_LineBuffer.append(args.buffer().begin(), args.buffer().begin() + args.bytesRead());
   String::size_type pos;
   while((pos = m_LineBuffer.find(eol())) != String::npos)
   {
    onLine.raise(SocketLineEventArgs(*this, m_LineBuffer.substr(0, pos)));
    m_LineBuffer = m_LineBuffer.substr(pos + eol().length());
   };
  }; // eo readHandler


  // writeHandler
  void TextSocket::writeHandler(const boost::system::error_code& _errorCode, std::size_t _written)
  {
   if(!_errorCode)
   {
    m_Queue.pop_front();
    if(!m_Queue.empty()) // more to do?
     boost::asio::async_write(*socket().get(), boost::asio::buffer(m_Queue.front(), m_Queue.front().length()), boost::bind(&TextSocket::writeHandler, this, _1, _2));
   }
   else
    close();
  }; // eo writeHandler

  void TextSocket::sendLine(String _line)
  {
   Root::instance().ioService().post(boost::bind(&TextSocket::_sendLine, this, _line));
  }; // eo sendLine


  // _sendLine
  void TextSocket::_sendLine(String _line)
  {
   // copy'n'queue
   _line.append(m_EOL);
   m_Queue.push_back(_line);
   if(m_Queue.size() == 1) // previously an empty queue, must start write!
    boost::asio::async_write(*socket().get(), boost::asio::buffer(m_Queue.front(), m_Queue.front().length()), boost::bind(&TextSocket::writeHandler, this, _1, _2));
  }; // eo sendLine