muduo庫的簡單使用-echo服務的編寫
阿新 • • 發佈:2019-01-12
muduo是一個基於事件驅動的非阻塞網路庫,採用C++和Boost庫編寫。
它的使用方法很簡單,參考這篇文章:TCP網路程式設計本質論
裡面有這麼幾句:
我認為,TCP 網路程式設計最本質的是處理三個半事件: 連線的建立,包括服務端接受 (accept) 新連線和客戶端成功發起 (connect) 連線。 連線的斷開,包括主動斷開 (close 或 shutdown) 和被動斷開 (read 返回 0)。 訊息到達,檔案描述符可讀。這是最為重要的一個事件,對它的處理方式決定了網路程式設計的風格(阻塞還是非阻塞,如何處理分包,應用層的緩衝如何設計等等)。 訊息傳送完畢,這算半個。對於低流量的服務,可以不必關心這個事件;另外,這裡“傳送完畢”是指將資料寫入作業系統的緩衝區,將由 TCP 協議棧負責資料的傳送與重傳,不代表對方已經收到資料。
所以,使用muduo庫只需編寫上面幾處相關的邏輯即可。像套接字建立、epoll輪詢這種例行公事的程式碼,我們不必再編寫。
下面我們實現echo伺服器,echo的核心邏輯只有一個,那就是將收到的資訊回顯給對方,所以這裡我們只需關心訊息到達這個事件即可。
程式碼如下:
#include <muduo/base/Logging.h> #include <muduo/base/Timestamp.h> #include <muduo/net/TcpServer.h> #include <muduo/net/EventLoop.h> #include <muduo/net/TcpConnection.h> using namespace muduo; using namespace muduo::net; void onConnection(const TcpConnectionPtr &conn) { LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); } void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp time) { muduo::string msg(buf->retrieveAllAsString()); LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, " << "data received at " << time.toString(); conn->send(msg); } int main(int argc, const char *argv[]) { EventLoop loop; InetAddress addr("127.0.0.1", 8988); TcpServer server(&loop, addr, "echo"); server.setConnectionCallback(&onConnection); server.setMessageCallback(&onMessage); server.start(); loop.loop(); return 0; }
使用下列命令編譯:
g++ -o echo echo.cc -lmuduo_net -lmuduo_base -lpthread
客戶端採用netcat即可:
echo "hello" | nc localhost 8988
基於物件的使用方法
上面的使用方式是採用了全域性函式,我們還可以將echo伺服器封裝成一個類:
#include <muduo/net/TcpServer.h> #include <muduo/base/Logging.h> #include <boost/bind.hpp> #include <muduo/net/EventLoop.h> class EchoServer { public: EchoServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress& listenAddr); void start(); // calls server_.start(); private: void onConnection(const muduo::net::TcpConnectionPtr& conn); void onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp time); muduo::net::TcpServer server_; }; EchoServer::EchoServer(muduo::net::EventLoop* loop, const muduo::net::InetAddress& listenAddr) : server_(loop, listenAddr, "EchoServer") { server_.setConnectionCallback( boost::bind(&EchoServer::onConnection, this, _1)); server_.setMessageCallback( boost::bind(&EchoServer::onMessage, this, _1, _2, _3)); } void EchoServer::start() { server_.start(); } void EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn) { LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> " << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN"); } void EchoServer::onMessage(const muduo::net::TcpConnectionPtr& conn, muduo::net::Buffer* buf, muduo::Timestamp time) { // 接收到所有的訊息,然後回顯 muduo::string msg(buf->retrieveAllAsString()); LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, " << "data received at " << time.toString(); conn->send(msg); } int main() { LOG_INFO << "pid = " << getpid(); muduo::net::EventLoop loop; muduo::net::InetAddress listenAddr(2007); EchoServer server(&loop, listenAddr); server.start(); loop.loop(); }
後面陸續分析一些複雜的示例。