简单有趣:使用Go语言实现“时间服务器”,从此时间你说了算

前言

Go语言自带的net包,强大而丰富,封装了大多数网络编程所使用的方法和库函数。

今天我们做一个简单的小实验,使用Go语言,实现“时间服务器”功能,让服务器对外提供时间同步校准功能。

时间服务器浅说

为什么需要时间服务器?为了同步。举一个例子吧。

话说程序员小王,因为工作比较忙,大龄了还没女朋友,家里给介绍了个对象小宋。加了微信,要了手机号,一来二去感觉不错。

“见个面吧!”

“好哇!”

“明天下午4:00步行街咖啡厅见吧?”

“好的。”

等到第二天,小宋4:00准时到了咖啡厅。找了一圈没见小王人影。

“难道这小子写代码忘时间了?太不守时了!差评。”小宋心里不快。

又过了5分钟,小王轻松地来了,说:

“我说准点儿到,想不到你来的更早。”

“你都迟到了!”

“没有吧,我刚4:00”。

小王拿出自己的手机晃了晃。果然4:00。小宋也拿出手机,看了看,4:05分。

(擦汗)

到底谁的时间不准确?

小王和小宋要是能对一下时,或者知道有NTP时间服务器,就不会这样了。

NTP协议

为了时间同步,指定了一个专门的协议NTP,网络时间协议。用于同步计算机,手机,手表等联网设备的时间。

当前提供此功能的服务器有很多,下面是一张列表。

  • Windows 系统自带:time.windows.com 和 time.nist.gov
  • IOS自带:time.apple.com 和 time.asia.apple.com
  • 国内免费使用的 cn.ntp.org.cn
  • 阿里公司提供的 ntp.aliyun.com

在windows系统中,通过时间和日期设置中的“Internet时间”选项中设置服务器,即可与远程服务器同步时间。

用Go语言实现

NTP是由RFC 867定义, 默认的端口13, 协议是TCP和UDP。所以我们使用net包进行实现。

主要流程是注册并监听一个端口,然后阻塞在 accept 操作,并等待客户端连接。当一个客户端连接, accept 调用返回一个连接(connection)对象。时间服务非常简单,只是将当前时间写入到客户端, 关闭该连接,并继续等待下一个客户端。

我们主要使用以下两个方法:

func ListenTCP(net string, laddr *TCPAddr) (l *TCPListener, err os.Error)  func (l *TCPListener) Accept() (c Conn, err os.Error)

对上述函数的参数说明:

net变量:

可以设置为字符串”tcp”, “tcp4”或者”tcp6”中的一个。

laddr变量:

如果监听所有端口,IP地址应设置为0。如果是特定端口,则需要制定IP。特别注意的是,在类Unix系统上,用户程序监听端口应大于1024。因为该段端口是系统预留的。下方代码中,我们选择了没有使用的1240端口。

主要由三大段组成,

  • 一段是err_handler处理错误并打印和退出,
  • 一段是绑定IP和端口,
  • 一段是循环监听,如果收到请求,发送时间字符串,并关闭连接。

本身功能流程也比较直观。

测试一下

我们在本地运行这个工具,并用telnet工具连接1240端口,看能否获取到需要的数据。

可以不用编译,直接运行以下指令:

go run net.go

然后使用telnet连接,

telnet 127.0.0.1 1240

得到类似如下的结果:

可以看到程序正常地执行了。也获取到了结果字符串。

结语

通过时间同步服务这样的小功能,我们重温了tcp协议,以及实现NTP的步骤。可见Go语言在网络编程方面,给程序员提供了非常多的便利,可以使用少量的代码,实现逻辑功能。

这可能就是Go为什么高效的一种体现。