KLALB V3.4
This commit is contained in:
@@ -0,0 +1,429 @@
|
||||
package org.kne.cloud.network.ntp;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.fusesource.jansi.io.AnsiOutputStream.ZeroWidthSupplier;
|
||||
import org.kne.cloud.clock.HighAccuracyClock;
|
||||
import org.kne.cloud.clock.NTPTimestamps;
|
||||
import org.kne.cloud.network.MultipurposeSocketAddress;
|
||||
import org.kne.cloud.network.ntp.NTPv4Protocol.NTPPeer;
|
||||
import org.kne.math.Long128;
|
||||
|
||||
|
||||
public class NTPContext implements Closeable, AutoCloseable {
|
||||
private HighAccuracyClock clock;
|
||||
private static final boolean debug = true;
|
||||
private static final int REQUEST_COUNT = 10;
|
||||
|
||||
private BigInteger systemFrequencyOffset = NTPTimestamps.nanosToNtp128BitTimeInterval(BigInteger.valueOf(5000));
|
||||
private BigInteger localPrecision = NTPTimestamps.nanosToNtp128BitTimeInterval(BigInteger.valueOf(1000));
|
||||
private static final BigInteger adjustThreshold = BigInteger.valueOf(2000000L);
|
||||
|
||||
private int minStratum = 16;
|
||||
private volatile PeerInfo currentClock = new PeerInfo();
|
||||
|
||||
private volatile boolean closed = false;
|
||||
private Runnable send = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (!closed) {
|
||||
try {
|
||||
Map<NTPv4Protocol, List<NTPPeer>> mlp = new HashMap<>();
|
||||
ios.forEach((v) -> {
|
||||
mlp.put(v, v.getPeersWillSend());
|
||||
});
|
||||
for (int i = 0; i < REQUEST_COUNT; i++) {
|
||||
Set<Entry<NTPv4Protocol, List<NTPPeer>>> mlps = mlp.entrySet();
|
||||
for (Iterator<Entry<NTPv4Protocol, List<NTPPeer>>> iterator = mlps.iterator(); iterator
|
||||
.hasNext();) {
|
||||
Entry<NTPv4Protocol, List<NTPPeer>> object = iterator.next();
|
||||
List<NTPPeer> val = object.getValue();
|
||||
for (NTPPeer perr : val) {
|
||||
try {
|
||||
object.getKey().request(perr);
|
||||
} catch (IOException e) {
|
||||
if (debug)
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Thread.sleep(100);
|
||||
}
|
||||
Thread.sleep(1000);
|
||||
|
||||
if (!mlp.isEmpty())
|
||||
mergeAndApply();
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public NTPContext(HighAccuracyClock clock) {
|
||||
this.clock = clock;
|
||||
Thread ts = new Thread(send);
|
||||
ts.setName("NTPv4 Send Thread");
|
||||
ts.start();
|
||||
}
|
||||
|
||||
public void syncToSystem() {
|
||||
clock.syncToClock(new HighAccuracyClock());
|
||||
minStratum = 15;
|
||||
currentClock.stratum = Math.min(currentClock.stratum, minStratum);
|
||||
currentClock.leapIndicator = 0;
|
||||
}
|
||||
|
||||
public int getMinStratum() {
|
||||
return minStratum;
|
||||
}
|
||||
|
||||
public void setMinStratum(int minStratum) {
|
||||
this.minStratum = minStratum;
|
||||
}
|
||||
|
||||
public int getStratum() {
|
||||
return currentClock.stratum;
|
||||
}
|
||||
|
||||
public HighAccuracyClock getClock() {
|
||||
return clock;
|
||||
}
|
||||
|
||||
private ConcurrentHashMap<MultipurposeSocketAddress, List<NTPv4Packet>> recvmap = new ConcurrentHashMap<MultipurposeSocketAddress, List<NTPv4Packet>>();
|
||||
|
||||
protected void putPacket(NTPv4Packet nv4, MultipurposeSocketAddress inetSocketAddress) {
|
||||
checkIP();
|
||||
List<NTPv4Packet> newv = new Vector<NTPv4Packet>();
|
||||
List<NTPv4Packet> oldv = recvmap.putIfAbsent(inetSocketAddress, newv);
|
||||
if (oldv == null) {
|
||||
oldv = newv;
|
||||
}
|
||||
|
||||
synchronized (oldv) {
|
||||
oldv.add(nv4);
|
||||
while (oldv.size() > 8) {
|
||||
oldv.remove(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIP() {
|
||||
Set<Entry<MultipurposeSocketAddress, List<NTPv4Packet>>> ens = recvmap.entrySet();
|
||||
for (Iterator<Entry<MultipurposeSocketAddress, List<NTPv4Packet>>> iterator = ens.iterator(); iterator
|
||||
.hasNext();) {
|
||||
Entry<MultipurposeSocketAddress, List<NTPv4Packet>> entry = (Entry<MultipurposeSocketAddress, List<NTPv4Packet>>) iterator
|
||||
.next();
|
||||
AtomicBoolean ab = new AtomicBoolean(false);
|
||||
ios.forEach((x) -> {
|
||||
if (!ab.get())
|
||||
if (x.findPeer(entry.getKey()) != null) {
|
||||
ab.set(true);
|
||||
return;
|
||||
}
|
||||
});
|
||||
if (!ab.get()) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PeerInfo implements Comparable<PeerInfo> {
|
||||
private MultipurposeSocketAddress address;
|
||||
private int leapIndicator = 3;
|
||||
private int stratum = 16;
|
||||
private int referenceIdentifier;
|
||||
private int pollInterval;
|
||||
private byte precision;
|
||||
private long rootDelay = Integer.MAX_VALUE;
|
||||
private long rootDispersion = Integer.MAX_VALUE;
|
||||
private BigInteger referenceTimestamp = BigInteger.ZERO;
|
||||
private BigInteger uploadDelay;
|
||||
private BigInteger downloadDelay;
|
||||
private BigInteger rtt;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PeerInfo [address=" + address + ", leapIndicator=" + leapIndicator + ", stratum=" + stratum
|
||||
+ ", referenceIdentifier=" + referenceIdentifier + ", pollInterval=" + pollInterval + ", precision="
|
||||
+ precision + ", rootDelay=" + rootDelay + ", rootDispersion=" + rootDispersion
|
||||
+ ", referenceTimestamp=" + referenceTimestamp + ", uploadDelay=" + uploadDelay + ", downloadDelay="
|
||||
+ downloadDelay + "]";
|
||||
}
|
||||
|
||||
private long getRootDistance() {
|
||||
return rootDelay / 2 + rootDispersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PeerInfo o) {
|
||||
return Long.compare(getRootDistance(), o.getRootDistance());
|
||||
}
|
||||
|
||||
public BigInteger getCurrentSelfDispersion128() {
|
||||
BigInteger vk = (clock.getCurrentTimeNTP128().subtract(referenceTimestamp)).multiply(systemFrequencyOffset)
|
||||
.divide(BigInteger.TWO.pow(64));
|
||||
BigInteger vkl;
|
||||
if (vk.signum() < 0) {
|
||||
vkl = BigInteger.ZERO;
|
||||
} else {
|
||||
vkl = vk;
|
||||
}
|
||||
return vkl;
|
||||
}
|
||||
|
||||
public long getCurrentRootDispersion() {
|
||||
BigInteger vkl = getCurrentSelfDispersion128().shiftRight(16 + 32);
|
||||
long rez = rootDispersion + vkl.longValue();
|
||||
if (rez > Integer.MAX_VALUE) {
|
||||
rez = Integer.MAX_VALUE;
|
||||
}
|
||||
return rez;
|
||||
}
|
||||
|
||||
public BigInteger getAdj() {
|
||||
return NTPTimestamps.ntp128BitToNanosInterval(uploadDelay.subtract(downloadDelay).shiftRight(1));
|
||||
}
|
||||
}
|
||||
|
||||
public void mergeAndApply() {
|
||||
List<PeerInfo> peerInfo = mergeResponses();
|
||||
selectAndApply(peerInfo);
|
||||
}
|
||||
|
||||
private AtomicLong prevAdjustTime = new AtomicLong(System.nanoTime());
|
||||
private volatile BigInteger deltaRemaining;
|
||||
private long avgAdj=0;
|
||||
private void selectAndApply(List<PeerInfo> peerInfo) {
|
||||
Collections.sort(peerInfo);
|
||||
if(debug)
|
||||
System.out.println(peerInfo);
|
||||
if (!peerInfo.isEmpty()) {
|
||||
PeerInfo pix = peerInfo.get(0);
|
||||
currentClock = pix;
|
||||
int i;
|
||||
int m = Math.min(peerInfo.size(), 3);
|
||||
BigInteger bi = BigInteger.ZERO;
|
||||
for (i = 0; i < m; i++) {
|
||||
PeerInfo pi = peerInfo.get(i);
|
||||
BigInteger adjt = pi.getAdj();
|
||||
bi = bi.add(adjt);
|
||||
}
|
||||
BigInteger adj = bi.divide(BigInteger.valueOf(i));
|
||||
avgAdj=(avgAdj*7+adj.abs().longValue())/8;
|
||||
BigInteger adjustment;
|
||||
if (adj.abs().compareTo(adjustThreshold) > 0) {
|
||||
adjustment = new BigDecimal(adj).multiply(BigDecimal.valueOf(0.5)).toBigInteger();
|
||||
} else {
|
||||
adjustment = new BigDecimal(adj).multiply(BigDecimal.valueOf(0.0625)).toBigInteger();
|
||||
if (deltaRemaining != null) {
|
||||
long curr = System.nanoTime();
|
||||
long old = prevAdjustTime.getAndSet(curr);
|
||||
BigInteger fadj = adjustment.multiply(BigInteger.valueOf(TimeUnit.SECONDS.toNanos(1)))
|
||||
.divide(BigInteger.valueOf(curr - old));
|
||||
BigInteger fadjustment = new BigDecimal(fadj).multiply(BigDecimal.valueOf(0.015625/4)).toBigInteger();
|
||||
clock.adjustFrequency(fadjustment.longValue());
|
||||
if(debug)
|
||||
System.out.println("fadj:" + fadjustment + " f:" + clock.getFrequency());
|
||||
}
|
||||
deltaRemaining = adj.subtract(adjustment);
|
||||
}
|
||||
clock.adjustClock(Long128.valueOf( adjustment));
|
||||
if(debug)
|
||||
System.out.println("delta:" + adjustment + " deltaRemaining:" + deltaRemaining);
|
||||
}
|
||||
}
|
||||
|
||||
private List<PeerInfo> mergeResponses() {
|
||||
List<PeerInfo> peerInfo = new ArrayList<PeerInfo>();
|
||||
Set<Entry<MultipurposeSocketAddress, List<NTPv4Packet>>> ens = recvmap.entrySet();
|
||||
for (Iterator<Entry<MultipurposeSocketAddress, List<NTPv4Packet>>> iterator = ens.iterator(); iterator
|
||||
.hasNext();) {
|
||||
Entry<MultipurposeSocketAddress, List<NTPv4Packet>> entry = (Entry<MultipurposeSocketAddress, List<NTPv4Packet>>) iterator
|
||||
.next();
|
||||
|
||||
List<NTPv4Packet> newv = entry.getValue();
|
||||
PeerInfo pi = null;
|
||||
for (NTPv4Packet pack : newv) {
|
||||
int li = pack.getLeapIndicator();
|
||||
if (li == 3) {
|
||||
continue;
|
||||
}
|
||||
int stratum = pack.getStratum();
|
||||
if (stratum == 0) {
|
||||
stratum = 16;
|
||||
}
|
||||
int mode = pack.getMode();
|
||||
switch (mode) {
|
||||
case NTPv4Packet.NTP_SERVER:
|
||||
stratum += 1;
|
||||
break;
|
||||
case NTPv4Packet.NTP_SYMMETRIC_PASSIVE:
|
||||
if (stratum < minStratum)
|
||||
stratum += 1;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
if (stratum >= 16) {
|
||||
continue;
|
||||
}
|
||||
if (pi == null) {
|
||||
pi = new PeerInfo();
|
||||
pi.address = entry.getKey();
|
||||
}
|
||||
pi.leapIndicator = pack.getLeapIndicator();
|
||||
pi.stratum = stratum;
|
||||
pi.referenceIdentifier = pack.getReferenceIdentifier();
|
||||
pi.pollInterval = pack.getPollInterval();
|
||||
pi.precision = pack.getPrecision();
|
||||
pi.rootDelay = pack.getRootDelay();// (1/65536.0*1000000000)
|
||||
pi.rootDispersion = pack.getRootDispersion();
|
||||
pi.referenceTimestamp = pack.getReferenceTimestamp128();
|
||||
BigInteger uploadD = pack.getReceiveTimestamp128().subtract(pack.getOriginateTimestamp128());
|
||||
BigInteger downloadD = pack.getDestinationTimestamp128().subtract(pack.getTransmitTimestamp128());
|
||||
BigInteger rtt = uploadD.add(downloadD);
|
||||
if (pi.uploadDelay == null) {
|
||||
pi.uploadDelay = uploadD;
|
||||
} else {
|
||||
if (pi.uploadDelay.compareTo(uploadD) > 0) {
|
||||
pi.uploadDelay = uploadD;
|
||||
}
|
||||
}
|
||||
if (pi.downloadDelay == null) {
|
||||
pi.downloadDelay = downloadD;
|
||||
} else {
|
||||
if (pi.downloadDelay.compareTo(downloadD) > 0) {
|
||||
pi.downloadDelay = downloadD;
|
||||
}
|
||||
}
|
||||
if (pi.rtt == null) {
|
||||
pi.rtt = rtt;
|
||||
} else {
|
||||
if (pi.rtt.compareTo(rtt) > 0) {
|
||||
pi.rtt = rtt;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pi != null) {
|
||||
|
||||
long ndl = pi.rootDelay + (pi.rtt).shiftRight(32 + 16).longValue();
|
||||
// System.out.println(pi.rtt+" "+pi.rootDelay+" "+ndl);
|
||||
pi.rootDelay = Math.min(ndl, Integer.MAX_VALUE);
|
||||
long ndsp = pi.rootDispersion + Math.max(localPrecision.shiftRight(16 + 32).longValue(), 1);
|
||||
pi.rootDispersion = Math.min(ndsp, Integer.MAX_VALUE);
|
||||
peerInfo.add(pi);
|
||||
}
|
||||
}
|
||||
return peerInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NTPContext [clock=" + clock + ", systemFrequencyOffset=" + systemFrequencyOffset + ", minStratum="
|
||||
+ minStratum + ", leapIndicator=" + currentClock.leapIndicator + ", stratum=" + currentClock.stratum
|
||||
+ ", referenceIdentifier=" + currentClock.referenceIdentifier + ", pollInterval="
|
||||
+ currentClock.pollInterval + ", localPrecision=" + localPrecision + ", rootDelay="
|
||||
+ currentClock.rootDelay + ", rootDispersion=" + currentClock.rootDispersion + ", referenceTimestamp="
|
||||
+ NTPTimestamps.ntp128ToString(currentClock.referenceTimestamp) + "]";
|
||||
}
|
||||
|
||||
public BigInteger getSystemFrequencyOffset() {
|
||||
return systemFrequencyOffset;
|
||||
}
|
||||
|
||||
public void setSystemFrequencyOffset(BigInteger systemFrequencyOffset) {
|
||||
this.systemFrequencyOffset = systemFrequencyOffset;
|
||||
}
|
||||
|
||||
public BigInteger getLocalPrecision() {
|
||||
return localPrecision;
|
||||
}
|
||||
|
||||
public void setLocalPrecision(BigInteger localPrecision) {
|
||||
this.localPrecision = localPrecision;
|
||||
}
|
||||
|
||||
public int getLeapIndicator() {
|
||||
return currentClock.leapIndicator;
|
||||
}
|
||||
|
||||
public int getReferenceIdentifier() {
|
||||
return currentClock.referenceIdentifier;
|
||||
}
|
||||
|
||||
public int getPollInterval() {
|
||||
return currentClock.pollInterval;
|
||||
}
|
||||
|
||||
public long getRootDelay() {
|
||||
return currentClock.rootDelay;
|
||||
}
|
||||
|
||||
public long getRootDispersion() {
|
||||
return currentClock.rootDispersion;
|
||||
}
|
||||
|
||||
public BigInteger getReferenceTimestamp() {
|
||||
return currentClock.referenceTimestamp;
|
||||
}
|
||||
|
||||
public BigInteger getReferenceTimestamp64() {
|
||||
return NTPTimestamps.ntp128To64(currentClock.referenceTimestamp);
|
||||
}
|
||||
|
||||
public BigInteger getCurrentSelfDispersion128() {
|
||||
|
||||
return currentClock.getCurrentSelfDispersion128();
|
||||
}
|
||||
|
||||
public long getCurrentRootDispersion() {
|
||||
|
||||
return currentClock.getCurrentRootDispersion();
|
||||
}
|
||||
|
||||
private static Set<NTPv4Protocol> ios = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
public void registerIO(NTPv4Protocol ntPv4Protocol) {
|
||||
ios.add(ntPv4Protocol);
|
||||
}
|
||||
|
||||
public void unregisterIO(NTPv4Protocol ntPv4Protocol) {
|
||||
ios.remove(ntPv4Protocol);
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,513 @@
|
||||
package org.kne.cloud.network.ntp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.kne.cloud.clock.AdjustedNanoClock;
|
||||
import org.kne.cloud.clock.HighAccuracyClock;
|
||||
import org.kne.cloud.clock.NTPTimestamps;
|
||||
import org.kne.cloud.network.NetworkPacket;
|
||||
import org.kne.io.KNEChannels;
|
||||
|
||||
public class NTPv4Packet extends NetworkPacket {
|
||||
public static final int NTP_VERSION_NUMBER = 4;
|
||||
public static final int NTP_DEFAULT_PORT = 123;
|
||||
|
||||
public static final int NTP_SYMMETRIC_ACTIVE = 1;
|
||||
|
||||
public static final int NTP_SYMMETRIC_PASSIVE = 2;
|
||||
|
||||
public static final int NTP_CLIENT = 3;
|
||||
|
||||
public static final int NTP_SERVER = 4;
|
||||
|
||||
|
||||
public static final int BASE_HEADER_LENGTH = 48; // 基本头部长度
|
||||
public static final int AUTH_FIELD_LENGTH = 20; // 认证字段长度
|
||||
private static final BigInteger TWO_POW_64 = BigInteger.TWO.pow(64);
|
||||
|
||||
private ByteBuffer header = NetworkPacket.bufferAllocator.allocate(BASE_HEADER_LENGTH);
|
||||
private List<ExtensionField> extensionFields = new ArrayList<>();
|
||||
private AuthenticationField authField;
|
||||
|
||||
|
||||
private BigInteger destinationTimeStamp;
|
||||
private HighAccuracyClock clock;
|
||||
|
||||
public NTPv4Packet(HighAccuracyClock clock) {
|
||||
initializeHeader();
|
||||
this.clock=clock;
|
||||
}
|
||||
|
||||
private void initializeHeader() {
|
||||
// 设置默认值:版本4,模式3(客户端)
|
||||
header.put((byte) (0x23)); // LI=0, VN=4, Mode=3
|
||||
header.put((byte) 0); // Stratum
|
||||
header.put((byte) 0); // Poll
|
||||
header.put((byte) 0); // Precision
|
||||
|
||||
// 根延迟、根分散、参考标识符初始为0
|
||||
for (int i = 4; i < 16; i++) {
|
||||
header.put((byte) 0);
|
||||
}
|
||||
|
||||
// 时间戳字段初始为0
|
||||
for (int i = 16; i < BASE_HEADER_LENGTH; i++) {
|
||||
header.put((byte) 0);
|
||||
}
|
||||
|
||||
header.flip();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTotalLength() {
|
||||
long length = BASE_HEADER_LENGTH;
|
||||
|
||||
// 添加扩展字段长度
|
||||
for (ExtensionField ext : extensionFields) {
|
||||
length += ext.getLength();
|
||||
}
|
||||
|
||||
// 添加认证字段长度
|
||||
if (authField != null) {
|
||||
length += AUTH_FIELD_LENGTH;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToChannel(WritableByteChannel dto) throws IOException {
|
||||
// 写入基本头部
|
||||
dto.write(header.slice(0, header.limit()));
|
||||
|
||||
// 写入扩展字段
|
||||
for (ExtensionField ext : extensionFields) {
|
||||
ext.writeToChannel(dto);
|
||||
}
|
||||
|
||||
// 写入认证字段
|
||||
if (authField != null) {
|
||||
authField.writeToChannel(dto);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFromChannel(ReadableByteChannel din, long length) throws IOException {
|
||||
destinationTimeStamp=clock.getCurrentTimeNTP128();
|
||||
// 读取基本头部
|
||||
header.clear().limit(BASE_HEADER_LENGTH);
|
||||
KNEChannels.readFully(din, header);
|
||||
header.flip();
|
||||
|
||||
// 计算剩余长度
|
||||
long remaining = length - BASE_HEADER_LENGTH;
|
||||
|
||||
// 读取扩展字段(如果有)
|
||||
extensionFields.clear();
|
||||
while (remaining > 0) {
|
||||
// 检查是否是认证字段(认证字段有固定格式)
|
||||
if (remaining >= AUTH_FIELD_LENGTH) {
|
||||
// 这里简化处理,实际应该根据协议判断
|
||||
break;
|
||||
}
|
||||
|
||||
ExtensionField ext = new ExtensionField();
|
||||
ext.readFromChannel(din, remaining);
|
||||
extensionFields.add(ext);
|
||||
remaining -= ext.getLength();
|
||||
}
|
||||
|
||||
// 读取认证字段(如果有)
|
||||
if (remaining >= AUTH_FIELD_LENGTH) {
|
||||
authField = new AuthenticationField();
|
||||
authField.readFromChannel(din, AUTH_FIELD_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needEndPosition() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// ================== NTP 头部字段访问方法 ==================
|
||||
|
||||
public int getLeapIndicator() {
|
||||
return (header.get(0) & 0xC0) >> 6;
|
||||
}
|
||||
|
||||
public void setLeapIndicator(int li) {
|
||||
byte value = header.get(0);
|
||||
value = (byte) ((value & 0x3F) | ((li & 0x03) << 6));
|
||||
header.put(0, value);
|
||||
}
|
||||
|
||||
public int getVersionNumber() {
|
||||
return (header.get(0) & 0x38) >> 3;
|
||||
}
|
||||
|
||||
public void setVersionNumber(int vn) {
|
||||
byte value = header.get(0);
|
||||
value = (byte) ((value & 0xC7) | ((vn & 0x07) << 3));
|
||||
header.put(0, value);
|
||||
}
|
||||
|
||||
public int getMode() {
|
||||
return header.get(0) & 0x07;
|
||||
}
|
||||
|
||||
public void setMode(int mode) {
|
||||
byte value = header.get(0);
|
||||
value = (byte) ((value & 0xF8) | (mode & 0x07));
|
||||
header.put(0, value);
|
||||
}
|
||||
|
||||
public int getStratum() {
|
||||
return header.get(1) & 0xFF;
|
||||
}
|
||||
|
||||
public void setStratum(int stratum) {
|
||||
header.put(1, (byte) stratum);
|
||||
}
|
||||
|
||||
public int getPollInterval() {
|
||||
return header.get(2);
|
||||
}
|
||||
|
||||
public void setPollInterval(int poll) {
|
||||
header.put(2, (byte) poll);
|
||||
}
|
||||
|
||||
public byte getPrecision() {
|
||||
return header.get(3);
|
||||
}
|
||||
|
||||
public void setPrecision(byte precision) {
|
||||
header.put(3, precision);
|
||||
}
|
||||
|
||||
public int getRootDelay() {
|
||||
return header.getInt(4);
|
||||
}
|
||||
|
||||
public void setRootDelay(int rootDelay) {
|
||||
header.putInt(4, rootDelay);
|
||||
}
|
||||
|
||||
public int getRootDispersion() {
|
||||
return header.getInt(8);
|
||||
}
|
||||
|
||||
public void setRootDispersion(int rootDispersion) {
|
||||
header.putInt(8, rootDispersion);
|
||||
}
|
||||
|
||||
public int getReferenceIdentifier() {
|
||||
return header.getInt(12);
|
||||
}
|
||||
|
||||
public void setReferenceIdentifier(int refId) {
|
||||
header.putInt(12, refId);
|
||||
}
|
||||
|
||||
// ================== 时间戳字段访问方法(64位格式) ==================
|
||||
|
||||
/**
|
||||
* 获取 Reference Timestamp (64位)
|
||||
*/
|
||||
public long getReferenceTimestamp() {
|
||||
return header.getLong(16);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Reference Timestamp (64位)
|
||||
*/
|
||||
public void setReferenceTimestamp(long timestamp) {
|
||||
header.putLong(16, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Originate Timestamp (64位)
|
||||
*/
|
||||
public long getOriginateTimestamp() {
|
||||
return header.getLong(24);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Originate Timestamp (64位)
|
||||
*/
|
||||
public void setOriginateTimestamp(long timestamp) {
|
||||
header.putLong(24, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Receive Timestamp (64位)
|
||||
*/
|
||||
public long getReceiveTimestamp() {
|
||||
return header.getLong(32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Receive Timestamp (64位)
|
||||
*/
|
||||
public void setReceiveTimestamp(long timestamp) {
|
||||
header.putLong(32, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Transmit Timestamp (64位)
|
||||
*/
|
||||
public long getTransmitTimestamp() {
|
||||
return header.getLong(40);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 Transmit Timestamp (64位)
|
||||
*/
|
||||
public void setTransmitTimestamp(long timestamp) {
|
||||
header.putLong(40, timestamp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setReferenceTimestamp64(BigInteger longValue) {
|
||||
setReferenceTimestamp(longValue.longValue());
|
||||
}
|
||||
|
||||
public void setOriginateTimestamp64(BigInteger originateTimestamp) {
|
||||
setOriginateTimestamp(originateTimestamp.longValue());
|
||||
}
|
||||
|
||||
|
||||
public void setReceiveTimestamp64(BigInteger longValue) {
|
||||
setReceiveTimestamp(longValue.longValue());
|
||||
}
|
||||
|
||||
|
||||
public void setTransmitTimestamp64(BigInteger longValue) {
|
||||
setTransmitTimestamp(longValue.longValue());
|
||||
}
|
||||
|
||||
public BigInteger getReferenceTimestamp64() {
|
||||
BigInteger rez=BigInteger.valueOf(getReferenceTimestamp());
|
||||
if(rez.signum()<0) {
|
||||
rez=rez.add(TWO_POW_64);
|
||||
}
|
||||
return rez;
|
||||
}
|
||||
|
||||
public BigInteger getOriginateTimestamp64() {
|
||||
BigInteger rez=BigInteger.valueOf(getOriginateTimestamp());
|
||||
if(rez.signum()<0) {
|
||||
rez=rez.add(TWO_POW_64);
|
||||
}
|
||||
return rez;
|
||||
}
|
||||
public BigInteger getReceiveTimestamp64() {
|
||||
BigInteger rez=BigInteger.valueOf(getReceiveTimestamp());
|
||||
if(rez.signum()<0) {
|
||||
rez=rez.add(TWO_POW_64);
|
||||
}
|
||||
return rez;
|
||||
}
|
||||
public BigInteger getTransmitTimestamp64() {
|
||||
BigInteger rez=BigInteger.valueOf(getTransmitTimestamp());
|
||||
if(rez.signum()<0) {
|
||||
rez=rez.add(TWO_POW_64);
|
||||
}
|
||||
return rez;
|
||||
}
|
||||
|
||||
|
||||
// ================== 扩展字段方法 ==================
|
||||
|
||||
public List<ExtensionField> getExtensionFields() {
|
||||
return extensionFields;
|
||||
}
|
||||
|
||||
public void addExtensionField(ExtensionField field) {
|
||||
extensionFields.add(field);
|
||||
}
|
||||
|
||||
public void clearExtensionFields() {
|
||||
extensionFields.clear();
|
||||
}
|
||||
|
||||
// ================== 认证字段方法 ==================
|
||||
|
||||
public AuthenticationField getAuthField() {
|
||||
return authField;
|
||||
}
|
||||
|
||||
public void setAuthField(AuthenticationField authField) {
|
||||
this.authField = authField;
|
||||
}
|
||||
|
||||
public boolean hasAuthentication() {
|
||||
return authField != null;
|
||||
}
|
||||
|
||||
// ================== 工具方法 ==================
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NTPv4Packet [getLeapIndicator()=" + getLeapIndicator() + ", getVersionNumber()=" + getVersionNumber()
|
||||
+ ", getMode()=" + getMode() + ", getStratum()=" + getStratum() + ", getPollInterval()="
|
||||
+ getPollInterval() + ", getPrecision()=" + getPrecision() + ", getRootDelay()=" + getRootDelay()
|
||||
+ ", getRootDispersion()=" + getRootDispersion() + ", getReferenceIdentifier()="
|
||||
+ getReferenceIdentifier() + ", getReferenceTimestamp()=" +NTPTimestamps.ntp64ToString( getReferenceTimestamp64())
|
||||
+ ", getOriginateTimestamp()=" + NTPTimestamps.ntp64ToString(getOriginateTimestamp64()) + ", getReceiveTimestamp()="
|
||||
+ NTPTimestamps.ntp64ToString(getReceiveTimestamp64()) + ", getTransmitTimestamp()=" + NTPTimestamps.ntp64ToString(getTransmitTimestamp64()) + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
public BigInteger getDestinationTimestamp128() {
|
||||
return destinationTimeStamp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ================== 内部类:扩展字段 ==================
|
||||
|
||||
public static class ExtensionField {
|
||||
private ByteBuffer data;
|
||||
|
||||
public ExtensionField() {
|
||||
data = NetworkPacket.bufferAllocator.allocate(0);
|
||||
}
|
||||
|
||||
public ExtensionField(byte[] extensionData) {
|
||||
data = NetworkPacket.bufferAllocator.allocate(extensionData.length);
|
||||
data.put(extensionData);
|
||||
data.flip();
|
||||
}
|
||||
|
||||
public long getLength() {
|
||||
return data.limit() + 4; // 数据长度 + 长度字段(4字节)
|
||||
}
|
||||
|
||||
public void writeToChannel(WritableByteChannel dto) throws IOException {
|
||||
// 写入长度字段
|
||||
ByteBuffer lengthBuffer = NetworkPacket.bufferAllocator.allocate(4);
|
||||
lengthBuffer.putInt(data.limit());
|
||||
lengthBuffer.flip();
|
||||
dto.write(lengthBuffer);
|
||||
|
||||
// 写入数据
|
||||
dto.write(data.slice(0, data.limit()));
|
||||
}
|
||||
|
||||
public void readFromChannel(ReadableByteChannel din, long maxLength) throws IOException {
|
||||
// 读取长度字段
|
||||
ByteBuffer lengthBuffer = NetworkPacket.bufferAllocator.allocate(4);
|
||||
KNEChannels.readFully(din, lengthBuffer);
|
||||
lengthBuffer.flip();
|
||||
int length = lengthBuffer.getInt();
|
||||
|
||||
// 读取数据
|
||||
if (length > 0 && length <= maxLength - 4) {
|
||||
data = NetworkPacket.bufferAllocator.allocate(length);
|
||||
KNEChannels.readFully(din, data);
|
||||
data.flip();
|
||||
}
|
||||
}
|
||||
|
||||
public ByteBuffer getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(ByteBuffer data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
// ================== 内部类:认证字段 ==================
|
||||
|
||||
public static class AuthenticationField extends NetworkPacket{
|
||||
private int keyIdentifier;
|
||||
private ByteBuffer digest;
|
||||
|
||||
public AuthenticationField() {
|
||||
digest = NetworkPacket.bufferAllocator.allocate(16); // MD5摘要长度
|
||||
}
|
||||
|
||||
public long getTotalLength() {
|
||||
return AUTH_FIELD_LENGTH; // 固定20字节
|
||||
}
|
||||
|
||||
public void writeToChannel(WritableByteChannel dto) throws IOException {
|
||||
ByteBuffer buffer = NetworkPacket.bufferAllocator.allocate(AUTH_FIELD_LENGTH);
|
||||
buffer.putInt(keyIdentifier);
|
||||
|
||||
ByteBuffer digestCopy = digest.duplicate();
|
||||
digestCopy.position(0);
|
||||
while (digestCopy.hasRemaining()) {
|
||||
buffer.put(digestCopy.get());
|
||||
}
|
||||
|
||||
buffer.flip();
|
||||
dto.write(buffer);
|
||||
}
|
||||
|
||||
public void readFromChannel(ReadableByteChannel din, long length) throws IOException {
|
||||
ByteBuffer buffer = NetworkPacket.bufferAllocator.allocate(AUTH_FIELD_LENGTH);
|
||||
KNEChannels.readFully(din, buffer);
|
||||
buffer.flip();
|
||||
|
||||
keyIdentifier = buffer.getInt();
|
||||
|
||||
digest = NetworkPacket.bufferAllocator.allocate(16);
|
||||
while (buffer.hasRemaining() && digest.hasRemaining()) {
|
||||
digest.put(buffer.get());
|
||||
}
|
||||
digest.flip();
|
||||
}
|
||||
|
||||
public int getKeyIdentifier() {
|
||||
return keyIdentifier;
|
||||
}
|
||||
|
||||
public void setKeyIdentifier(int keyIdentifier) {
|
||||
this.keyIdentifier = keyIdentifier;
|
||||
}
|
||||
|
||||
public ByteBuffer getDigest() {
|
||||
return digest;
|
||||
}
|
||||
|
||||
public void setDigest(ByteBuffer digest) {
|
||||
this.digest = digest;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needEndPosition() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public BigInteger getReceiveTimestamp128() {
|
||||
return NTPTimestamps.inferNtp64To128(getReceiveTimestamp64(),clock.getCurrentTimeNTP128());
|
||||
}
|
||||
|
||||
public BigInteger getOriginateTimestamp128() {
|
||||
return NTPTimestamps.inferNtp64To128(getOriginateTimestamp64(),clock.getCurrentTimeNTP128());
|
||||
}
|
||||
|
||||
public BigInteger getTransmitTimestamp128() {
|
||||
return NTPTimestamps.inferNtp64To128(getTransmitTimestamp64(),clock.getCurrentTimeNTP128());
|
||||
}
|
||||
|
||||
public BigInteger getReferenceTimestamp128() {
|
||||
return NTPTimestamps.inferNtp64To128(getReferenceTimestamp64(),clock.getCurrentTimeNTP128());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
package org.kne.cloud.network.ntp;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.kne.cloud.clock.HighAccuracyClock;
|
||||
import org.kne.cloud.clock.NTPTimestamps;
|
||||
import org.kne.cloud.network.MultipurposeSocketAddress;
|
||||
import org.kne.io.KNEChannels;
|
||||
|
||||
public class NTPv4Protocol implements Closeable, AutoCloseable {
|
||||
private static final boolean showPacket = false;
|
||||
public static final int NTP_DEFAULT_PORT = 123;
|
||||
private static final long DEFAULT_POLL_INTERVAL = 2000000000L;
|
||||
private static final long REQUEST_TIMEOUT = 10000000000L;
|
||||
private static final long EPHEMERAL_TIMEOUT = 60000000000L;
|
||||
|
||||
public static class NTPPeer {
|
||||
private MultipurposeSocketAddress address;
|
||||
private boolean isEphemeral;// ephemeral
|
||||
private volatile long ephemeralUpdateTime = System.nanoTime();
|
||||
private volatile long pollInterval = DEFAULT_POLL_INTERVAL;
|
||||
private volatile long pollUpdateTime = System.nanoTime() - pollInterval;
|
||||
private volatile int requestMode;
|
||||
private volatile long peerPollInterval = pollInterval;
|
||||
|
||||
private boolean checkPollInterval() {
|
||||
long curr = System.nanoTime();
|
||||
if (curr - pollUpdateTime > Math.min(peerPollInterval, pollInterval)) {
|
||||
pollUpdateTime = curr;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkEphemeralTimeout() {
|
||||
if (!isEphemeral) {
|
||||
return false;
|
||||
}
|
||||
long curr = System.nanoTime();
|
||||
return curr - ephemeralUpdateTime > EPHEMERAL_TIMEOUT;
|
||||
}
|
||||
|
||||
private void resetEphemeralTimeout() {
|
||||
ephemeralUpdateTime = System.nanoTime();
|
||||
}
|
||||
|
||||
public long getPollInterval() {
|
||||
return pollInterval;
|
||||
}
|
||||
|
||||
public void setPollInterval(long pollInterval) {
|
||||
this.pollInterval = pollInterval;
|
||||
}
|
||||
|
||||
public NTPPeer(MultipurposeSocketAddress address, int requestMode) {
|
||||
this(address, requestMode, false);
|
||||
}
|
||||
|
||||
protected NTPPeer(MultipurposeSocketAddress address, int requestMode, boolean isEphemeral) {
|
||||
super();
|
||||
checkRequestMode(requestMode);
|
||||
this.address = address;
|
||||
this.isEphemeral = isEphemeral;
|
||||
this.requestMode = requestMode;
|
||||
}
|
||||
|
||||
private void checkRequestMode(int requestMode2) {
|
||||
if (requestMode2 != NTPv4Packet.NTP_CLIENT && requestMode2 != NTPv4Packet.NTP_SYMMETRIC_ACTIVE) {
|
||||
throw new IllegalArgumentException(
|
||||
"request mode must be NTPv4Packet.NTP_CLIEN or NTPv4Packet.NTP_SYMMETRIC_ACTIVE");
|
||||
}
|
||||
}
|
||||
|
||||
public NTPPeer(String hostport, int requestMode) {
|
||||
this(new MultipurposeSocketAddress(hostport), requestMode);
|
||||
}
|
||||
|
||||
public MultipurposeSocketAddress getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
private boolean isEphemeral() {
|
||||
return isEphemeral;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NTPPeer [address=" + address + ", isEphemeral=" + isEphemeral + ", ephemeralUpdateTime="
|
||||
+ ephemeralUpdateTime + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
NTPPeer other = (NTPPeer) obj;
|
||||
return Objects.equals(address, other.address);
|
||||
}
|
||||
|
||||
private void setPeerPollInterval(long peerPollInterval) {
|
||||
this.peerPollInterval = peerPollInterval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class CheckListItem {
|
||||
private NTPPeer peer;
|
||||
private BigInteger timeStamp;
|
||||
private long createTime = System.nanoTime();
|
||||
|
||||
private CheckListItem(BigInteger timeStamp, NTPPeer peer) {
|
||||
this.timeStamp = timeStamp;
|
||||
this.peer = peer;
|
||||
}
|
||||
|
||||
private boolean checkTimeout() {
|
||||
return System.nanoTime() - createTime > REQUEST_TIMEOUT;
|
||||
}
|
||||
}
|
||||
|
||||
private Set<NTPPeer> peers = Collections.synchronizedSet(new HashSet<>());
|
||||
private Set<NTPPeer> ephemeralPeers = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private ReentrantLock checkLock = new ReentrantLock();
|
||||
private List<CheckListItem> checkList = new ArrayList<>();
|
||||
|
||||
private NTPContext context;
|
||||
private DatagramSocket dgs;
|
||||
private MultipurposeSocketAddress bind;
|
||||
|
||||
public NTPv4Protocol(NTPContext context) throws UnknownHostException, IOException {
|
||||
this(context, new MultipurposeSocketAddress("UDP", "::0", NTP_DEFAULT_PORT));
|
||||
}
|
||||
|
||||
public NTPv4Protocol(NTPContext context, MultipurposeSocketAddress bind) throws UnknownHostException, IOException {
|
||||
this.bind = bind;
|
||||
this.context = context;
|
||||
dgs = bind.listenDatagramSocket();
|
||||
context.registerIO(this);
|
||||
Thread tr = new Thread(recv);
|
||||
tr.setName("NTPv4 Receive Thread");
|
||||
tr.start();
|
||||
}
|
||||
|
||||
private void setNTPInformation(NTPv4Packet nv4, NTPPeer peer) {
|
||||
if (peer != null) {
|
||||
long interval = peer.getPollInterval();
|
||||
int pi = (int) log(interval / 1000000000L, 2);
|
||||
nv4.setPollInterval(pi);
|
||||
} else {
|
||||
long interval = DEFAULT_POLL_INTERVAL;
|
||||
int pi = (int) log(interval / 1000000000L, 2);
|
||||
nv4.setPollInterval(pi);
|
||||
}
|
||||
nv4.setVersionNumber(NTPv4Packet.NTP_VERSION_NUMBER);
|
||||
nv4.setLeapIndicator(context.getLeapIndicator());
|
||||
int stratum = context.getStratum();
|
||||
if (stratum == 16) {
|
||||
stratum = 0;
|
||||
}
|
||||
nv4.setStratum(stratum);
|
||||
BigInteger precision = context.getLocalPrecision();
|
||||
byte b = (byte) Math.ceil(log(precision.doubleValue() / (Math.pow(2, 64)), 2));
|
||||
nv4.setPrecision(b);
|
||||
nv4.setRootDelay((int) context.getRootDelay());
|
||||
nv4.setRootDispersion((int) context.getCurrentRootDispersion());
|
||||
nv4.setReferenceTimestamp64(context.getReferenceTimestamp64());
|
||||
nv4.setTransmitTimestamp64(context.getClock().getCurrentTimeNTP64());
|
||||
}
|
||||
|
||||
private void setNTPInformation(NTPv4Packet nv4, NTPv4Packet request, NTPPeer peer) {
|
||||
nv4.setOriginateTimestamp(request.getTransmitTimestamp());
|
||||
nv4.setReceiveTimestamp64(NTPTimestamps.ntp128To64(request.getDestinationTimestamp128()));
|
||||
setNTPInformation(nv4, peer);
|
||||
}
|
||||
|
||||
private static double log(double value, double base) {
|
||||
|
||||
return Math.log(value) / Math.log(base);
|
||||
|
||||
}
|
||||
|
||||
public void request(NTPPeer peer) throws IOException {
|
||||
NTPv4Packet nv4 = new NTPv4Packet(context.getClock());
|
||||
nv4.setMode(peer.requestMode);
|
||||
setNTPInformation(nv4, peer);
|
||||
putOriginTimestamp(nv4, peer);
|
||||
peer.address.getInetAddress();
|
||||
sendNTPPacket(nv4, peer.address.getSocketAddress());
|
||||
}
|
||||
|
||||
private void putOriginTimestamp(NTPv4Packet nv4, NTPPeer peer) {
|
||||
checkLock.lock();
|
||||
try {
|
||||
removeTimeoutTimestamp();
|
||||
checkList.add(new CheckListItem(nv4.getTransmitTimestamp128(), peer));
|
||||
} finally {
|
||||
checkLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void removeTimeoutTimestamp() {
|
||||
for (Iterator<CheckListItem> iterator = checkList.iterator(); iterator.hasNext();) {
|
||||
CheckListItem bigInteger = (CheckListItem) iterator.next();
|
||||
if (bigInteger.checkTimeout()) {
|
||||
if (showPacket)
|
||||
System.out.println("timeout:" + bigInteger.timeStamp);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNTPPacket(NTPv4Packet nv4, SocketAddress target) throws IOException {
|
||||
ByteBuffer bbf = ByteBuffer.allocate(65535);
|
||||
nv4.writeToChannel(KNEChannels.newWritableChannel(bbf));
|
||||
bbf.flip();
|
||||
DatagramPacket dp = new DatagramPacket(bbf.array(), bbf.limit());
|
||||
dp.setSocketAddress(target);
|
||||
dgs.send(dp);
|
||||
if (showPacket)
|
||||
System.out.println("NTPv4 TX:" + target + " " + nv4);
|
||||
}
|
||||
|
||||
private NTPPeer checkOriginTimestamp(NTPv4Packet nv4) {
|
||||
checkLock.lock();
|
||||
try {
|
||||
BigInteger ori = nv4.getOriginateTimestamp128();
|
||||
removeTimeoutTimestamp();
|
||||
for (Iterator<CheckListItem> iterator = checkList.iterator(); iterator.hasNext();) {
|
||||
CheckListItem item = (CheckListItem) iterator.next();
|
||||
if (item.timeStamp.equals(ori)) {
|
||||
iterator.remove();
|
||||
return item.peer;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
checkLock.unlock();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
NTPPeer findPeer(MultipurposeSocketAddress addr) {
|
||||
Object[] objs = peers.toArray();
|
||||
for (int i = 0; i < objs.length; i++) {
|
||||
NTPPeer np = (NTPPeer) objs[i];
|
||||
if (np.getAddress().equals2(addr)) {
|
||||
return np;
|
||||
}
|
||||
}
|
||||
objs = ephemeralPeers.toArray();
|
||||
for (int i = 0; i < objs.length; i++) {
|
||||
NTPPeer np = (NTPPeer) objs[i];
|
||||
if (np.getAddress().equals2(addr)) {
|
||||
return np;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Runnable recv = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
while (!dgs.isClosed()) {
|
||||
AtomicReference<InetSocketAddress> isa = new AtomicReference<>();
|
||||
NTPv4Packet nv4 = receiveNTPPacket(isa);
|
||||
MultipurposeSocketAddress mpsafrom = new MultipurposeSocketAddress(bind.getType(), isa.get());
|
||||
switch (nv4.getMode()) {
|
||||
case NTPv4Packet.NTP_SYMMETRIC_ACTIVE:
|
||||
NTPv4Packet nv4r = new NTPv4Packet(context.getClock());
|
||||
nv4r.setMode(NTPv4Packet.NTP_SYMMETRIC_PASSIVE);
|
||||
setNTPInformation(nv4r, nv4, findPeer(mpsafrom));
|
||||
sendNTPPacket(nv4r, isa.get());
|
||||
ephemeralPeers.add(new NTPPeer(mpsafrom, NTPv4Packet.NTP_SYMMETRIC_ACTIVE, true));
|
||||
break;
|
||||
case NTPv4Packet.NTP_SYMMETRIC_PASSIVE:
|
||||
NTPPeer npr = checkOriginTimestamp(nv4);
|
||||
if (npr != null) {
|
||||
npr.resetEphemeralTimeout();
|
||||
npr.setPeerPollInterval((1L << nv4.getPollInterval()) * 1000000000);
|
||||
context.putPacket(nv4, mpsafrom);
|
||||
} else {
|
||||
}
|
||||
break;
|
||||
case NTPv4Packet.NTP_CLIENT:
|
||||
NTPv4Packet nv4s = new NTPv4Packet(context.getClock());
|
||||
nv4s.setMode(NTPv4Packet.NTP_SERVER);
|
||||
setNTPInformation(nv4s, nv4, findPeer(mpsafrom));
|
||||
sendNTPPacket(nv4s, isa.get());
|
||||
break;
|
||||
case NTPv4Packet.NTP_SERVER:
|
||||
NTPPeer nprx = checkOriginTimestamp(nv4);
|
||||
if (nprx != null) {
|
||||
nprx.resetEphemeralTimeout();
|
||||
nprx.setPeerPollInterval((1L << nv4.getPollInterval()) * 1000000000);
|
||||
context.putPacket(nv4, mpsafrom);
|
||||
} else {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
protected void sendReqPackets() throws IOException {
|
||||
List<NTPPeer> sends = getPeersWillSend();
|
||||
for (NTPPeer npr : sends) {
|
||||
request(npr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected List<NTPPeer> getPeersWillSend() {
|
||||
List<NTPPeer> sends = new ArrayList<>();
|
||||
peers.forEach((pr) -> {
|
||||
if (pr.checkPollInterval()) {
|
||||
sends.add(pr);
|
||||
}
|
||||
});
|
||||
ephemeralPeers.removeIf((pr) -> {
|
||||
return pr.checkEphemeralTimeout();
|
||||
});
|
||||
ephemeralPeers.forEach((pr) -> {
|
||||
if (pr.checkPollInterval()) {
|
||||
if (!peers.contains(pr)) {
|
||||
sends.add(pr);
|
||||
}
|
||||
}
|
||||
});
|
||||
return sends;
|
||||
}
|
||||
|
||||
private NTPv4Packet receiveNTPPacket(AtomicReference<InetSocketAddress> isa) throws IOException {
|
||||
ByteBuffer bbf = ByteBuffer.allocate(65535);
|
||||
DatagramPacket dp = new DatagramPacket(bbf.array(), bbf.limit());
|
||||
dgs.receive(dp);
|
||||
isa.set((InetSocketAddress) dp.getSocketAddress());
|
||||
NTPv4Packet nv4 = new NTPv4Packet(context.getClock());
|
||||
nv4.readFromChannel(KNEChannels.newReadableChannel(bbf), dp.getLength());
|
||||
if (showPacket)
|
||||
System.out.println("NTPv4 RX:" + isa.get() + " " + nv4);
|
||||
return nv4;
|
||||
}
|
||||
|
||||
public Set<NTPPeer> getPeers() {
|
||||
return peers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
dgs.close();
|
||||
context.unregisterIO(this);
|
||||
}
|
||||
|
||||
public boolean isClosed() {
|
||||
return dgs.isClosed();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
HighAccuracyClock hac = new HighAccuracyClock();
|
||||
NTPContext context = new NTPContext(hac);
|
||||
System.out.println(context);
|
||||
NTPv4Protocol nvc = new NTPv4Protocol(context, new MultipurposeSocketAddress("{UDP}0.0.0.0:123"));// 106.55.184.199
|
||||
nvc.getPeers().add(new NTPPeer("{UDP}106.55.184.199:123", NTPv4Packet.NTP_CLIENT));
|
||||
nvc.getPeers().add(new NTPPeer("{UDP}time.windows.com:123", NTPv4Packet.NTP_CLIENT));
|
||||
nvc.getPeers().add(new NTPPeer("{UDP}127.0.0.1:123", NTPv4Packet.NTP_SYMMETRIC_ACTIVE));
|
||||
/*
|
||||
* for (int i = 0; i < 10000; i++) { Thread.sleep(1000);
|
||||
* System.out.println(context.getCurrentSelfDispersion128());
|
||||
* System.out.println(context); }
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.kne.cloud.network.ntp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.kne.cloud.clock.HighAccuracyClock;
|
||||
import org.kne.cloud.network.MultipurposeSocketAddress;
|
||||
import org.kne.cloud.network.ntp.NTPv4Protocol.NTPPeer;
|
||||
|
||||
public class TestNTP1 {
|
||||
public static void main(String[] args) throws UnknownHostException, IOException {
|
||||
HighAccuracyClock hac = new HighAccuracyClock();
|
||||
NTPContext context = new NTPContext(hac);
|
||||
context.syncToSystem();
|
||||
System.out.println(context);
|
||||
NTPv4Protocol nvc = new NTPv4Protocol(context, new MultipurposeSocketAddress("{UDP}0.0.0.0:123"));// 106.55.184.199
|
||||
//nvc.getPeers().add(new NTPPeer("{UDP}106.55.184.199:123", NTPv4Packet.NTP_CLIENT));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.kne.cloud.network.ntp;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.kne.cloud.clock.HighAccuracyClock;
|
||||
import org.kne.cloud.network.MultipurposeSocketAddress;
|
||||
import org.kne.cloud.network.ntp.NTPv4Protocol.NTPPeer;
|
||||
|
||||
public class TestNTP2 {
|
||||
public static void main(String[] args) throws UnknownHostException, IOException {
|
||||
HighAccuracyClock hac = new HighAccuracyClock();
|
||||
NTPContext context = new NTPContext(hac);
|
||||
context.syncToSystem();
|
||||
System.out.println(context);
|
||||
NTPv4Protocol nvc = new NTPv4Protocol(context, new MultipurposeSocketAddress("{UDP}0.0.0.0:0"));// 106.55.184.199
|
||||
nvc.getPeers().add(new NTPPeer("{UDP}127.0.0.1:123", NTPv4Packet.NTP_SYMMETRIC_ACTIVE));
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user