6.4.2 echo 服务的实现(1)
muduo 的使用非常简单,不需要从指定的类派生,也不用覆写虚函数,只需要注册几个回调函数去处理前面提到的三个半事件就行了。
下面以经典的echo 回显服务为例:
1. 定义EchoServer class,不需要派生自任何基类。
- examples/simple/echo/echo.h
- 4 #include <muduo/net/TcpServer.h>
- 5
- 6 // RFC 862
- 7 class EchoServer
- 8 {
- 9 public:
- 10 EchoServer(muduo::net::EventLoop* loop,
- 11 const muduo::net::InetAddress& listenAddr);
- 12
- 13 void start(); // calls server_.start();
- 14
- 15 private:
- 16 void onConnection(const muduo::net::TcpConnectionPtr& conn);
- 17
- 18 void onMessage(const muduo::net::TcpConnectionPtr& conn,
- 19 muduo::net::Buffer* buf,
- 20 muduo::Timestamp time);
- 21
- 22 muduo::net::EventLoop* loop_;
- 23 muduo::net::TcpServer server_;
- 24 };
- examples/simple/echo/echo.h
在构造函数里注册回调函数。- examples/simple/echo/echo.cc
- 10 EchoServer::EchoServer(muduo::net::EventLoop* loop,
- 11 const muduo::net::InetAddress& listenAddr)
- 12 : loop_(loop),
- 13 server_(loop, listenAddr, "EchoServer")
- 14 {
- 15 server_.setConnectionCallback(
- 16 boost::bind(&EchoServer::onConnection, this, _1));
- 17 server_.setMessageCallback(
- 18 boost::bind(&EchoServer::onMessage, this, _1, _2, _3));
- 19 }
- examples/simple/echo/echo.cc
2. 实现EchoServer::onConnection() 和EchoServer::onMessage()。- examples/simple/echo/echo.cc
- 26 void EchoServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
- 27 {
- 28 LOG_INFO << "EchoServer - " << conn->peerAddress().toIpPort() << " -> "
- 29 << conn->localAddress().toIpPort() << " is "
- 30 << (conn->connected() "UP" : "DOWN");
- 31 }
- 32
- 33 void EchoServer::onMessage(const muduo::net::TcpConnectionPtr& conn,
- 34 muduo::net::Buffer* buf,
- 35 muduo::Timestamp time)
- 36 {
- 37 muduo::string msg(buf->retrieveAllAsString());
- 38 LOG_INFO << conn->name() << " echo " << msg.size() << " bytes, "
- 39 << "data received at " << time.toString();
- 40 conn->send(msg);
- 41 }
- examples/simple/echo/echo.cc
L37 和L40 是echo 服务的“业务逻辑”:把收到的数据原封不动地发回客户端。注意我们不用担心L40 的send(msg) 是否完整地发送了数据,因为muduo 网络库会帮我们管理发送缓冲区。
这两个函数体现了“基于事件编程(www.cppentry.com)”的典型做法,即程序主体是被动等待事件发生,事件发生之后网络库会调用(回调)事先注册的事件处理函数(event handler)。
在onConnection() 函数中, conn 参数是TcpConnection 对象的shared_ptr,TcpConnection::connected() 返回一个bool 值, 表明目前连接是建立还是断开,TcpConnection 的peerAddress() 和localAddress() 成员函数分别返回对方和本地的地址(以InetAddress 对象表示的IP 和port)。
在onMessage() 函数中,conn 参数是收到数据的那个TCP 连接;buf 是已经收到的数据,buf 的数据会累积,直到用户从中取走(retrieve)数据。注意buf是指针,表明用户代码可以修改(消费)buffer;time 是收到数据的确切时间,即epoll_wait(2) 返回的时间,注意这个时间通常比read(2) 发生的时间略早,可以用于正确测量程序的消息处理延迟。另外,Timestamp 对象采用pass-by-value,而不是pass-by-(const)reference,这是有意的,因为在x86-64 上可以直接通过寄存器传参。