muduo庫的簡單使用
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();
}
后面陸續分析一些復雜的示例。