引き続き「Goならわかるシステムプログラミング」の7章を写経しつつ読んでみる。
前章はTCPだったけどこの章はUDP。TCPより機能が少なくシンプルなプロトコル
UDPはTCPと同じトランスポート層プロトコルですが、TCPと違ってコネクションレスであり、誰とつながっているかは管理しません。 プロトコルとしてデータロスの検知をすることも、通信速度の制限をすることもなく、一方的にデータを送りつけるのに使われます。 パケットの到着順序も管理しません
サーバ側実装例
package main import ( "fmt" "net" ) func main() { fmt.Println("Server is running at localhost:8888") conn, err := net.ListenPacket("udp", "localhost:8888") if err != nil { panic(err) } defer conn.Close() buffer := make([]byte, 1500) for { length, remoteAddress, err := conn.ReadFrom(buffer) if err != nil { panic(err) } fmt.Printf("Received from %v: %v\n", remoteAddress, string(buffer[:length])) _, err = conn.WriteTo([]byte("Hello from Server"), remoteAddress) if err != nil { panic(err) } } }
net.ListenPacket()でUDP接続の待ち受けができる。
net.Listen()やnet.ListenPacket()、net.Dial()は、 プロトコルの種類を文字列で指定するだけで具体的なインタフェースを隠して通信を 抽象的に書くためのインタフェースです。
クライアント側
package main import ( "fmt" "net" ) func main() { conn, err := net.Dial("udp4", "localhost:8888") if err != nil { panic(err) } defer conn.Close() fmt.Println("Sending to server") _, err = conn.Write([]byte("Hello From Client")) if err != nil { panic(err) } fmt.Println("Receiving from server") buffer := make([]byte, 1500) length, err := conn.Read(buffer) if err != nil { panic(err) } fmt.Printf("Received: %s\n", string(buffer[:length])) }
マルチキャストの実装
マルチキャストは、リクエスト側の負担を増やすことなく多くのクライアントに同 時にデータを送信できる仕組みです。マルチキャストはUDPならではの機能なので、 次はGo言語でマルチキャストサーバーとクライアントを作ってみましょう。
それでは Go でマルチキャストを利用するコードを見てみましょう。例題として 117の時報のようなサービスを実装しました
サーバサイド
package main import ( "fmt" "net" "time" ) const interval = 10 * time.Second func main() { fmt.Println("Start tick server at 224.0.0.1:9999") conn, err := net.Dial("udp", "224.0.0.1:9999") if err != nil { panic(err) } defer conn.Close() start := time.Now() wait := start.Truncate(interval).Add(interval).Sub(start) time.Sleep(wait) ticker := time.Tick(interval) for now := range ticker { conn.Write([]byte(now.String())) fmt.Println("Tick: ", now.String()) } }
クライアントサイド
net.ResolveUDPAddr関数でパースしてnet.ListenMulticastUDP()でソケットを開いてサーバのソケットを待ち受ける。
package main import ( "fmt" "net" ) func main() { fmt.Println("Listen tick server at 224.0.0.1:9999") address, err := net.ResolveUDPAddr("udp", "224.0.0.1:9999") if err != nil { panic(err) } listener, err := net.ListenMulticastUDP("udp", nil, address) defer listener.Close() buffer := make([]byte, 1500) for { length, remoteAddress, err := listener.ReadFromUDP(buffer) if err != nil { panic(err) } fmt.Printf("Server %v\n", remoteAddress) fmt.Printf("Now %s\n", string(buffer[:length])) } }