精华内容
下载资源
问答
  • evpp based echo server

    2018-05-03 18:05:20
    simple net framework , echo server use qihoo360 open source code evpp
  • evpp 简介 是一个现代C ++网络库,用于使用TCP / UDP / HTTP协议开发高性能的网络服务。 提供了一个TCP服务器来支持多线程非阻塞事件驱动服务器,还提供了一个HTTP,UDP服务器来支持HTTP和UDP协议。 特征 现代C ++ ...
  • evpp so load system

    2018-07-02 08:48:18
    load so and run. server listen, subprocess connect .
  • evpp是奇虎360内部使用的开源多线程网络库,集tcp/udp/http多种协议...本项目高度参考了muduo网络库,而底层使用现成的libevent库作为事件驱动库,典型的一个reactor网络编程模式的例子,本文就是通过分析evpp源码来...

            evpp是奇虎360内部使用的开源多线程网络库,集tcp/udp/http多种协议的服务器和客户端支持。github代码路径是:https://github.com/Qihoo360/evpp,可以不依赖boost库,使用现代c++14语言(evpp/invoke_timer.cc的lambda表达式使用到了c++14的特性)进行编码。本项目高度参考了muduo网络库(https://github.com/chenshuo/muduo),而底层使用现成的libevent库作为事件驱动库,典型的一个reactor网络编程模式的例子,本文就是通过分析evpp源码来达到学习c++网络编程的效果。

            muduo代码我也拜读过,muduo有个特点,它完全是为linux而写的,不支持windows,譬如里面用到了eventfd,timerfd以及epoll等,都是linux系统特有的,而且还跟linux版本有关,系统版本太低也不支持,譬如eventfd等,而evpp做了一定的平台兼容性,得益于libevent库,能一定程度做到支持windows平台。另外,muduo有个base库,是重复造轮子了,其他还好,也是一个不可多得的多线程网络服务器编程demo,值得参考,而evpp没有像muduo那样重新实现一套基础库(如线程库、互斥锁、条件变量等),而是直接利用了c++11/c++14自带的std::thread、std::mutex等,相对通用很多,而且学习这些类库使用在其他项目也能用得着,毕竟是c++的标准类库。

            摘抄了github上的说明,大家可以点进去前文给出的github路径阅读Readme:

            我们给出基础的TCP服务器和客户端的例子,http的例子请自行阅读。

    TCP服务器端:

    server.cc
    
    #include <evpp/tcp_server.h>
    #include <evpp/buffer.h>
    #include <evpp/tcp_conn.h>
    
    void OnConnection(const evpp::TCPConnPtr& conn) {
        if (conn->IsConnected()) {
            conn->SetTCPNoDelay(true);
        }
    }
    
    void OnMessage(const evpp::TCPConnPtr& conn,
                   evpp::Buffer* msg) {
        conn->Send(msg);
    }
    
    int main(int argc, char* argv[]) {
        std::string addr = "0.0.0.0:9099";
        int thread_num = 4;
    
        if (argc != 1 && argc != 3) {
            printf("Usage: %s <port> <thread-num>\n", argv[0]);
            printf("  e.g: %s 9099 12\n", argv[0]);
            return 0;
        }
    
        if (argc == 3) {
            addr = std::string("0.0.0.0:") + argv[1];
            thread_num = atoi(argv[2]);
        }
    
        evpp::EventLoop loop;
        evpp::TCPServer server(&loop, addr, "TCPPingPongServer", thread_num);
        server.SetMessageCallback(&OnMessage);
        server.SetConnectionCallback(&OnConnection);
        server.Init();
        server.Start();
        loop.Run();
        return 0;
    }

    客户端:

    client_fixed_size.cc
    
    // Modified from https://github.com/chenshuo/muduo/blob/master/examples/pingpong/client.cc
    //
    // Every time, we need to receive the whole message and then we can send the next one.
    //
    
    #include <iostream>
    
    #include <evpp/tcp_client.h>
    #include <evpp/event_loop_thread_pool.h>
    #include <evpp/buffer.h>
    #include <evpp/tcp_conn.h>
    
    class Client;
    
    class Session {
    public:
        Session(evpp::EventLoop* loop,
                const std::string& serverAddr/*ip:port*/,
                const std::string& name,
                size_t block_size,
                Client* owner)
            : client_(loop, serverAddr, name),
            owner_(owner),
            bytes_read_(0),
            bytes_written_(0),
            messages_read_(0),
            block_size_(block_size) {
            client_.SetConnectionCallback(
                std::bind(&Session::OnConnection, this, std::placeholders::_1));
            client_.SetMessageCallback(
                std::bind(&Session::OnMessage, this, std::placeholders::_1, std::placeholders::_2));
        }
    
        void Start() {
            client_.Connect();
        }
    
        void Stop() {
            client_.Disconnect();
        }
    
        int64_t bytes_read() const {
            return bytes_read_;
        }
    
        int64_t messages_read() const {
            return messages_read_;
        }
    
    private:
        void OnConnection(const evpp::TCPConnPtr& conn);
    
        void OnMessage(const evpp::TCPConnPtr& conn, evpp::Buffer* buf) {
            ++messages_read_;
            while (buf->size() >= block_size_) {
                bytes_read_ += block_size_;
                bytes_written_ += block_size_;
                conn->Send(buf->data(), block_size_);
                buf->Skip(block_size_);
            }
        }
    
    private:
        evpp::TCPClient client_;
        Client* owner_;
        int64_t bytes_read_;
        int64_t bytes_written_;
        int64_t messages_read_;
        size_t block_size_;
    };
    
    class Client {
    public:
        Client(evpp::EventLoop* loop,
               const std::string& name,
               const std::string& serverAddr, // ip:port
               int blockSize,
               int sessionCount,
               int timeout_sec,
               int threadCount)
            : loop_(loop),
            name_(name),
            session_count_(sessionCount),
            timeout_(timeout_sec),
            connected_count_(0) {
            loop->RunAfter(evpp::Duration(double(timeout_sec)), std::bind(&Client::HandleTimeout, this));
            tpool_.reset(new evpp::EventLoopThreadPool(loop, threadCount));
            tpool_->Start(true);
    
            for (int i = 0; i < blockSize; ++i) {
                message_.push_back(static_cast<char>(i % 128));
            }
    
            for (int i = 0; i < sessionCount; ++i) {
                char buf[32];
                snprintf(buf, sizeof buf, "C%05d", i);
                Session* session = new Session(tpool_->GetNextLoop(), serverAddr, buf, blockSize, this);
                session->Start();
                sessions_.push_back(session);
            }
        }
    
        ~Client() {
        }
    
        const std::string& message() const {
            return message_;
        }
    
        void OnConnect() {
            if (++connected_count_ == session_count_) {
                std::cout << "all connected" << std::endl;
            }
        }
    
        void OnDisconnect(const evpp::TCPConnPtr& conn) {
            if (--connected_count_ == 0) {
                std::cout << "all disconnected" << std::endl;
    
                int64_t totalBytesRead = 0;
                int64_t totalMessagesRead = 0;
                for (auto &it : sessions_) {
                    totalBytesRead += it->bytes_read();
                    totalMessagesRead += it->messages_read();
                }
                std::cout << "name=" << name_ << " " << totalBytesRead << " total bytes read" << std::endl;
                std::cout << "name=" << name_ << " " << totalMessagesRead << " total messages read" << std::endl;
                std::cout << "name=" << name_ << " " << static_cast<double>(totalBytesRead) / static_cast<double>(totalMessagesRead) << " average message size" << std::endl;
                std::cout << "name=" << name_ << " " << static_cast<double>(totalBytesRead) / (timeout_ * 1024 * 1024) << " MiB/s throughput" << std::endl;
                loop_->QueueInLoop(std::bind(&Client::Quit, this));
            }
        }
    
    private:
        void Quit() {
            tpool_->Stop();
            loop_->Stop();
            for (auto &it : sessions_) {
                delete it;
            }
            sessions_.clear();
            while (!tpool_->IsStopped() || !loop_->IsStopped()) {
                std::this_thread::sleep_for(std::chrono::seconds(1));
            }
            tpool_.reset();
        }
    
        void HandleTimeout() {
            std::cout << "stop" << std::endl;
            for (auto &it : sessions_) {
                it->Stop();
            }
        }
    private:
        evpp::EventLoop* loop_;
        std::string name_;
        std::shared_ptr<evpp::EventLoopThreadPool> tpool_;
        int session_count_;
        int timeout_;
        std::vector<Session*> sessions_;
        std::string message_;
        std::atomic<int> connected_count_;
    };
    
    void Session::OnConnection(const evpp::TCPConnPtr& conn) {
        if (conn->IsConnected()) {
            conn->SetTCPNoDelay(true);
            conn->Send(owner_->message());
            owner_->OnConnect();
        } else {
            owner_->OnDisconnect(conn);
        }
    }
    
    int main(int argc, char* argv[]) {
        if (argc != 7) {
            fprintf(stderr, "Usage: client <host_ip> <port> <threads> <blocksize> <sessions> <time_seconds>\n");
            return -1;
        }
    
        const char* ip = argv[1];
        uint16_t port = static_cast<uint16_t>(atoi(argv[2]));
        int threadCount = atoi(argv[3]);
        int blockSize = atoi(argv[4]);
        int sessionCount = atoi(argv[5]);
        int timeout = atoi(argv[6]);
    
        evpp::EventLoop loop;
        std::string serverAddr = std::string(ip) + ":" + std::to_string(port);
    
        Client client(&loop, argv[0], serverAddr, blockSize, sessionCount, timeout, threadCount);
        loop.Run();
        return 0;
    }

            server.cc很简单,收到什么就把返回什么,类似echo服务器,而client_fixed_size.cc比较复杂,下发一大段数据,收到服务器返回的数据后测量平均时间,pingpang性能测试,源码在evpp/benchmark/throughput/evpp/下。看得出来类的使用跟muduo如出一辙,也是抛弃c++的继承,积极使用"std::function+std::bind"或lambda来注册回调函数。作者也是致敬了一番muduo库。

    可以在ubuntu下开启两个shell终端,分别运行:

    ./server 9099 200

    以及

    ./client_fixed_size 127.0.0.1 9099 150 512 100 10
    #all disconnected
    #name=./client_fixed_size 357193728 total bytes read
    #name=./client_fixed_size 697644 total messages read
    #name=./client_fixed_size 512 average message size
    #name=./client_fixed_size 34.0646 MiB/s throughput
    

            从上面的客户端和服务器代码可以看到,evpp封装了两个比较常用的接口:SetConnectionCallback和SetMessageCallback。这两个回调所要处理的就是所谓的“应用层”,而库是底层,在写应用时,无需关注底层。也就是说evpp库分离了“用户逻辑”与“繁琐的tcp socket读写操作”。linux内核也是大量使用这种注册回调到框架的思路,以此实现“面向对象”和“机制与策略分离”。

    1,当TCPClient建立连接或存在的连接断开或建立连接失败时,就会回调我们注册的回调函数:

    // Set a connection event relative callback when the TCPClient
    // establishes a connection or an exist connection breaks down or failed to establish a connection.
    // When these three events happened, the value of the parameter in the callback is:
    //      1. Successfully establish a connection : TCPConn::IsConnected() == true
    //      2. An exist connection broken down : TCPConn::IsDisconnecting() == true
    //      3. Failed to establish a connection : TCPConn::IsDisconnected() == true and TCPConn::fd() == -1
    void TCPClient::SetConnectionCallback(const ConnectionCallback& cb);
    
    // Set the message callback to handle the messages from remote server
    void TCPClient::SetMessageCallback(const MessageCallback& cb);
    

    2,当TCPServer收到新连接或现有连接中断时,就会回调我们注册的回调函数:

    // Set a connection event relative callback when the TCPServer
    // receives a new connection or an exist connection breaks down.
    // When these two events happened, the value of the parameter in the callback is:
    //      1. Received a new connection : TCPConn::IsConnected() == true
    //      2. An exist connection broken down : TCPConn::IsDisconnecting() == true
    void TCPServer::SetConnectionCallback(const ConnectionCallback& cb);
    
    // Set the message callback to handle the messages from remote client
    void TCPServer::SetMessageCallback(MessageCallback cb);

            通俗地认为,SetConnectionCallback是处理刚连上或者掉线时的业务,譬如可以在连接上后保存evpp::TCPConnPtr(智能指针),断开连接就reset该TCPConnPtr,使其变为nullptr,用于后续send数据,参考evpp/examples/chatroom/simple底下的简易聊天app:client.cc、codec.h和server.cc:

    //参考evpp/examples/chatroom/simple/client.cc
    std::mutex mutex_;
    evpp::TCPConnPtr connection_;
    ...
    ...
    void OnConnection(const evpp::TCPConnPtr& conn) 
    {
        LOG_INFO << conn->AddrToString() << " is " << (conn->IsConnected() ? "UP" : "DOWN");
    
        std::lock_guard<std::mutex> lock(mutex_);
        if (conn->IsConnected()) 
        {
          connection_ = conn;
        } 
        else 
        {
          connection_.reset();
        }
    }
    
    void Write(const evpp::Slice& message) 
    {
        std::lock_guard<std::mutex> lock(mutex_);
        if (connection_) 
        {
          evpp::Buffer buf;
          buf.Append(message.data(), message.size());
          buf.PrependInt32(message.size());
          connection_->Send(&buf);
        }
    }
    
    ...
    ...
    std::string line = "hello world";
    Write(line);
    

            而SetMessageCallback则是用于处理接收对端的消息,数据能从evpp::Buffer类中得到,由于使用了reactor模式,这些回调函数都不能阻塞,想要延时一段时间处理的话,不能直接使用sleep(),可以注册超时时间,再比如处理一些会阻塞的任务时,可以自己做一个“任务队列+线程池”,不过这些都是编程手段,跟evpp库无关。

            TCPClient类TCPServer类怎么使用呢?

    作者在tcp_client.h里写得很清楚:
    1.创建一个TCPClient对象
    2.使用SetConnectionCallback()和SetMessageCallback()设置回调函数
    3.调用TCPClient::Connect()去尝试跟远端的服务器建立一个tcp连接
    4.使用TCPClient::Send去发送消息
    5.在回调函数中处理"连接"和"消息"
    6.想要关闭连接,只需调用TCPClient::Disonnect()

    而在tcp_server.h上也有描述:
    1.创建一个TCPServer对象
    2.使用SetConnectionCallback()和SetMessageCallback()设置回调函数
    3.调用TCPServer::Init()
    4.调用TCPServer::Start()
    5.在回调函数中处理"连接"和"消息"
    6.最后想要关闭服务,只需调用Server::Stop()

            上面列出的使用方法,可以在evpp/examples/*下找例子进行印证!

            在分析代码前,要先学会怎么使用!

     

    展开全文
  • trade server use evpp test

    2018-07-04 08:41:45
    use callprogram sys to call other system. modify vlogger
  • evpp是一个基于libevent开发的现代化C 11高性能网络服务器,自带TCP/UDP/HTTP等协议的异步非阻塞式的服务器和客户端库。
  • muduo很多人都听说过,那evpp可以理解成是muduo用C++11改写后的升级版。 相比muduo的代码风格,evpp会显得更加现代一点,更讨我们年轻人的喜欢。 作为例子,这里是一段TCP Echo Server的示例代码: std::string ...

    介绍

    muduo很多人都听说过,那evpp可以理解成是muduo用C++11改写后的升级版。
    相比muduo的代码风格,evpp会显得更加现代一点,更讨我们年轻人的喜欢。
    作为例子,这里是一段TCP Echo Server的示例代码:

        evpp::EventLoop loop;
        evpp::TCPServer server(&loop, "0.0.0.0:9099", "TCPEchoServer", thread_num);
        server.SetMessageCallback([](const evpp::TCPConnPtr& conn,
                                      evpp::Buffer* msg) {
            conn->Send(msg);
        });
        server.SetConnectionCallback([](const evpp::TCPConnPtr& conn) {
            if (conn->IsConnected()) {
                LOG_INFO << "A new connection from " << conn->remote_addr();
            } else {
                LOG_INFO << "Lost the connection from " << conn->remote_addr();
            }
        });
        server.Init();
        server.Start();
        loop.Run();

    evpp的核心代码其实不多,之前也已经花了一段时间去阅读;
    总的来说,代码本身还是十分通俗易懂的,不像某些天书C++;
    相比muduo,evpp一个很明显的变化就是,IO复用相关的抽象直接使用了libevent;
    但是,看完之后,总是缺少一个系统性的认识,因此,想借此机会进行梳理;
    此外,网上虽然有很多muduo的解析,但是介绍evpp的还是挺少的;
    对于习惯C++11风格的人,应该算是多了一种选择;
    https://github.com/Qihoo360/evpp

    核心类

    这里,我先罗列下evpp中的核心类

    • TCPServer
    • Listener
    • TCPClient
    • Connector
    • TCPConn
    • FdChannel
    • EventLoop
    • EventLoopThread
    • EventLoopThreadPool

    只要是熟悉Socket编程的同学,看见这几个类后应该还是有点亲切感的。
    其中,TcpClient/TcpServer分别抽象TCP客户端和服务器;Connector/Listener分别包装TCP客户端和服务器的建立连接/接受连接;TcpConnection抽象一个TCP连接,无论是客户端还是服务器只要建立了网络连接就会使用TcpConnection;FdChannel是设备fd的包装,在muduo中主要包装socket以及该socket对应的事件处理回调函数;EventLoop是一个事件发生器,它驱动底层的IO复用器产生/发现事件,然后将事件派发到Channel处理;EventLoopThread是一个带有EventLoop的线程;EventLoopThreadPool自然是一EventLoopThread的资源池,维护一堆EventLoopThread。

    TCPServer类

    这里,我们以自顶向下的方式进行梳理。所以,我们先来看看TCPServer的数据成员。

        // TCPServer的名字,比如"Echo", "Discard"等
        const std::string name_; 
    
        // TCPServer最重要的一点就是,创建ListenFD然后Accept新连接;
        // 接下来的3项,给出了必要的信息;
        // 第一项是“监听地址”,值得提的一点是,evpp借鉴了Golang,可以直接使用字符串指定IP地址;
        // 第二项是一个指针,指向Listener结构体;在evpp中,监听端口创建新连接 相关的细节都被封装在这个类之中;
        // 这个类对应至muduo中的acceptor类;另外一点就是,accepted的连接,在evpp中被抽象成TCPConn这个类,这个类我们稍后会提到;
        // 第三项就是事件驱动的引擎,可以理解成是对IO复用器的封装;
        // ListenFD会注册到该IO复用器上,当一个ListenFD上有新的连接时,该FD就会变成“可读”,从而回调“可读Callback”;
        // 当然,这个可读Callback执行的是创建TCPConn的逻辑,而不是读数据啥的;
        // 另外,这个loop_就是TCPServer_的主事件循环;
        const std::string listen_addr_; // ip:port
        std::unique_ptr<Listener> listener_;
        EventLoop* loop_;  // the listening loop
    
        // 一个EventLoop会绑定到一个特定的线程上运行;
        // 为了能够更充分地利用多核心CPU,直观的想法就是创建更多的线程(线程池),
        // 然后在每个线程上运行EventLoop;
        std::shared_ptr<EventLoopThreadPool> tpool_;
    
        ConnectionCallback conn_fn_;  // 连接建立/断开时的回调函数,由用户注册;
        MessageCallback msg_fn_;   // 当收到消息时的回调函数,由用户注册;
        DoneCallback stopped_cb_;
    
        // 一个TCPServer可以有多个TCP连接,下面的这两项就反映了这样一个客观现实;
        // TCPServer会为每个TCP连接分配一个ID;与此同时,以该ID为key将TCPConn保存起来;
        uint64_t next_conn_id_ = 0;
        std::map<uint64_t, TCPConnPtr> connections_;

    看完数据成员,我们再来看看TCPServer的开放接口;

    // 对于TCPServer的构造函数,当中无非是对我们刚刚提到的数据成员进行初始化;
    // 但是,值得提的一点是Eventloop是由用户分配的;
        TCPServer(EventLoop* loop,  
                  const std::string& listen_addr/*ip:port*/,
                  const std::string& name,
                  uint32_t thread_num);
    
    // 一个Server端正常的流程如下:创建套接字、绑定地址、设置成Listen状态、调用Accept开始接受链接
    // 在evpp/muduo的设计中,前三项被单独放在了Init()初始化函数中,而真正调用Accept的操作被单独放在了Start()当中;
        bool Init() {
            listener_.reset(new Listener(loop_, listen_addr_));
            listener_->Listen();
            status_.store(kInitialized);
        }
    
    // 在调用前面提到的Accept之前,Start()当中为listener指定了回调函数,
    // 当有新的连接到来时,这个回调函数就会被调用;
    // 值得提醒的是,到目前为止,在这几个初始化启动函数中,我们并没有发现与“业务”相关的逻辑;
    // 一个TCP服务器总有其业务逻辑,那么业务逻辑在哪里得到处理呢?
    // 让我们去看下TCPServer::HandleNewConn;
    // (这里再赘述下,HandleNewConn是TCPServer类中的函数,
    // 但是通过listener_->SetNewConnectionCallback()的接口被注册到listener_中,由listener_调用;
    // 这种操作方式,在evpp/muduo中是十分普遍的;接下来,我们还会看到更多的这样的例子;
    // 所以,我们应该留神这个callback到底是哪里来的;)
        bool Start() {
            tpool_->Start(true);
            listener_->SetNewConnectionCallback(...TCPServer::HandleNewConn...)
            listener_->Accept();
        }

    在接着讲HandleNewConn之前,我们先过一眼剩下的两个开放(public)接口;
    这两个函数看似十分简单,甚至是微不足道,但是它起到了 “封装业务逻辑”的巨大作用;
    用户首先单独编写“业务处理函数”,然后以“回调”的形式注册到框架中,
    实现“业务”和“框架”的解耦;
    稍后,我们就会立马看到它们的身影;

        void SetConnectionCallback(const ConnectionCallback& cb) { conn_fn_ = cb; }
        void SetMessageCallback(MessageCallback cb) { msg_fn_ = cb; }
    
    void TCPServer::HandleNewConn(evpp_socket_t sockfd,
                                  const std::string& remote_addr/*ip:port*/,
                                  const struct sockaddr_in* raddr) {
    // 接上文提到的,当Listener上有新的链接到来时,HandleNewConn函数会被回调;
    // 由于是事件驱动模型,一个TCP链接总是要对应到一个IO复用器(或者说Eventloop事件循环);
    // 因此,代码中的第一步就是获取一个Eventloop;
        EventLoop* io_loop = GetNextLoop(raddr);
    
    // 在evpp/muduo中,TCP连接被抽象成TCPConn类;
    // 既然现在有新连接到来,那我们new一个出来;
    // 抛去一个TCP连接应该具备的属性之外,在构造函数中我们还传递了io_loop;
        TCPConnPtr conn(new TCPConn(io_loop, ConnectionName, sockfd, listen_addr_, remote_addr, ++next_conn_id_));
    
    // 在这里,TCPServer用户通过Setter函数设置的回调函数,被进一步注册到了底层的TCPConn当中;
    // 这种回调函数一层一层往下注册的情况,接下来还会经常看到;
        conn->SetMessageCallback(msg_fn_);
        conn->SetConnectionCallback(conn_fn_);
    
    // 既然TCPConn本身初始化完毕,同时上头也指定了“当连接状态变化”或“当有数据到达时”该如何处理,
    // 那我们就把这个TCPConn Attach到事件循环当中;
    // 至此,一个TCP连接就算是正式建立,可以服务客户端了;
    // 这里剧透下,SockFD以及刚刚提到的“回调函数”实际上又被封装在了Channel这个类中;
    // 这个类,我们稍后再进行解释;
        io_loop->RunInLoop(std::bind(&TCPConn::OnAttachedToLoop, conn));
    
    // 每个TCPConn被分配一个ID,以这种形式TCPServer便于对TCPConn进行集中管理;
        connections_[conn->id()] = conn;
    }

    TCPClient类

    在熟悉了TCPServer之后,再去看对称的TCPClient类就会觉得轻车熟路;

        evpp::EventLoop loop;
        evpp::TCPClient client(&loop, "127.0.0.1:9099", "TCPPingPongClient");
        client.SetMessageCallback([&loop, &client](const evpp::TCPConnPtr& conn,
                                   evpp::Buffer* msg) {
            // 用户业务逻辑
        });
    
        client.SetConnectionCallback([](const evpp::TCPConnPtr& conn) {
            // 用户业务逻辑
        });
    
        client.Connect();
    
        loop.Run();

    在用户代码层面,二者唯一明显的区别在于,listen/accept的封装函数被替换成了Connect()函数;因此,让我们先来探一探Connect();

    // 还记得TCPServer中的Listener类吗? Listener类封装了监听套接字、创建新连接相关的细节;
    // 类似地,在TCPClient这边也有一个Connector类,该类封装了socket connect相关的细节;
    // 当用户调用client.Connect()时,将会发起异步的建立连接操作; 
    // 当TCP连接建立成功(或失败)时,TCPClient::OnConnection函数将会被回调;
    void TCPClient::Connect() {
        auto f = [this]() {
            connector_.reset(new Connector(loop_, this));  // 对Connector中的数据成员进行必要的赋值;
            connector_->SetNewConnectionCallback(...TCPClient::OnConnection...);        
            connector_->Start();
        };
        loop_->RunInLoop(f);  // 在loop_所在的线程中调用函数f
    }
    
    // 当TCP连接建立成功或失败时,将会回调TCPClient::OnConnection
    void TCPClient::OnConnection(evpp_socket_t sockfd, const std::string& laddr) {
        if (sockfd < 0) {  // 当TCP连接建立失败时,通过回调用户设置的回调函数conn_fn_,从而使得上层有机会对错误进行处理;`
            conn_fn_(TCPConnPtr(new TCPConn(loop_, "", sockfd, laddr, remote_addr_, 0)));
            return;
        }
    
        // 和TCPServer那边的情况类似,用户调用Setter函数设置的MessageCallback/ConnectionCallback,将会被进一步注册到TCPConn中;
        TCPConnPtr c = TCPConnPtr(new TCPConn(loop_, name_, sockfd, laddr, remote_addr_, id++));
        c->set_type(TCPConn::kOutgoing);
        c->SetMessageCallback(msg_fn_); 
        c->SetConnectionCallback(conn_fn_);
        c->SetCloseCallback(...TCPClient::OnRemoveConnection...);
    
        {
            std::lock_guard<std::mutex> guard(mutex_);
            conn_ = c;
        }
    
        // 到此为止,一个TCP连接就完成了建立操作;
        // 这时候,我们可以把这个TCP连接添加到事件循环中,并回调用户设置的conn_fn_函数;
        // 回调这一操作,使得“连接建立成功”这个事件能够被通知到上层用户;
        c->OnAttachedToLoop();
    }

    持续更新Ing

    参考文献

    https://github.com/Qihoo360/evpp
    https://www.cnblogs.com/gaorong/p/6476757.html
    https://blog.csdn.net/yusiguyuan/article/details/40593721
    https://blog.csdn.net/Shreck66/article/details/50945929
    https://blog.csdn.net/Shreck66/article/details/50948878
    http://www.cppblog.com/kevinlynx/archive/2014/05/04/206817.html

    展开全文
  • evpp的使用

    2020-03-24 08:44:01
    编译与安装 请参考下面的链接 https://blog.csdn.net/wei242425445/article/details/87968490 使用... } int main() { evpp::EventLoop loop; loop.RunEvery(evpp::Duration(1.0), &Start); loop.Run(); return 0; }

    编译与安装

    请参考下面的链接
    https://blog.csdn.net/wei242425445/article/details/87968490

    使用案例

    CMakeLists.txt文件的编写

    cmake_minimum_required(VERSION 2.8)
    
    project(demo)
    
    SET(CMAKE_BUILD_TYPE "Debug")
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread -g -Wall")
    
    aux_source_directory(. DIRSRCS)
    
    add_executable(demo ${DIRSRCS})
    target_link_libraries(demo libevpp.so)
    

    定时器的使用

    $ cat Main.cpp 
    #include <evpp/event_loop.h>
    
    void Start()
    {
        std::cout << "Hello,world..." << std::endl;
    }
    
    int main()
    {
        evpp::EventLoop loop;
        loop.RunEvery(evpp::Duration(1.0), &Start);
        loop.Run();
    
        return 0;
    }
    
    展开全文
  • 另外还有一种网络编程模型比较常用,就是“方案8”,这个模型在muduo中有现成方案,而在evpp中需要自己实现,时序图如下: 这种方案,只有一个EventLoop,但它把计算密集的部分(decode、compute、encode)分派到一...

            开局一张图!

            上图是盗用自《Linux多线程服务端编程,使用muduo C++网络库》一书6.6.2章节(以及下面的时序图也是盗用该书的图)。该图列举出大部分常用的网络编程模型,当然了,这里并没有列出Boost.Asio的proactor模式。其中表中的“互通”是指多个客户端(连接)间是否能方便地交换数据,如chat聊天程序;UNP是指经典的《Unix网络编程卷一:套接字联网API》一书章节。而我们的evpp库实际上是用到了“方案9”,方案9的时序图如下:

            可以看出,每一个线程有一个EventLoop处理事件。这种方案是典型的“one loop per thread”流程,有一个“主EventLoop”负责accept连接,然后把连接通过round-robin(轮询调度)挂到底下的多个“子EventLoop”中(EventLoopThreadPool),每个连接都是由一个“子EventLoop”完成的,能保证请求的顺序性,也可以充分利用CPU,防止出现一个reactor的处理能力饱和,而且EventLoop线程池线程数量固定,不会因为连接数过多到达临界点(线程太多导致操作系统线程调度不过来)而性能下降!

            另外还有一种网络编程模型比较常用,就是“方案8”,这个模型在muduo中有现成方案,而在evpp中需要自己实现,时序图如下:

            这种方案,只有一个EventLoop,但它把计算密集的部分(decode、compute、encode)分派到一个线程池中处理,处理完后再返回到EventLoop中,这样即使decode、compute、encode这三部分是阻塞的也不影响并发连接,正因为是异步处理,导致打乱了返回的顺序性,即同为连接1(同一个连接),先后下发请求1(如Conn 1 readaable1)和请求2(如Conn 1 readaable2),有可能请求2先于请求1返回数据(响应)。如果处理的事情没有优先级之分或者计算密集型(大部分时间都是处于计算中)可以使用这种方案,如果有优先级之分,应该使用方案9,方案9算是一个各方面都比较均衡的网络编程模式,evpp库就优先使用这种模式!

            evpp中比较重要的基础类有:

    EventLoop
    EventWatcher
    Buffer等

            EventLoop相关的类包含EventLoop、EventLoopThread和EventLoopThreadPool等类,它们提供事件循环、事件分发、为reactor网络编程模式提供一个基础框架。也提供了定时器,以及RunInLoop等接口,RunInLoop可在多线程环境下被调用,在本eventloop的IO线程内执行某个用户任务回调。我们提倡one loop per thread,顾名思义每个线程只能有一个EventLoop对象,因此EventLoop的构造函数会检查当前线程是否已经创建了其他EventLoop对象,遇到错误就终止程序。

            EventWatcher相关的类包含PipeEventWatcher、TimerEventWatcher和SignalEventWatcher能够注册管道、超时以及信号事件,底层是libevent2库。

            Buffer类则是类似libevent2中的evbuffer,供应用层读写,底下的socket读写操作也会频繁使用它。

            我们可以先阅读代码,证明evpp库是上述“方案9”的具体实现:

    int main(int argc, char* argv[]) {
        std::string addr = "0.0.0.0:9099";
        int thread_num = 4;
    
        evpp::EventLoop loop;
        evpp::TCPServer server(&loop, addr, "TCPPingPongServer", thread_num);
        server.SetMessageCallback(&OnMessage);
        server.SetConnectionCallback(&OnConnection);
        server.Init();
        server.Start();
        loop.Run();
    
        return 0;
    }
    

    这是一个简单的server端代码,先是创建了一个EventLoop对象loop,然后把用loop创建TCPServer对象,继续跟进去发现这个loop对象就是“方案9”时序图里的“Base IO Thread”。

            我们再来看TCPServer对象的构造函数:

    TCPServer::TCPServer(EventLoop* loop,
                         const std::string& laddr,
                         const std::string& name,
                         uint32_t thread_num)
        : loop_(loop)
        , listen_addr_(laddr)
        , name_(name)
        , conn_fn_(&internal::DefaultConnectionCallback)
        , msg_fn_(&internal::DefaultMessageCallback)
        , next_conn_id_(0) {
        tpool_.reset(new EventLoopThreadPool(loop_, thread_num));
    }

    它用“Base IO Thread”和线程数量作为参数创建了一个EventLoopThreadPool智能指针对象tpool_。

            我们看回main函数,它注册好必要的回调(后文会说到)后,调用server.Init()和server.Start()。

    init函数主要是创建了一个Listener对象,然后监听socket:

    bool TCPServer::Init() {
        listener_.reset(new Listener(loop_, listen_addr_));
        listener_->Listen();
        return true;
    }
    

    而Start函数则是重要函数:

    bool TCPServer::Start() {
        bool rc = tpool_->Start(true);
        if (rc) {
            listener_->SetNewConnectionCallback(
                std::bind(&TCPServer::HandleNewConn,
                          this,
                          std::placeholders::_1,
                          std::placeholders::_2,
                          std::placeholders::_3));
            listener_->Accept();
        }
        return rc;
    }

    它让EventLoopThreadPool启动,并且注册一个回调,用于当accept到一个客户端连接后就回调。

    最后我们来看下当Listener对象accept到一个连接后,回调函数HandleNewConn做了什么:

    void TCPServer::HandleNewConn(evpp_socket_t sockfd,
                                  const std::string& remote_addr/*ip:port*/,
                                  const struct sockaddr_in* raddr) {
        if (IsStopping()) {
            EVUTIL_CLOSESOCKET(sockfd);
            return;
        }
    
        EventLoop* io_loop = GetNextLoop(raddr);
        std::string n = remote_addr;
        ++next_conn_id_;
        TCPConnPtr conn(new TCPConn(io_loop, n, sockfd, listen_addr_, remote_addr, next_conn_id_));
        conn->SetMessageCallback(msg_fn_);
        conn->SetConnectionCallback(conn_fn_);
        conn->SetCloseCallback(std::bind(&TCPServer::RemoveConnection, this, std::placeholders::_1));
        io_loop->RunInLoop(std::bind(&TCPConn::OnAttachedToLoop, conn));
        connections_[conn->id()] = conn;
    }

    里面有一个重要函数GetNextLoop(),该函数返回的io_loop就是“方案9”时序图里面的“IO Thread n”,最后 利用io_loop这个EventLoop对象创建一个TCPConn智能指针对象等待io,跟“方案9”的思路如出一辙,也是“one loop per thread”,一个EventLoop挂一个连接。

    展开全文
  •  void HandleNewConn(evpp_socket_t sockfd, const std::string& remote_addr/*ip:port*/, const struct sockaddr_in* raddr);  EventLoop* GetNextLoop(const struct sockaddr_in* raddr); private:  EventLoop*...
  • C++开源库使用之evpp(一)

    千次阅读 2018-10-15 16:24:10
    配置安装与使用范例 1.1简介 evpp是一个基于libevent开发的现代化C++11高性能网络服务器,自带TCP/UDP/HTTP等...(由国内360公司开发并提供的C++开源库)发布在GitHub上:https://github.com/Qihoo360/evpp,更...
  • 上一篇博文中,我们从用户使用的角度入手,对evpp中TCPServer、TCPClient两大类进行了梳理。接下来,我们再来分析梳理下Listener、Connector两个类的细节。当然,这让我联想起了杨宗纬的那首歌“如果你愿意一层一层...
  • 使用方面,因为支持跨平台,通过vcpkg install evpp安装很方便,故在团队内推广,最近希望扩大其使用范围,奈何其不支持openssl和websocket,还需要自己动手。 本教程很多思路借鉴了 不败青铜: websocket协议实现及...
  • evpp心跳机制

    2019-06-03 14:14:00
    client server xin good 转载于:https://www.cnblogs.com/hshy/p/10967310.html
  • Ubuntu安装evpp

    2020-03-04 10:03:14
    $ sudo ldconfig evpp的安装 克隆 $ git clone https://github.com/Qihoo360/evpp 更新模块 $ cd evpp $ git submodule update --init --recursive 创建编译文件夹 $ mkdir build && cd build 编译 $ cmake .....
  • evpp::buffer使用

    2019-06-07 00:25:00
    evpp::Buffer testmsg; const char* buf123 = "12342333333333333333333333333323424324234234"; testmsg.Append(buf123, strlen(buf123)); conn->Send(&testmsg); 转载于:...
  • evpp在Windows下的简单使用
  • evpp是一个基于libevent开发的现代化的支持C++11特性的高性能网络库,自带TCP/UDP/HTTP等协议的异步非阻塞式的服务器和客户端库。特性如下: 现代版的C++11接口 非阻塞异步接口都是C++11的functional/bind形式的回调...
  • muduo是最近几年中国开源界里产生的优秀作品。它是由业内大牛陈硕实现的。...由于evpp本身是基于libevent2实现的,因此我们希望将evpp和muduo放到一起做一次全面的性能测试。本文是关于这两个库在吞吐量方面的测试。
  • benchmark/throughput/evpp ,代码如 https://github.com/Qihoo360/evpp/tree/master/benchmark/throughput/evpp 所示。并使用 tools 目录下的 benchmark-build.sh asio 的测试代码直接使用陈硕 recipes 的实现...
  • ubuntu 安装 evpp

    2019-04-16 18:29:00
    ubuntu 安装 evpp 来源 https://www.cnblogs.com/wisdomyzw/p/9402440.html Ubuntu虚拟机安装开源库evpp说明: EVPP为奇虎360基于libevent开发的现代化的支持C++11特性的高性能网络库,自带TCP/UDP/HTTP等协议...
  • ubuntu下编译evpp

    2020-01-31 16:29:28
    EVPP为奇虎360基于libevent开发的现代化的支持C++11特性的高性能网络库,自带TCP/UDP/HTTP等协议的异步非...evpp的编译需要依赖如下动态库: libevent glog gflags gtest boost 文章目录libevent glog gflags gtest...
  • Win7 编译EVPP

    2019-04-09 17:24:55
    vcpkg install evpp 把VS 2017更改为英文版 如果遇到下面的提示,需要把VS的环境更改为英文版 如果没有英文的选项,需要在安装程序把语言安装进来。 如果一直卡在一个安装包无法继续执行 -- ...
  • 安装evpp依赖库和工具 evpp官方快速教程 https://github.com/Qihoo360/evpp/blob/master/docs/quick_start.md evpp依赖库: libevent glog gtest (optional) boost (optional) gflags (optional) thrift (optional)...
  • 场景 使用evpp的evnsq的代码连接nsqlookupd进行Http查询,然后连接nsq,存在内存泄漏。...evpp/event_loop.h> int OnMessage(const evnsq::Message* msg) { LOG_INFO << "Received a message, i
  • evpp windows编译

    2019-05-05 10:32:00
    glog google-glog.sln 035版本 转载于:https://www.cnblogs.com/hshy/p/10811625.html
  • 下载直接打开evpp-master\vsprojects\libevpp.sln,库生成需要libevent和glog的支持,请自行解决依赖问题 错误1 仅生成evpp_static这个项目会提示time_weak错误 修改invoke_timer.cc内的代码 auto f = [this]...
  • https://github.com/Qihoo360/evpp 项目中有一个InvokeTimer对象,接口头文件详细代码请参见https://github.com/Qihoo360/evpp/blob/master/evpp/invoke_timer.h 。它是一个能自我管理定时器类,可以将一个仿函数...
  • evpp Introduction 中文说明 evpp is a modern C++ network library for developing high performance network services using TCP/UDP/HTTP protocols. evpp provides a TCP Server to support multi-...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 186
精华内容 74
关键字:

evpp