簡單有趣:使用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為什麼高效的一種體現。