13-Roundtrip代码分析
仿照NTP原理,测量两台主机的时钟差,但是将原本T2和T3的合作一个时间点(由于T2和T3之间的间隔很小,对于非严格测试可以忽略)

代码分析(UDP, two threads)
-
消息格式
16B,T1和T2。是主机字节序。发送时客户端将T1填上,服务端接收后,将T2填上。
struct Message
{
int64_t request; // T1,客户端填上
int64_t response; // T2,服务端填上
} __attribute__ ((__packed__));
-
服务端
void runServer(bool ipv6)
{
Socket sock(Socket::createUDP(ipv6 ? AF_INET6 : AF_INET));
sock.bindOrDie(InetAddress(g_port, ipv6));
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);
}
}
}
-
客户端:不能像服务端一样用一个线程,先调用sendto后调用recvfrom,因为sendto可能会丢包,导致recvfrom一直堵塞。所以由两个线程组成,一个负责不断发request,一个辅助不断收response。
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()));
if (sock.connect(serverAddr) != 0)
{
perror("connect to server");
return;
}
// c++11线程,线程入口函数为lambda表达式,每个200ms就发送一个request
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);
}
});
while (true)
{
Message message = { 0, 0 };
int nr = sock.recv(&message, sizeof message);
if (nr == sizeof message)
{
int64_t back = now();
// 计算时间差
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);
}
}
}
代码出处
-
-
UDP with muduo, single thread
-
TCP with muduo
测试步骤
-
输入命令
ntptime和ntpq -qn查看本机时钟信息以及NTP校验。

-
以atom机为服务端,以e6400为客户端。客户端会依次打印当前时间戳(秒)、往返延迟(微妙)、时钟差(微妙)

扩展

浙公网安备 33010602011771号