muduo网络库——五个简单编程示例

 

五个简单TCP示例:echo, discard, chargen, daytime, time

echo:回显服务,把收到的数据发回客户端。

discard:丢弃所有收到的数据。

chargen:服务端accept连接之后,不停地发送测试数据。

daytime:服务端accept连接之后,以字符串形式发送当前时间,然后主动断开连接。

time:服务端accept连接之后,以二进制形式发送当前事件,然后主动断开连接;需要一个客户程序把收到的时间转换为字符串。

 

discard

最简单的长连接TCP应用层协议,只需要关注“三个半事件”中的“消息/数据到达”事件,事件处理函数如下:

void DiscardServer::onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)
{
    string mas(buf->retrieveAllAsString());
    LOG_INFO << conn->name() << " discards " << msg.size() << " bytes received at " << time.toString();
}

 

 

daytime

daytime是短连接协议,在发送完当前事件后,服务端主动断开连接。只需要关注“三个半事件”中的“连接已建立”事件,事件处理函数如下:

void DaytimeServer::onConnection(const TcpConnectionPtr& conn)
{
    LOG_INFO << "DaytimeServer - " << conn->peerAddress().toIpPort() << " -> " << 
        conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN");
    
    if(conn->connected())
    {
        conn->send(Timestamp::now().toFormattedString() + "\n");
        conn->shutdown();
    }
}

 

 

time

time协议返回一个32-bit整数,表示从1970-01-01 00:00:00Z 到现在的秒数。只需要关注“连接已建立”事件,事件处理函数如下:

void TimeServer::onConnection(const muduo::net::TcpConnectionPtr& conn)
{
    LOG_INFO << "TimeServer - " << conn->peerAddress().toIpPort() << " -> " 
        << conn->localAddress().toIpPort() << " is " << (conn->connected() ? "UP" : "DOWN");
    if(conn->connected())        //连接建立之后开始发送时间
    {
        time_t now = ::time(NULL);        //取当前时间
        int32_t be32 = sockets::hostToNetword32(static_cast(now));    //转换为网络字节序
        conn->send(&be32, sizeof(be32);
        conn->shutdown():
    }
}

 

time客户端:因为time服务端发送的是二进制数据,我们编写一个客户端来解析并打印收到的4个字节数据。只需要关注“消息/数据到达”事件,事件处理函数如下:

void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)
{
    if(buf->readableBytes() >= sizeof(int32_t))
    {
        const void* data = buf->peek();
        int32_t be32 = *static_cast(data):
        buf->retrieve(sizeof(int32_t)):
        time_t time = sockets::networkToHost32(be32);
        Timestamp ts(time* Timestamp::kMicroSecondPerSecond):
        LOG_INFO << "Server time = " << time << ", " << ts.toFormattedString();
    }
    else
    {
        LOG_INFO << conn->name() << " no enough data " << buf->readableBytes()
            << " at " << receiveTime.toFormattedString();
    }
}

 

 

 

echo

echo是我们遇到的第一个双向的协议:服务端把客户端发过来的数据原封不动地传回去。只需要关注“消息/数据到达"事件,事件处理函数:

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):
}

这段代码不是行回显服务,而是有一点数据就发送一点数据。避免客户端恶意地不发送换行字符,而服务端又必须缓存已经收到的数据,导致服务器内存暴涨。

 

 

chargen

Chargen协议很特殊,只发送数据,不接受数据。只需要关注”三个半事件“中的半个”消息/数据发送完毕“事件,事件处理函数:

void ChargenServer::onConnection(const TcpConnectionPtr& conn)
{
    LOG_INFO << "ChargenServer - " << conn->peerAddress().toIpPort() << " -> " 
            << conn->localAddress().toIpPort() << " is "
            << (conn->connected() ? "UP" ? "DOWN");
    if(conn->connected())
    {
        conn->setTcpNoDelay(true);
        conn->send(message_);        //在连接建立时发送第一次数据
    }
}


void ChargenServer::onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp time)
{
    string msg(buf->retrieveAllAsString());
    LOG_INFO << conn->name() << " discards " << msg.size()
            << " bytes received at " << time.toString();
}

vond ChargenServer::onWriteComplete(const TcpConnectionPtr& conn)
{
    transferred_ += message_.size();
    conn->send(message_);            //继续发送数据
} 

 

五合一

前面五个程序都用到了EventLoop,这其实是个Reactor,用于注册和分发IO事件。muduo遵循one loop per thread模型,多个服务端和客户端共享同一个EventLoop,也可以分配到多个EventLoop以发挥多核多线程的好处。

这里我们把五个服务端用同一个EventLoop跑起来:

int main()
{
    LOG_INFO << "pid = " << getpid();
    EventLoop loop;        //one loop shared by multiple servers
    
    ChargenServer chargenServer(&loop, InetAddress(2019));
    chargenServer.start();

    DaytimeServer daytimeServer(&loop, InetAddress(2013));
    daytimeServer.start();
    
    DiscardServer discardServer(&loop, InetAddress(2009));
    discardServer.start();

    EchoServer echoServer(&loop, InetAddress(2007));
    echoServer.start();

    TimeServer timeServer(&loop, InetAddress(2037));
    timeServer.start();

    loop.loop();
}

这个例子充分展示了Reactor模式复用线程的能力,让一个单线程程序同时具备多个网络功能。

 

 

 

 

 

 

你可能感兴趣的:(moduo网络库,muduo源码分析)