C++ 网络客户端模拟器设计
我试图设计一个C++软件,它将发送请求字节(遵循标准**应用程序级别**协议,其字段将被从文本文件中提取)使用*UDP协议**。p> 现在,该客户端必须能够以非常高的速率发送这些请求。每秒最多**2000个事务**,并且如果在指定的超时时间内收到响应,则还应接收响应,否则不接收响应 我将使用boost库来实现所有套接字功能,但我不确定它在这种高速应用程序中的设计:( 我想我必须使用一个高度多线程的应用程序(再次使用Boost).我说的对吗?我必须为每个请求创建一个单独的线程吗?但我认为只有一个线程必须等待接收响应,否则如果许多线程都在等待响应,我们如何区分哪些线程请求得到了响应!! 希望这个问题是清楚的。我只是需要一些关于我可能面临的设计点和可疑问题的帮助。C++ 网络客户端模拟器设计,c++,multithreading,network-programming,boost-asio,C++,Multithreading,Network Programming,Boost Asio,我试图设计一个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