一. 客户端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# 命名为client.py import socket import sys import time # Create a TCP/IP socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Connect the socket to the port where the server is listening # 注意修改适配目标主机的ip地址和监听端口 server_address = ('172.0.0.1', 20000) #print('connecting to {} port {}'.format(*server_address)) sock.connect(server_address) try: # Send data message = str(round(time.time()*1e6)) message = bytes(message, 'utf8') #print('sending {!r}'.format(message)) sock.sendall(message) # Look for the response amount_received = 0 amount_expected = len(message) while amount_received < amount_expected: data = sock.recv(32) amount_received += len(data) str_data = data.decode('utf8') #print(str_data) index = str_data.index('|') timestamp = str_data[:index] latency = str_data[index+1:] now = round(time.time()*1e6) diff = int(now)-int(timestamp) #print('Receive latency {0} peer latency:{1}'.format(diff, latency)) y1 = int(diff) x1 = int(latency) a = (x1-y1)*1.0/2 b = -(y1+x1)*1.0/2 print('Current machine clock - Target machine clock = {0} ns Network latency = {1} ns'.format(b,a)) finally: sock.close() |
二. 服务端程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# 命名为 server.py import socket import sys import time # Create a TCP/IP socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Bind the socket to the port server_address = ('0.0.0.0', 20000) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) print('starting up on {} port {}'.format(*server_address)) sock.bind(server_address) # Listen for incoming connections sock.listen(1) while True: # Wait for a connection print('waiting for a connection') connection, client_address = sock.accept() try: #print('connection from', client_address) # Receive the data in small chunks and retransmit it while True: data = connection.recv(32) if len(data) <= 0: break; #print('received {!r}'.format(data)) now = round(time.time()*1e6) diff = int(now)-int(data) send_data = str(now) + '|' + str(diff) send_data = bytes(str(send_data), 'utf8') if data: print('Send time:{0} Receive time:{1}'.format(int(data), int(now))) print('Receive latency: {0}'.format(int(now)-int(data))) connection.sendall(send_data) else: #print('no data from', client_address) break except Exception as e: print(e) continue finally: # Clean up the connection connection.close() |
运行: python3 server.py
三. 测试流程
将server.py放在其中一台主机上运行,然后将client.py放在另一台主机上运行,运行后客户端程序将打印网络时延及两端的时钟差。
四. 原理解析
假设有两台主机A,B。
A在以本机时钟为基准情形下记下自己在$t_1$时刻将包发往主机B,并将这个时间戳$t_1$传送给B,
B收到A的包之后,取出时间戳$t_1$,并以本机时钟为基准得到当前时间戳$t_2$,将$t_2-t_1$测得的结果$R$连带发包时刻$t_4$一起发往A。
A收到回包后的时间戳为$t_3$,拆解B发包时刻以及A->B的总耗时(即$t_2-t_1$),然后以各自的时钟为基准计算出B->A的总耗时$Q$,即$t_3 – t_4 = Q$。
设往返网络时延皆为$\alpha$,时钟差为$Clock_a – Clock_b = \delta$,则可得以下联立方程:
$t_1+\alpha = t_2 + \delta$
$t_2 – t_1 = R$
$t_4 + \delta = t_3 – \alpha$
$t_3 – t_4 = Q$
联立方程可解:
$\delta = – ( R + Q )/2$
$\alpha = (R – Q)/2$
方程解释:
其中$R,Q$结果表示以各自的时钟为基准,将时间戳作差计算出的收包耗时。
$t_1+\alpha = t_2 + \delta$ 表示主机A发包时刻$t_1$经过网络时延$\alpha$,以A主机为准得到的时间戳应为$ t_2 + \delta$
五. 注意事项
以上假设都是忽略了主机A,B的收发包时延, 以及CPU指令执行的时间. 如果AB收发包因为各种原因存在很大延时, 那么真正的时钟差及网络时延是很难计算出来的.