POCO TCPServer 解析
POCO C++ Libraries提供一套 C++ 的类库用以开发基于网络的可移植的应用程序,功能涉及线程、文件、流,网络协议包括:HTTP、FTP、SMTP 等,还提供 XML 的解析和 SQL 数据库的访问接口。不仅给我的工作带来极大的便利,而且设计巧妙,代码易读,注释丰富,也是非常好的学习材料,我个人非常喜欢。POCO的创始人在这个开源项目的基础上做了一些收费产品,也成立了自己的公司,"I am in the lucky position to work for my own company",真是让人羡慕啊。
POCO C++ Libraries
基于POCO的产品
Poco主页:http://pocoproject.org/
Poco文档:http://pocoproject.org/docs/
创始人主页:http://obiltschnig.com/
公司主页:http://www.appinf.com/
我主要用过Net,Data,XML部分,Net里对socket的封装类,实现TCP,HTTP,SMTP协议的框架,Data里对MySQL接口封装,XML里对DOM标准的实现。我目前主要是做C++网络编程,曾经尝试接触ACE库,但觉得太复杂难理解,而且也没有条件用于项目,后来发现了Poco,不仅简单易用,而且也包含《C++ Networking Programming》中提到的各种模式和框架,更难得的是文档注释丰富,看起源码来相当舒服。因此想写一些笔记,一方面借此加深对网络编程的理解,另一方面也希望和大家多多交流。
第一篇讨论Poco::TCPServer框架,先看看Poco::TCPServer有哪些类
class Net_API TCPServerConnection: public Poco::Runnable
class Net_API TCPServerConnectionFactory
class Net_API TCPServerDispatcher: public Poco::Runnable
class Net_API TCPServerParams: public Poco::RefCountedObject
Runnable是一个抽象类,要求其子类实现一个run()函数,run()一般作为线程的入口函数.
先看看run函数是如何被调用的,以TCPServer为例。
{
poco_assert (_stopped);
_stopped = false;
_thread.start(*this);//这里传入本类对象地址
}
再看看Thread类的start函数
{
startImpl(target);
}
这里的ThreadImpl::startImpl是POSIX实现版本
{
_pData->pRunnableTarget = ⌖
if (pthread_create(&_pData->thread, &attributes, runnableEntry, this))
{
_pData->pRunnableTarget = 0;
throw SystemException("cannot start thread");
}
pRunnableTarget就指向Runnable了
{
ThreadImpl* pThreadImpl = reinterpret_cast<ThreadImpl*>(pThread);
AutoPtr<ThreadData> pData = pThreadImpl->_pData;
try
{
pData->pRunnableTarget->run();//在这里
}
catch (Exception& exc)
知道run()是如何被调用了,再看看TcpServer::run()的实现
2{
3 while (!_stopped)
4 {
5 Poco::Timespan timeout(250000);
6 if (_socket.poll(timeout, Socket::SELECT_READ))
7 {
8 try
9 {
10 StreamSocket ss = _socket.acceptConnection();
11 // enabe nodelay per default: OSX really needs that
12 ss.setNoDelay(true);
13 _pDispatcher->enqueue(ss);
14 }
15 catch (Poco::Exception& exc)
16
在第6行调用标准select函数,准备好读后,第10行调用标准accept建立连接,然后把StreamSocke对象放入TCPServerDispatcher对象内的队列中,可见TCPServer只是建立连接,之后的工作就都交给TCPServerDispatcher了。
在讲TCPServerDispatcher之前,先需要说明的是第10行TCPServer的_socket成员变量类型是ServerSocket,ServerSocket在一个构造函数中调用了bind和listen,具体如下
{
IPAddress wildcardAddr;
SocketAddress address(wildcardAddr, port);
impl()->bind(address, true);
impl()->listen(backlog);
}
要注意,尽管ServerSocket类提供了无参构造函数,但使用无参构造函数创建的对象,在TCPServer对象调用start()之前,必须先bind和listen。
" /// Before start() is called, the ServerSocket passed to
/// TCPServer must have been bound and put into listening state."
继续看TCPServerDispatcher,沿着TcpServer::run()第13行,我们看它的equeue()
2{
3 FastMutex::ScopedLock lock(_mutex);
4
5 if (_queue.size() < _pParams->getMaxQueued())
6 {
7 _queue.enqueueNotification(new TCPConnectionNotification(socket));
8 if (!_queue.hasIdleThreads() && _currentThreads < _pParams->getMaxThreads())
9 {
10 try
11 {
12 static const std::string threadName("TCPServerConnection");
13 _threadPool.start(*this, threadName);
14 ++_currentThreads;
15 }
16 catch (Poco::Exception&)
17
第7行enqueueNotification把一个TCPConnectionNotification入队,第13行把this交给ThreadPool启动一个线程,那么这个线程就要运行TCPServerDispatcher的run函数了,看TCPServerDispatcher::run()
2{
3 AutoPtr<TCPServerDispatcher> guard(this, true); // ensure object stays alive
4
5 int idleTime = (int) _pParams->getThreadIdleTime().totalMilliseconds();
6
7 for (;;)
8 {
9 AutoPtr<Notification> pNf = _queue.waitDequeueNotification(idleTime);
10 if (pNf)
11 {
12 TCPConnectionNotification* pCNf = dynamic_cast<TCPConnectionNotification*>(pNf.get());
13 if (pCNf)
14 {
15 std::auto_ptr<TCPServerConnection> pConnection(_pConnectionFactory->createConnection(pCNf->socket()));
16 poco_check_ptr(pConnection.get());
17 beginConnection();
18 pConnection->start();
19 endConnection();
20 }
21 }
第9行waitDequeueNotification一个Notification,第12行把这个通知类型转换成TCPConnectionNotification,联系之前的enqueueNotification,大概能才到是什么意思。第15行又出现个TCPServerConnection。好吧,看来搞清楚TCPServerDispatcher还是要先看下TCPServerConnection,还有TCPConnectionNotification。
尽管TCPServerConnection继承了Runable,但没有实现run(),它的start()如下
{
try
{
run();
}
用户需要继承TCPServerConnection实现run函数,看下源码中的说明,要完成对这个socket的处理,因为run函数返回时,连接就自动关闭了。
/// The abstract base class for TCP server connections
/// created by TCPServer.
///
/// Derived classes must override the run() method
/// (inherited from Runnable). Furthermore, a
/// TCPServerConnectionFactory must be provided for the subclass.
///
/// The run() method must perform the complete handling
/// of the client connection. As soon as the run() method
/// returns, the server connection object is destroyed and
/// the connection is automatically closed.
///
/// A new TCPServerConnection object will be created for
/// each new client connection that is accepted by
/// TCPServer.
连接在哪被关闭的呢,注释说随着TCPServerConnection对象销毁而关闭。具体就是,TCPServerConnection类有个StreamSocket成员,StreamSocket继承自Socket,Socket类又包含了个SocketImpl成员,所以就有~TCPServerConnection()->~StreamSocket()->~Socket()->~SocketImpl(),最后~SocketImpl()调用close()关闭了连接。
那么TCPServerConnection对象何时被创建何时被销毁呢,这下又回到TCPServerDispatcher::run()来了,看TCPServerDispatcher::run()的第15行,创建了TCPServerConnection对象,第18行调用TCPServerConnection::start()进而调用TCPServerConnection::run(),第20行块结束,TCPServerConnection对象随着智能指针销毁而被销毁。
还剩TCPConnectionNotification或者说Notification要搞清楚了,但是要对Poco的Notifactions模块源码进行分析的话,本文的篇幅也就太长了,就从文档来大致看看吧
Notifications
Facilities for type-safe sending and delivery of notification objects within a single thread or from one thread to another, also well suited for the implementation of notification mechanisms. A notification queue class for distributing tasks to worker threads, simplifying the implementation of multithreaded servers.
简单的说Notifications模块可用于线程间传递消息,简化多线程服务器的实现。具体到TCPServer,就是把已连接的socket,放到NotificationQueue中,并从TheadPool出来一个线程,线程从NotificationQueue取到这个socket,从而用TCPServerConnection::run()里的逻辑对socket进行处理。
至此TCPServer基本分析完毕,还有TCPServerConnectionFactory和TCPServerParams,分别用于产生TCPServerConnection和设置参数,就不细说了。
纵观Poco::TCPServer,套一下《Unix Network Programming》里讲的服务器模型,属于“在程序启动阶段创建一个线程池之后让主线程调用accept,并把每个客户连接传递给池中某个可用线程”。Poco1.3.6版里用select作为IO multiplexing。1.3.7版正在尝试epoll(windows平台依然是select),但还未release,跟踪svn来看也没有使用edge-triggered模式。套用《The C10K problem》里讲的服务器模型,属于"Serve many clients with each thread, and use nonblocking I/O and level-triggered readiness notification"。总结起来就是:non-blocking IO + IO multiplexing(level-triggered) + thread pool。
Poco::TCPServer也许并不能算一个性能很高的TCP服务器,但我非常喜欢它的设计和编码风格。顺便提一下对底层socket的封装,由socket类派生的各种子类,ServerSocket在构造函数中进行bind和listen,StreamSocket在构造函数进行connect,都是非常贴心的设计。
POCO TCPServer 解析的更多相关文章
- Poco::TCPServer框架解析
Poco::TCPServer框架解析 POCO C++ Libraries提供一套 C++ 的类库用以开发基于网络的可移植的应用程序,功能涉及线程.文件.流,网络协议包括:HTTP.FTP.SMTP ...
- 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记.
说明 为何要写这篇文章 ,之前看过阿二的梦想船的<Poco::TCPServer框架解析> http://www.cppblog.com/richbirdandy/archive/2010 ...
- Poco XMLconfiguration 解析xml配置文件
环境: Centos7 GCC: 7.3.0 准备需要读取的xml文件: <config> <prop1>1.23</prop1> <prop2>2.3 ...
- linux 下cmake 编译 ,调用,调试 poco 1.6.0 小记
上篇文章 小记了: 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记. http://www.cnblogs.com/bleachli/p/4352 ...
- poco json 中文字符,抛异常JSON Exception -->iconv 转换 备忘录。
起因 最近linux服务器通信需要用到json. jsoncpp比较出名,但poco 1.5版本以后已经带有json库,所以决定使用poco::json(linux 上已经用到了poco这一套框架). ...
- Poco C++库网络模块例子解析2-------HttpServer
//下面程序取自 Poco 库的Net模块例子----HTTPServer 下面开始解析代码 #include "Poco/Net/HTTPServer.h" //继承自TCPSe ...
- Poco库网络模块例子解析1-------字典查询
Poco的网络模块在Poco::Net名字空间下定义 下面是字典例子解析 #include "Poco/Net/StreamSocket.h" //流式套接字 #include ...
- POCO库中文编程参考指南(10)如何使用TCPServer框架?
1 TCPServer 框架概述 POCO 库提供TCPServer框架,用以搭建自定义的 TCP 服务器.TCPServer维护一个连接队列.一个连接线程池.连接线程用于处理连接,连接线程只要一空闲 ...
- Windows 10上源码编译Poco并编写httpserver和tcpserver | compile and install poco cpp library on windows
本文首发于个人博客https://kezunlin.me/post/9587bb47/,欢迎阅读! compile and install poco cpp library on windows Se ...
随机推荐
- SENNA
SENNA is a software distributed under a non-commercial license, which outputs a host of Natural Lang ...
- Business Component(BC)和Business Object(BO)
Siebel应用架构的一个成功的地方就是在应用里引入了BC,BO的概念,从而使得几千张关系数据表能够按照业务的含义组织成业务对象,对于业务人员而言具有了业务上的含义,而不仅仅是从技术人员的观点来对待数 ...
- jsp页面传输到xxAction.java乱码解决
jsp页面传输到xxAction.java乱码解决:jsp:encodeURI(encodeURI("xx"))java:if(!StringUtils.isBlank(belon ...
- Python简单介绍以及Python环境搭建(入门1)
转载请标明出处: http://www.cnblogs.com/why168888/p/6400694.html 本文出自:[Edwin博客园] Python 简单介绍 适合领域: Web网站和各种网 ...
- POJ 1330 Nearest Common Ancestors 【LCA模板题】
任意门:http://poj.org/problem?id=1330 Nearest Common Ancestors Time Limit: 1000MS Memory Limit: 10000 ...
- python 3+djanjo 2.0.7简单学习(五)--Django投票应用
1.编写一个简单的表单 编写的投票详细页面的模板 ("votes/detail.html") ,让它包含一个 HTML <form> 元素: <!DOCTYPE ...
- 【洛谷P3469】[POI2008]BLO-Blockade
BLO-Blockade 题目链接 若一个点为割点:统计出每个子树的大小,两两相乘再相加, 再加上n-1,为这个点与其他点的拜访数, 因为拜访是互相的,最后再乘二即可 若一个点不是割点:只有(n-1) ...
- Java中关于String的比较
关于String的各种==和equals,有的人搞不懂 简而言之 String s1 = "Hello" 声明的是一个常量,会在常量池里. ...
- Android学习笔记_32_通过WebView实现JS代码与Java代码互相通信
webview两种实现方法,覆盖onKeyDown()方法 缓存 WebSettings应用注意的几个问题 1.要实现JS代码与Java代码互相通信,需要通过Android的WebView控件,在视图 ...
- 【XP-IE8】XP系统的IE8无法正常访问图片,按钮无反应,不执行JS脚本代码
环境: Windows XP ,自带的是IE6,另外安装的 IE8 . 状况: 使用IE8浏览内网网站,点击登录按钮,没有反应.滚动图片新闻不显示.但使用Chrome浏览器,一切正常,说明是IE8某处 ...