梁越

网络编程学习笔记4-Roundtrip代码分析

0 人看过

解释Rounftrip代码,视频p13笔记

本笔记参考的视频链接:https://www.bilibili.com/video/BV1Ht411p7wx?p=13
库链接:https://github.com/chenshuo/muduo

前面说的是一些网络时间同步的历史原理,我觉得没必要深入这个,所以我跳过了

接下来看一下代码,由于现在github上的代码和视频里解释的代码有差别,所以解释起来会有不一样的地方,但是本质还是一样的

视频里的

struct Message
{
int64_t request;
int64_t response;
}

改为使用int64_t message[2];

视频里while(true)的部分也提取成callback函数,包括服务端的和客户端

#include <muduo/base/Logging.h>
#include <muduo/net/Channel.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/Socket.h>
#include <muduo/net/SocketsOps.h>
#include <muduo/net/TcpClient.h>
#include <muduo/net/TcpServer.h>

#include <stdio.h>

using namespace muduo;
using namespace muduo::net;

const size_t frameLen = 2*sizeof(int64_t);

int createNonblockingUDP()  //创建UDP非阻塞socket
{
  int sockfd = ::socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_UDP);
  if (sockfd < 0)
  {
    LOG_SYSFATAL << "::socket";
  }
  return sockfd;
}

/////////////////////////////// Server ///////////////////////////////

void serverReadCallback(int sockfd, muduo::Timestamp receiveTime)
{
  int64_t message[2];  //时间信息数组
  struct sockaddr peerAddr;
  memZero(&peerAddr, sizeof peerAddr);
  socklen_t addrLen = sizeof peerAddr;
  ssize_t nr = ::recvfrom(sockfd, message, sizeof message, 0, &peerAddr, &addrLen);

  char addrStr[64];
  sockets::toIpPort(addrStr, sizeof addrStr, &peerAddr);
  LOG_DEBUG << "received " << nr << " bytes from " << addrStr;

  if (nr < 0)
  {
    LOG_SYSERR << "::recvfrom";
  }
  else if (implicit_cast<size_t>(nr) == frameLen)
  {
    message[1] = receiveTime.microSecondsSinceEpoch();  //修改response为本机当前时间
    ssize_t nw = ::sendto(sockfd, message, sizeof message, 0, &peerAddr, addrLen); //将修改的信息发回到客户端
    if (nw < 0)
    {
      LOG_SYSERR << "::sendto";
    }
    else if (implicit_cast<size_t>(nw) != frameLen)
    {
      LOG_ERROR << "Expect " << frameLen << " bytes, wrote " << nw << " bytes.";
    }
  }
  else
  {
    LOG_ERROR << "Expect " << frameLen << " bytes, received " << nr << " bytes.";
  }
}

void runServer(uint16_t port)
{
  Socket sock(createNonblockingUDP());
  sock.bindAddress(InetAddress(port));
  EventLoop loop;
  Channel channel(&loop, sock.fd());
  channel.setReadCallback(std::bind(&serverReadCallback, sock.fd(), _1));
  channel.enableReading();
  loop.loop();
}

/////////////////////////////// Client ///////////////////////////////

void clientReadCallback(int sockfd, muduo::Timestamp receiveTime)
{
  int64_t message[2];
  ssize_t nr = sockets::read(sockfd, message, sizeof message);

  if (nr < 0)
  {
    LOG_SYSERR << "::read";
  }
  else if (implicit_cast<size_t>(nr) == frameLen)  //收到长度刚好是两个int64_t
  {
    int64_t send = message[0];  //T1
    int64_t their = message[1];  //T2
    int64_t back = receiveTime.microSecondsSinceEpoch();  //T3
    int64_t mine = (back+send)/2;  
    LOG_INFO << "round trip " << back - send  //客户端自己计算的收发的时间
             << " clock error " << their - mine;   //T2-(T1+T3)/2
  }
  else
  {
    LOG_ERROR << "Expect " << frameLen << " bytes, received " << nr << " bytes.";
  }
}

void sendMyTime(int sockfd)
{
  int64_t message[2] = { 0, 0 };  //定义一个数组,第一个存放request,第二个存放response
  message[0] = Timestamp::now().microSecondsSinceEpoch();  //修改request为本机当前时间
  ssize_t nw = sockets::write(sockfd, message, sizeof message);
  if (nw < 0)
  {
    LOG_SYSERR << "::write";
  }
  else if (implicit_cast<size_t>(nw) != frameLen)
  {
    LOG_ERROR << "Expect " << frameLen << " bytes, wrote " << nw << " bytes.";
  }
}

void runClient(const char* ip, uint16_t port)
{
  Socket sock(createNonblockingUDP());
  InetAddress serverAddr(ip, port);
  int ret = sockets::connect(sock.fd(), serverAddr.getSockAddr());
  if (ret < 0)
  {
    LOG_SYSFATAL << "::connect";
  }
  EventLoop loop;
  Channel channel(&loop, sock.fd());
  channel.setReadCallback(std::bind(&clientReadCallback, sock.fd(), _1));  //while(true),不停接收信息
  channel.enableReading();
  loop.runEvery(0.2, std::bind(sendMyTime, sock.fd()));  //每0.2秒发送一个时间
  loop.loop();
}

int main(int argc, char* argv[])
{
  if (argc > 2)
  {
    uint16_t port = static_cast<uint16_t>(atoi(argv[2]));
    if (strcmp(argv[1], "-s") == 0)
    {
      runServer(port);
    }
    else
    {
      runClient(argv[1], port);
    }
  }
  else
  {
    printf("Usage:\n%s -s port\n%s ip port\n", argv[0], argv[0]);
  }
}

然后在虚拟机上自测以下,误差比视频的大一些