惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
IT之家
IT之家
博客园 - 聂微东
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Privacy International News Feed
人人都是产品经理
人人都是产品经理
PCI Perspectives
PCI Perspectives
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
V
Vulnerabilities – Threatpost
美团技术团队
S
Secure Thoughts
N
News | PayPal Newsroom
L
LINUX DO - 最新话题
腾讯CDC
Application and Cybersecurity Blog
Application and Cybersecurity Blog
雷峰网
雷峰网
B
Blog
MyScale Blog
MyScale Blog
T
The Blog of Author Tim Ferriss
TaoSecurity Blog
TaoSecurity Blog
N
News and Events Feed by Topic
Blog — PlanetScale
Blog — PlanetScale
C
Check Point Blog
T
Tailwind CSS Blog
月光博客
月光博客
Simon Willison's Weblog
Simon Willison's Weblog
Hacker News: Ask HN
Hacker News: Ask HN
The Last Watchdog
The Last Watchdog
Google DeepMind News
Google DeepMind News
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
MongoDB | Blog
MongoDB | Blog
S
Security @ Cisco Blogs
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
S
Security Affairs
Forbes - Security
Forbes - Security
P
Palo Alto Networks Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
T
Tor Project blog
O
OpenAI News
L
Lohrmann on Cybersecurity
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Proofpoint News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
LangChain Blog
B
Blog RSS Feed
H
Hackread – Cybersecurity News, Data Breaches, AI and More

克洛洛日记

一年回本计划 | 克洛洛日记 股票期权的Theta | 克洛洛日记 股票期权的Gamma | 克洛洛日记 股票期权的Delta | 克洛洛日记 KDJ指标 | 克洛洛日记 布林线 | 克洛洛日记 MACD指标 | 克洛洛日记 RSI指标 | 克洛洛日记 聊聊Spring的stereotype注解 | 克洛洛日记 免费申请 JetBrains 全产品 License | 克洛洛日记 Aloha 一个Hexo主题 | 克洛洛日记 泰国之旅 [三] | 克洛洛日记 泰国之旅 [二] | 克洛洛日记 泰国之旅 [一] | 克洛洛日记 Nginx使用SSL证书配置https | 克洛洛日记 i18next | 克洛洛日记 错误排查:非ROOT用户使用密钥进行ssh登录提示输入密码 | 克洛洛日记 使用Github、Travis-CI和Coding.net自动部署博客[三] | 克洛洛日记 使用EditorConfig | 克洛洛日记 JavaScript执行window.print()打印内容为空白 | 克洛洛日记 使用Github、Travis-CI和Coding.net自动部署博客[二] | 克洛洛日记 寓食记 | 克洛洛日记 使用Github、Travis-CI和Coding.net自动部署博客[一] | 克洛洛日记 书单 | 克洛洛日记 WebJars使用示例 | 克洛洛日记 WebJars介绍 | 克洛洛日记 Maven打jar包内容出错 | 克洛洛日记 数据分布式模型 | 克洛洛日记 Wicket的HelloWorld | 克洛洛日记 Java运行时内存结构 | 克洛洛日记 前端优化法则 | 克洛洛日记
时间协议 | 克洛洛日记
海拉鲁球果 <hyrul · 2020-01-18 · via 克洛洛日记

时间协议

· 小知识点 / 网络

时间协议

前两天在看Netty的hello world的时候,里面举了个例子—创建时间协议服务器。通过学习,才知道这个协议很老的,甚至已经被网络时间替代了。但是这个协议很简单,所以学习一下也无碍。

什么是时间协议

时间协议(英语:TIME protocol)是一个在RFC 868内定义的网络传输协议。它用作提供机器可读的日期时间信息。

这是时间协议(Time Protocol)在RFC868上的定义(中文由我自己翻译的):

                             Time Protocol
                                时间协议

This RFC specifies a standard for the ARPA Internet community.  Hosts on
the ARPA Internet that choose to implement a Time Protocol are expected
to adopt and implement this standard.
这个RFC的标准给ARPA(美国国防部国防高等研究计划署)英特网定义的。在ARPA英特网上,
要实现时间协议的主机,则希望采用并且实现这个标准。


This protocol provides a site-independent, machine readable date and
time.  The Time service sends back to the originating source the time in
seconds since midnight on January first 1900.
这个协议提供了一个独立的,机器可读的日期和时间。时间服务将回复给客户端一个从
格林威治时间1900年1月1日开始计算,到请求时刻的总秒数。

One motivation arises from the fact that not all systems have a
date/time clock, and all are subject to occasional human or machine
error.  The use of time-servers makes it possible to quickly confirm or
correct a system's idea of the time, by making a brief poll of several
independent sites on the network.
现实中,并不是所有的系统都有时间功能,而且偶尔会受到人为的或者机器错误的影响。
那么时间服务器就可以快速地提供正确的时间来让其他系统确认,或者用来纠正时间,
只需要在网络上简单地查询就可以。


