前言
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)对象。时间服务非常简单,只是将当前时间写入到客户端, 关闭该连接,并继续等待下一个客户端。
我们主要使用以下两个方法:
代码语言:javascript复制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为什么高效的一种体现。