网络编程学习笔记4-Roundtrip代码分析
2023-02-18 16:42:14 时间
解释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]);
}
}
然后在虚拟机上自测以下,误差比视频的大一些
相关文章
- 常见算法合集[java源码+持续更新中...]
- 分布式事务(二)Java事务API(JTA)规范
- 北漂不易,逆风前行[给想在北京工作的java同学们诚恳的建议!]
- Java工程师的生活、读书、技术栈
- 十大基础排序算法[java源码+动静双图解析+性能分析]
- 分享两个免费下载Windows系统镜像
- CSRF-跨站请求伪造-相关知识
- 在VMware上安装Metasploitable2
- SSRF-服务器端请求伪造-相关知识
- RCE-远程命令执行和代码执行漏洞-知识
- 文件包含漏洞-知识点
- 包含漏洞-读写文件以及执行命令
- XXE-XML外部实体注入-知识点
- Ninjutsu_v3_08_2020-安全渗透系统安装
- 单兵渗透工具-Yakit-Windows安装使用
- SpringBoot-快速搭建并快速验证是否可用
- Metasploit-模块介绍
- Kali-登录暴力破解器工具-medusa使用
- PS软件下载及安装Photoshop cc 2018下载链接及安装教程(包括2023版本)
- 顺应时代趋势,CSN的安全可控之路