This protocol may be used either above the Transmission Control Protocol
(TCP) or above the User Datagram Protocol (UDP).
这个协议可以用在TCP和UDP协议上。

When used via TCP the time service works as follows:
当通过TCP协议使用时,时间服务器将执行以下步骤:

   S: Listen on port 37 (45 octal).
   服务端:监听37端口

   U: Connect to port 37.
   客户端:连接到37端口

   S: Send the time as a 32 bit binary number.
   服务端:发送32位二进制的时间

   U: Receive the time.
   客户端:接收到时间

   U: Close the connection.
   客户端:断开连接

   S: Close the connection.
   服务端:断开连接

   The server listens for a connection on port 37.  When the connection
   is established, the server returns a 32-bit time value and closes the
   connection.  If the server is unable to determine the time at its
   site, it should either refuse the connection or close it without
   sending anything.
   服务器监听端口37的连接。当连接建立,服务器将返回32位的时间值,然后关闭连接。
   如果服务器无法确定时间,它需要拒绝连接,或者不发送任何信息,直接关闭连接。

When used via UDP the time service works as follows:
当通过UDP协议使用时,时间服务器将执行以下步骤:

   S: Listen on port 37 (45 octal).
   服务端:监听37端口

   U: Send an empty datagram to port 37.
   客户端:发送空数据报到37端口

   S: Receive the empty datagram.
   服务端:接受到空的数据报

   S: Send a datagram containing the time as a 32 bit binary number.
   服务端:发送32位二进制的时间的数据报

   U: Receive the time datagram.
   客户端:接收时间数据报

   The server listens for a datagram on port 37.  When a datagram
   arrives, the server returns a datagram containing the 32-bit time
   value.  If the server is unable to determine the time at its site, it
   should discard the arriving datagram and make no reply.
   服务器用37端口监听数据报。当接收到数据报,服务器就返回包含32位二进制的时间的数据报。
   如果服务器无法确定时间,则它应该丢弃接收到的数据报,不作回复。

The Time
时间

The time is the number of seconds since 00:00 (midnight) 1 January 1900
GMT, such that the time 1 is 12:00:01 am on 1 January 1900 GMT; this
base will serve until the year 2036.
这里的时间,时从格林威治时间1900年1月1日0点0分0秒开始计算,
比如1就表示时格林威治时间1900年1月1日上午12点0分1秒;但是,这种计算方式到2036年将不再适用。

For example:
示例:

   the time  2,208,988,800 corresponds to 00:00  1 Jan 1970 GMT,

             2,398,291,200 corresponds to 00:00  1 Jan 1976 GMT,

             2,524,521,600 corresponds to 00:00  1 Jan 1980 GMT,

             2,629,584,000 corresponds to 00:00  1 May 1983 GMT,

        and -1,297,728,000 corresponds to 00:00 17 Nov 1858 GMT.

时间协议服务器的Java实现

完整代码在https://github.com/henryhuang/time-protocol-example

Server


public class TPServer {

    private static final int PORT = 37;
    private static final long OFF_SET = 2208988800L;

    public static void main(String[] args) {

        new TPServer().start();

    }

    public void start() {
        System.out.println("Server starting...");

        try (ServerSocket serverSocket = new ServerSocket(PORT)) {

            while (true) {
                Socket connection = serverSocket.accept();
                new TPHandlerThread(connection);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    private class TPHandlerThread implements Runnable {

        private Socket connection;

        private TPHandlerThread(Socket connection) {
            this.connection = connection;
            new Thread(this).start();
        }

        public void run() {

            System.out.println("Receive seek time request!");

            try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
                byte[] timeBytes = String.valueOf(getTime()).getBytes();
                os.writeInt(timeBytes.length);
                os.write(timeBytes);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private static long getTime() {
        return System.currentTimeMillis() / 1000L + OFF_SET;
    }

}

Client


public class TPClient {

    private static final int PORT = 37;
    private static final long OFF_SET = 2208988800L;

    public static void main(String[] args) {
        Long time = new TPClient("localhost").seekTime();
        if (time != null) {
            System.out.println(new Date((time - OFF_SET) * 1000L));
        }
    }

    private String host;

    public TPClient(String host) {
        this.host = host;
    }

    public Long seekTime() {

        try (Socket socket = new Socket(this.host, PORT);
            DataInputStream is = new DataInputStream(socket.getInputStream())) {
            int length = is.readInt();
            byte[] bytes = new byte[length];
            is.readFully(bytes);
            return Long.valueOf(new String(bytes));
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;

    }


}