Linux C/C++网络编程实战-陈硕-笔记11-Roundtrip代码分析

代码

在这里插入图片描述

代码

  • 客户端与服务段发送的消息体是一个16字节的数据,其中前 8 字节的数据表示客户端的发送时间(T1)。后 8 字节数据表示 服务端接收到数据的时间(T2)。消息体被响应传回至客户端的时间为T3 。则可得到三个时间点 T1, T2, T3,从而计算两台机器的时延。
    // 消息体,结构体表示
    struct Message
    {
      int64_t request;
      int64_t response;
    }
    
  • roundtrip_udp
    #include "InetAddress.h"
    #include "Socket.h"
    
    #include <thread>
    
    #include <stdio.h>
    #include <string.h>
    #include <sys/time.h>
    #include <unistd.h>
    
    const int g_port = 3123;
    
    struct Message
    {
      int64_t request;
      int64_t response;
    } __attribute__ ((__packed__));
    
    static_assert(sizeof(Message) == 16, "Message size should be 16 bytes");
    
    // 时间获取函数
    // unix元年到现在的微秒数
    int64_t now()
    {
      struct timeval tv = { 0, 0 };
      gettimeofday(&tv, NULL);
      return tv.tv_sec * int64_t(1000000) + tv.tv_usec;
    }
    
    /* 服务端 */
    void runServer(bool ipv6)
    {
      Socket sock(Socket::createUDP(ipv6 ? AF_INET6 : AF_INET));    // 创建UDP socket
      sock.bindOrDie(InetAddress(g_port, ipv6));                    // 绑定到端口 3123
    
      while (true)
      {
        Message message = { 0, 0 };
    
        struct sockaddr_storage peerAddr;
        bzero(&peerAddr, sizeof peerAddr);
        socklen_t addrLen = sizeof peerAddr;
        struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&peerAddr);
    
        ssize_t nr = ::recvfrom(sock.fd(), &message, sizeof message, 0, addr, &addrLen);
        if (nr == sizeof message)
        {
          message.response = now();                                 // 设置时间T2,即服务器当前的时间
          ssize_t nw = ::sendto(sock.fd(), &message, sizeof message, 0, addr, addrLen);
          if (nw < 0)
          {
            perror("send Message");
          }
          else if (nw != sizeof message)
          {
            printf("sent message of %zd bytes, expect %zd bytes.\n", nw, sizeof message);
          }
        }
        else if (nr < 0)
        {
          perror("recv Message");
        }
        else
        {
          printf("received message of %zd bytes, expect %zd bytes.\n", nr, sizeof message);
        }
      }
    }
    
    /* 客户端 */
    void runClient(const char* server_hostname)
    {
      InetAddress serverAddr;
      if (!InetAddress::resolve(server_hostname, g_port, &serverAddr))
      {
        printf("Unable to resolve %s\n", server_hostname);
        return;
      }
    
      Socket sock(Socket::createUDP(serverAddr.family()));
    
      /* udp客户端建立了socket后可以直接调用sendto()函数向服务器发送数据,
      但是需要在sendto()函数的参数中指定目的地址/端口,但是可以调用connect()
      函数先指明目的地址/端口,然后就可以使用send()函数向目的地址发送数据了 */
      if (sock.connect(serverAddr) != 0)
      {
        perror("connect to server");
        return;
      }
      
      /* 使用线程去处理 */
      std::thread thr([&sock] () {
        while (true)
        {
          Message message = { 0, 0 };
          message.request = now();
          int nw = sock.send(&message, sizeof message);
          if (nw < 0)
          {
            perror("send Message");
          }
          else if (nw != sizeof message)
          {
            printf("sent message of %d bytes, expect %zd bytes.\n", nw, sizeof message);
          }
    
          ::usleep(200*1000);                                   // 200ms,一秒钟发5个包
        }
      });
    
      while (true)
      {
        Message message = { 0, 0 };
        int nr = sock.recv(&message, sizeof message);           // 读消息
        if (nr == sizeof message)
        {
          int64_t back = now();                                 // 设置时间 T3
          int64_t mine = (back + message.request) / 2;          // 计算时延
          printf("now %jd, round trip time %jd us, clock error %jd us\n",
                 back, back - message.request, message.response - mine);
        }
        else if (nr < 0)
        {
          perror("recv Message");
        }
        else
        {
          printf("received message of %d bytes, expect %zd bytes.\n", nr, sizeof message);
        }
      }
    }
    
    int main(int argc, const char* argv[])
    {
      if (argc < 2)
      {
        printf("Usage:\nServer: %s -s [-6]\nClient: %s server_hostname\n", argv[0], argv[0]);
        return 0;
      }
    
      if (strcmp(argv[1], "-s") == 0)
      {
        bool ipv6 = argc > 2 ? strcmp(argv[2], "-6") == 0 : false;
        runServer(ipv6);
      }
      else
      {
        runClient(argv[1]);
      }
    }
    
  • 编译
    g++ -o roundtrip_udp roundtrip_udp.cc Socket.cc InetAddress.cc -std=c++11 -lpthread
    
  • 运行
    在这里插入图片描述

    当前时间,精确到秒 往返延迟(微秒) 误差:(单位:微秒)
    now 1637145421885614 round trip 195 clock error 30
    now 1637145422085667 round trip 135 clock error 13
    now 1637145422285777 round trip 123 clock error 15
    now 1637145422485995 round trip 208 clock error 16
    now 1637145422692842 round trip 314 clock error 89
    now 1637145422893054 round trip 294 clock error 93
    now 1637145423093058 round trip 140 clock error 8
    now 1637145423293291 round trip 179 clock error -13
    // 时间戳:前10为表示到秒级(例如1637145423 =》2021-11-17 18:37:03)。
    秒级时间戳可以使用一些在线的工具转换成标准时间。全部16位时间戳表示精确到微秒级。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值