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 TCPServer: public Poco::Runnable
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为例。

void TCPServer::start()
{
    poco_assert (_stopped);

    _stopped = false;
    _thread.start(*this);//这里传入本类对象地址
}

再看看Thread类的start函数

class Foundation_API Thread: private ThreadImpl
void Thread::start(Runnable& target)
{
    startImpl(target);
}

这里的ThreadImpl::startImpl是POSIX实现版本

;
        throw SystemException("cannot start thread");
    }
      

pRunnableTarget就指向Runnable了

void* ThreadImpl::runnableEntry(void* pThread)
{
      
    ThreadImpl* pThreadImpl = reinterpret_cast<ThreadImpl*>(pThread);
    AutoPtr<ThreadData> pData = pThreadImpl->_pData;
    try
    {
        pData->pRunnableTarget->run();//在这里
    }
    catch (Exception& exc)
      

知道run()是如何被调用了,再看看TcpServer::run()的实现

            

在第6行调用标准select函数,准备好读后,第10行调用标准accept建立连接,然后把StreamSocke对象放入TCPServerDispatcher对象内的队列中,可见TCPServer只是建立连接,之后的工作就都交给TCPServerDispatcher了。

在讲TCPServerDispatcher之前,先需要说明的是第10行TCPServer的_socket成员变量类型是ServerSocket,ServerSocket在一个构造函数中调用了bind和listen,具体如下

ServerSocket::ServerSocket(Poco::UInt16 port, int backlog): Socket(new ServerSocketImpl)
{
    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()

            

第7行enqueueNotification把一个TCPConnectionNotification入队,第13行把this交给ThreadPool启动一个线程,那么这个线程就要运行TCPServerDispatcher的run函数了,看TCPServerDispatcher::run()

        }
                 

第9行waitDequeueNotification一个Notification,第12行把这个通知类型转换成TCPConnectionNotification,联系之前的enqueueNotification,大概能才到是什么意思。第15行又出现个TCPServerConnection。好吧,看来搞清楚TCPServerDispatcher还是要先看下TCPServerConnection,还有TCPConnectionNotification。

尽管TCPServerConnection继承了Runable,但没有实现run(),它的start()如下

void TCPServerConnection::start()
{
    try
    {
        run();
    }

用户需要继承TCPServerConnection实现run函数,看下源码中的说明,要完成对这个socket的处理,因为run函数返回时,连接就自动关闭了。

class Net_API TCPServerConnection: public Poco::Runnable
    /// 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的Reator框架。

Poco::TCPServer框架解析的更多相关文章

  1. 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记.

    说明 为何要写这篇文章 ,之前看过阿二的梦想船的<Poco::TCPServer框架解析> http://www.cppblog.com/richbirdandy/archive/2010 ...

  2. POCO TCPServer 解析

    POCO C++ Libraries提供一套 C++ 的类库用以开发基于网络的可移植的应用程序,功能涉及线程.文件.流,网络协议包括:HTTP.FTP.SMTP 等,还提供 XML 的解析和 SQL ...

  3. POCO库中文编程参考指南(10)如何使用TCPServer框架?

    1 TCPServer 框架概述 POCO 库提供TCPServer框架,用以搭建自定义的 TCP 服务器.TCPServer维护一个连接队列.一个连接线程池.连接线程用于处理连接,连接线程只要一空闲 ...

  4. [转载]iOS 10 UserNotifications 框架解析

    活久见的重构 - iOS 10 UserNotifications 框架解析 TL;DR iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ...

  5. ABP使用及框架解析系列 - [Unit of Work part.1-概念及使用]

    前言 ABP ABP是“ASP.NET Boilerplate Project”的简称. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开 ...

  6. ABP使用及框架解析系列 - [Unit of Work part.2-框架实现]

    前言 ABP ABP是“ASP.NET Boilerplate Project”的简称. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开 ...

  7. iOS 10 UserNotifications 框架解析

    摘自:https://onevcat.com/2016/08/notification/ iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ...

  8. Scrapy爬虫框架解析

    Scrapy框架解析 Scrapy框架大致包括以下几个组件:Scrapy Engine.Spiders.Scheduler.Item Pipeline.Downloader: 组件 Scrapy En ...

  9. 使用 STHTTPRequest 框架解析 Soap1.2 教程

    1.STHTTPRequest框架地址 https://github.com/nst/STHTTPRequest 将 STHTTPRequest .h  STHTTPRequest.m 文件拖入工程中 ...

随机推荐

  1. flexbox 伸缩布局盒

    Flexbox(伸缩布局盒) 是 CSS3 中一个新的布局模式,为了现代网络中更为复杂的网页需求而设计. Flexbox 由 伸缩容器 和 伸缩项目 组成.通过设置元素的 display 属性为   ...

  2. 雪碧图(sprite)

    雪碧图 是一种将网页上常用且不经常变动的小图标集中在一张大图中,根据网页需求来显示图片的技术. 可以提高网页加载速度,增加用户体验. 其原理是通过html块状元素建立一个满足需求的视图窗口,然后在窗口 ...

  3. hdu 5104 Primes Problem(prime 将三重循环化两重)

    //宁用大量的二维不用量小的三维 #include <iostream> #include<cstdio> #include<cstring> using name ...

  4. POJ_1698_Alice's Chance

    #include <iostream> #include <queue> #include <climits> #include <cstring> u ...

  5. codility上的练习(5)

    codility出了lesson 5了. (1) 合法括号序列,包括( [ { ) ] }这6种字符的字符串,长度N在[0..200000]范围内,为其是否合法. 要求时间复杂度O(N),空间复杂度O ...

  6. JAVA语言规范和API网址

    Java语言规范: http://docs.oracle.com/javase/specs/ Java API: http://docs.oracle.com/javase/8/docs/api/in ...

  7. PHP mysqli类

    <?php header("content-type:text/html;charset=utf-8"); $conn = new mysqli("localhos ...

  8. [转]iOS多线程编程之NSThread的使用

    1.简介: 1.1 iOS有三种多线程编程的技术,分别是: 1..NSThread 2.Cocoa NSOperation (iOS多线程编程之NSOperation和NSOperationQueue ...

  9. Linux串口编程详解(转)

    串口本身,标准和硬件 † 串口是计算机上的串行通讯的物理接口.计算机历史上,串口曾经被广泛用于连接计算机和终端设备和各种外部设备.虽然以太网接口和USB接口也是以一个串行流进行数据传送的,但是串口连接 ...

  10. 浅谈USB设备的VID和PID

    根据USB规范的规定,所有的USB设备都有供应商ID(VID)和产品识别码(PID),主机通过不同的VID和PID来区别不同的设备,VID和PID都是两个字节长,其中,供应商ID(VID)由供应商向U ...