zoukankan      html  css  js  c++  java
  • 关于 Poco::TCPServer框架 (windows 下使用的是 select模型) 学习笔记.

    说明

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

    无奈代码太多,看起繁琐.所以 准备 以流程图简化,便于理解.也方便自己以后使用.

    本文内容 是基于window api分析的.

    本文的poco是1.4.6p4 (2014-04-18)版本的. 虽然现在poco版本是1.6 但调用改动不大.

    poco下载地址:http://pocoproject.org/releases/

    本文分析以 TimeServer.cpp 作为入口分析:

    关于开始前的了解:

    1,Inline 内联函数:可以参考:

    http://blog.sina.com.cn/s/blog_90e888f50100zgo2.html

    主要是提升执行效率.

    2,类成员函数的 重载,重写,隐藏,

    参考:

    dazhong159 的<类成员函数的重载、重写、和覆盖区别>

    http://blog.csdn.net/dazhong159/article/details/7844369

    代码中大量使用,重写,隐藏.

    3,select模型的原理:

    引用

    很幽默的讲解六种Socket I/O模型

    http://blog.csdn.net/normalnotebook/article/details/999840

    的内容:

      for i:=0 to fd_read.fd_count-1 do //注意,fd_count <= 64,也就是说select只能同时管理最多64个连接

               是同步操作.

    老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱,看是否有女儿的信~~~~~
    在这种情况下,"下楼检查信箱"然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。
    select模型和老陈的这种情况非常相似:周而复始地去检查......如果有数据......接收/发送.......

    .....
     MainSock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
     addr.sin_family := AF_INET;
     addr.sin_port := htons(5678);
     addr.sin_addr.S_addr := htonl(INADDR_ANY);
     bind( MainSock, @addr, sizeof(addr) );
     listen( MainSock, 5 );
     
     while (not Terminated) do
     begin
     FD_ZERO( fd_read );
     FD_SET( MainSock, fd_read );
     timeout.tv_sec := 0;
     timeout.tv_usec := 500;
     if select( 0, @fd_read, nil, nil, @timeout ) > 0 then //至少有1个等待Accept的connection
     begin
     if FD_ISSET( MainSock, fd_read ) then
     begin
     for i:=0 to fd_read.fd_count-1 do //注意,fd_count <= 64,也就是说select只能同时管理最多64个连接
     begin
     len := sizeof(addr);
     ASock := accept( MainSock, addr, len );
     if ASock <> INVALID_SOCKET then
     ....//为ASock创建一个新的线程,在新的线程中再不停地select
     end; 
     end; 
     end; 
     end; //while (not self.Terminated)
     
     shutdown( MainSock, SD_BOTH );
     closesocket( MainSock );
     end;

    所以,select模型,只能用于一般的小型连接....高并发是不行的.

    4,

    对构造函数初始化顺序的理解

    C++构造函数按下列顺序被调用:
    (1)任何虚拟基类的构造函数按照它们被继承的顺序构造;
    (2)任何非虚拟基类的构造函数按照它们被继承的顺序构造;
    (3)任何成员对象的构造函数按照它们声明的顺序调用;
    (4)类自己的构造函数。

    5,关于 FastMutex 互斥变量

    bool NotificationQueue::empty() const
    {
    FastMutex::ScopedLock lock(_mutex);
    return _nfQueue.empty();
    }

    在 empty() 执行完成后 调用 ~FastMutex::ScopedLock析构函数来释放.

    window下是使用的临界区:

    class Foundation_API MutexImpl
    {
    protected:
    MutexImpl();
    ~MutexImpl();
    void lockImpl();
    bool tryLockImpl();
    bool tryLockImpl(long milliseconds);
    void unlockImpl();

    private:
    CRITICAL_SECTION _cs;//临界区
    };

       可以看见,windows 下环境使用的临界区.

    6,关于线程:

    window下使用

    _thread = (HANDLE) _beginthreadex(NULL, _stackSize, ent, this, 0, &threadId);

    执行线程的操作.

    7,等待事件,连接请求的同步是使用的 

    WaitForSingleObject(这也是我的最爱)

    通过SetEvent () ,ResetEvent() 来激活重置.

     

    8,static_cast<>  reinterpret_cast<> dynamic_cast<> 的使用.

    可参考:

    http://www.cnblogs.com/bastard/archive/2011/12/14/2288117.html

    http://www.cnblogs.com/jerry19880126/archive/2012/08/14/2638192.html

    像代码中:

     

    void* pThread;

    reinterpret_cast<ThreadImpl*>(pThread)->_pRunnableTarget->run();

    //reinterpret_cas 这个转换是最“不安全”的,两个没有任何关系的类指针之间转换都可以用这个转换实现,举个例子

     

    _threadId = static_cast<DWORD>(threadId);

    //static_cast 用于基本的数据类型转换(char,int),及指针之间的转换

     

    9,关于智能(灵巧)指针auto_ptr.

    auto_ptr 简单点说,就是 保证创建的资源 在退出时能被free(无论有没有异常)

    std::auto_ptr<TCPServerConnection> pConnection(_pConnectionFactory->createConnection(pCNf->socket()));

    AutoPtr<Notification> pNf = _queue.waitDequeueNotification(idleTime);

    可以直接 在<memory>中找到

    template<class _Ty>
    class auto_ptr
    { // wrap an object pointer to ensure destruction

     

    可以参考:

    More Effective C++中文版.pdf  7.4 Item M28:灵巧(smart)指针 章节(baidu 查到下载)

    http://blog.chinaunix.net/uid-9525959-id-2001803.html

    中的片段:

    如何避免使用auto_ptr的缺陷

        auto_ptr并不是完美无缺的,它的确很方便,但也有缺陷,在使用时要注意避免。首先,不要将auto_ptr对象作为STL容器的元素。C++标准明确禁止这样做,否则可能会碰到不可预见的结果。
        auto_ptr的另一个缺陷是将数组作为auto_ptr的参数:  
    auto_ptr<char>  pstr (new char[12] ); //数组;为定义
     
        记住不管什么时候使用数组的new操作时,必须要用delete[]来摧毁数组。因为auto_ptr的析构函数只对非数组类型起作用。所以数组是不能被正确摧毁的话,程序的行为是不明确的。总之,auto_ptr控制一个由new分配的单对象指针,仅此而已。

     不过C++ 11标准中解决了这问题:

    unique_ptr

    smart pointer with unique object ownership semantics

    只能有一个主人的指针,可以用于STL容器

    shared_ptr

    smart pointer with shared object ownership semantics

    可共享的指针

    weak_ptr

    weak reference to an object managed by std::shared_ptr

    弱引用指针

    auto_ptr

    smart pointer with strict object ownership semantics

    只能有一个主人的指针,不能用于STL容器

    走远了,想深入(不要想多了-_- ),请baidu...

     

     

     

     

    看完上面之些,发现是不是觉得 各种知识又巩固了.

    所以还是要看开源代码,之前公司整死不用开源的...哎...

    开始

    代码中主要使用类的关系

    图片过宽,不能显示(请 在新标签中打开图片.谢谢.)

    主要的几个类:

    1,TCPServer 主服务,负责 调用select 模型,来处理 连接消息的变化.

    2,PooledThread 是线程池.当被激活时,调用 TCPServerDispatcher::run() 来处理收到包后的具体请求.而 TCPServerDispatcher::run()  中调用 

        TimeServerConnection.run().  TimeServerConnection.run()通过子类隐藏 来实现 程序员 自定义 功能.  不得不说写POCO的大牛利害.

    3,TCPServerDispatcher,派遣管理者(先这么叫吧). 接收消息变化,放入队列.管理 连接数.

    当放入队列时,会激活  PooledThread 中的事件 .

    PooledThread 又反过来 激活 TCPServerDispatcher::run() [姑且叫 有条件时相互激活吧 ]

    4,TCPServerConnection.实现具体行为,通过继承由子类的 run() 来自定义实现 功能.

    5,TCPServerConnectionFactory 负责创建和管理 TCPServerConnection.

    6,TCPServerParams 这个参数管理 ,就不说了.你懂的.

    7,NotificationQueue 把 连接 放入队列.进行管理.

    看完主要几个类的介绍,其它流程都应该懂大概了.

    流程图:

    由于图太长的关系多,

    图片过宽,不能显示(请 在新标签中打开图片.谢谢.)

    先看看 PooledThread 的流程吧

     再看下TCPServer 主宰的流程
    图片过宽,不能显示(请 在新标签中打开图片.谢谢.)

     

    windows 下的select的确性能不太行,而linux 版本是用的epoll.

    epoll相对select 要高效点.可参考:http://blog.csdn.net/legion8169/article/details/1637154

    但poco tcpserver 中是有线程池操作的,所以说来效率不会太低.

    先到这儿,还没有写完.

    我们可以改变什么:

        ThreadPool(int minCapacity = 2,
            int maxCapacity = 16,
            int idleTime = 60,
            int stackSize = POCO_THREAD_STACK_SIZE);
            /// Creates a thread pool with minCapacity threads.
            /// If required, up to maxCapacity threads are created
            /// a NoThreadAvailableException exception is thrown.
            /// If a thread is running idle for more than idleTime seconds,
            /// and more than minCapacity threads are running, the thread
            /// is killed. Threads are created with given stack size.

    增加线程池中线程数(费话!),来加快select 中处理.

    在中等程序中,增加  TCPSelect Manage进程,  来负责与多个  TcpServer 的进程通信.

    即再增加一个管理者(中间键,或activemq之类),来加强并发能力,

    或者直接使用linux 环境 ,即用了epoll 来实现高效性.

    个人愚见,可能有些没写明白.还望高手指点.

    谢谢.

  • 相关阅读:
    Python爬虫连载1-urllib.request和chardet包使用方式
    Java连载69-接受输入、用数组模拟栈
    HTML连载61-焦点图、固定定位
    Java连载68-数组的拷贝、二维数组
    [Java] MVC
    [Java] JSP
    [Java] HTTP
    [设计模式] 设计模式课程(八)--抽象工厂模式
    [设计模式] 设计模式课程(七)--工厂模式
    [刷题] 75 Sort Colors
  • 原文地址:https://www.cnblogs.com/bleachli/p/4352959.html
Copyright © 2011-2022 走看看