muduo编程示例之五个简单TCP示例

前言

五个简单TCP示例,分别是discard,daytime,time,echo, chargen,这些功能描述如下:
discard: 丢弃所有收到的数据。
daytime:服务端accept连接之后,以字符串形式发送当前时间,然后主动断开连接。
time:服务端accept连接之后,以二进制形式发送当前时间,然后主动断开连接,我们需要一个客户程序来把收到的时间转换为字符串。
echo:回显服务,把收到的数据发送回客户端。
chargen:服务端accept连接之后,不停地发送测试数据。

在<<UNIX网络编程卷一>>中有提到,参考第二章2.12标准英特网服务
在这里插入图片描述


五个简单TCP

1. discard

比较简单,只关心"三个半事件"中的"消息/数据到达"事件,事件处理函数如下:

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

运行:

服务端:
root@ubuntu:/opt/muduo/examples/simple/discard# ./a.out 
20220528 17:05:13.611973Z 62510 INFO  pid = 62510 - main.cc:13
20220528 17:05:22.777054Z 62510 INFO  TcpServer::newConnection [DiscardServer] - new connection [DiscardServer-0.0.0.0:2009#1] from 192.168.1.14:57380 - TcpServer.cc:80
20220528 17:05:22.777182Z 62510 INFO  DiscardServer - 192.168.1.14:57380 -> 192.168.1.14:2009 is UP - discard.cc:25
20220528 17:05:25.590716Z 62510 INFO  DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757525.590698 - discard.cc:35
20220528 17:05:25.798819Z 62510 INFO  DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757525.798808 - discard.cc:35
20220528 17:05:26.020168Z 62510 INFO  DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757526.020155 - discard.cc:35
20220528 17:05:26.234700Z 62510 INFO  DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757526.234687 - discard.cc:35
20220528 17:05:26.425043Z 62510 INFO  DiscardServer-0.0.0.0:2009#1 discards 1 bytes received at 1653757526.425030 - discard.cc:35
20220528 17:05:30.313801Z 62510 INFO  DiscardServer-0.0.0.0:2009#1 discards 4 bytes received at 1653757530.313788 - discard.cc:35

客户端使用netcat的nc命令模拟:
root@ubuntu:/home/lvch# nc 192.168.1.14 2009




123

2.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();
  }
}

运行:

root@ubuntu:/opt/muduo/examples/simple/daytime# ./a.out 
20220528 17:10:57.295966Z 62523 INFO  pid = 62523 - main.cc:13
20220528 17:11:04.130141Z 62523 INFO  TcpServer::newConnection [DaytimeServer] - new connection [DaytimeServer-0.0.0.0:2013#1] from 192.168.1.14:49562 - TcpServer.cc:80
20220528 17:11:04.130248Z 62523 INFO  DaytimeServer - 192.168.1.14:49562 -> 192.168.1.14:2013 is UP - daytime.cc:26

`客户端:
root@ubuntu:/home/lvch# nc 192.168.1.14 2013
20220528 17:11:04.130295

3. echo

相对discard多增加一个conn->send(msg);

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

4. time

time协议与daytime即为类似,只不过它返回的不是日期时间字符串,而是一个32-bit整数,表示从1970-01-01 00:00:00Z到现在的秒数。当然这个协议有“2038年问题”。服务端只需要关注"三个半事件"中的
"连接已建立"事件,事件处理函数:

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::hostToNetwork32(static_cast<int32_t>(now));
    conn->send(&be32, sizeof be32);
    conn->shutdown();
  }
}

客户端:

#include "muduo/base/Logging.h"
#include "muduo/net/Endian.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/InetAddress.h"
#include "muduo/net/TcpClient.h"

#include <utility>

#include <stdio.h>
#include <unistd.h>

using namespace muduo;
using namespace muduo::net;

class TimeClient : noncopyable
{
 public:
  TimeClient(EventLoop* loop, const InetAddress& serverAddr)
    : loop_(loop),
      client_(loop, serverAddr, "TimeClient")
  {
    client_.setConnectionCallback(
        std::bind(&TimeClient::onConnection, this, _1));
    client_.setMessageCallback(
        std::bind(&TimeClient::onMessage, this, _1, _2, _3));
    // client_.enableRetry();
  }

  void connect()
  {
    client_.connect();
  }

 private:

  EventLoop* loop_;
  TcpClient client_;

  void onConnection(const TcpConnectionPtr& conn)
  {
    LOG_INFO << conn->localAddress().toIpPort() << " -> "
             << conn->peerAddress().toIpPort() << " is "
             << (conn->connected() ? "UP" : "DOWN");

    if (!conn->connected())
    {
      loop_->quit();
    }
  }

  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<const int32_t*>(data);
      buf->retrieve(sizeof(int32_t));
      time_t time = sockets::networkToHost32(be32);
      Timestamp ts(implicit_cast<uint64_t>(time) * Timestamp::kMicroSecondsPerSecond);
      LOG_INFO << "Server time = " << time << ", " << ts.toFormattedString();
    }
    else
    {
      LOG_INFO << conn->name() << " no enough data " << buf->readableBytes()
               << " at " << receiveTime.toFormattedString();
    }
  }
};

int main(int argc, char* argv[])
{
  LOG_INFO << "pid = " << getpid();
  if (argc > 1)
  {
    EventLoop loop;
    InetAddress serverAddr(argv[1], 2037);

    TimeClient timeClient(&loop, serverAddr);
    timeClient.connect();
    loop.loop();
  }
  else
  {
    printf("Usage: %s host_ip\n", argv[0]);
  }
}


5. chargen

协议比较特殊,只发送不接收数据。而且,它发送的数据的速度不能快过客户端接收的速度,因此需要关注"消息/数据发送完毕"事件(onWriteComplete)

`#include "examples/simple/chargen/chargen.h"

#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"

#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

ChargenServer::ChargenServer(EventLoop* loop,
                             const InetAddress& listenAddr,
                             bool print)
  : server_(loop, listenAddr, "ChargenServer"),
    transferred_(0),
    startTime_(Timestamp::now())
{
  server_.setConnectionCallback(
      std::bind(&ChargenServer::onConnection, this, _1));
  server_.setMessageCallback(
      std::bind(&ChargenServer::onMessage, this, _1, _2, _3));
  server_.setWriteCompleteCallback(
      std::bind(&ChargenServer::onWriteComplete, this, _1));
  if (print)
  {
    loop->runEvery(3.0, std::bind(&ChargenServer::printThroughput, this));
  }

  string line;
  for (int i = 33; i < 127; ++i)
  {
    line.push_back(char(i));
  }
  line += line;

  for (size_t i = 0; i < 127-33; ++i)
  {
    message_ += line.substr(i, 72) + '\n';
  }
}

void ChargenServer::start()
{
  server_.start();
}

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();
}

void ChargenServer::onWriteComplete(const TcpConnectionPtr& conn)
{
  transferred_ += message_.size();
  conn->send(message_);
}

void ChargenServer::printThroughput()
{
  Timestamp endTime = Timestamp::now();
  double time = timeDifference(endTime, startTime_);
  printf("%4.3f MiB/s\n", static_cast<double>(transferred_)/time/1024/1024);
  transferred_ = 0;
  startTime_ = endTime;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值