Go语言圣经 练习8.1
练习 8.1: 修改clock2来支持传入参数作为端口号,然后写一个clockwall的程序,这个程序可以同时与多个clock服务器通信,从多个服务器中读取时间,并且在一个表格中一次显示所有服务器传回的结果,类似于你在某些办公室里看到的时钟墙。如果你有地理学上分布式的服务器可以用的话,让这些服务器跑在不同的机器上面;或者在同一台机器上跑多个不同的实例,这些实例监听不同的端口,假装自己在不同的时区。像下面这样:
$ TZ=US/Eastern ./clock2 -port 8010 &
$ TZ=Asia/Tokyo ./clock2 -port 8020 &
$ TZ=Europe/London ./clock2 -port 8030 &
$ clockwall NewYork=localhost:8010 Tokyo=localhost:8020 London=localhost:8030
服务端clock
首先修改clock2的代码以支持命令行参数,支持命令行参数主要有两种方式:
- 直接使用os.Args[1:]读取命令行参数,自行对读取到的字段进行解析。
- 使用之前 7.4节介绍的flag.Value接口来读取命令行参数
本文使用第二种方法实现,这样既可以熟悉之前章节的知识,也较为方便。 然后对应不同port 做不同的处理(handleConn中执行swtich分支处理)
package main
import (
"flag"
"io"
"log"
"net"
"time"
)
var port = flag.String("port", "8000", "timeZone")
func main() {
flag.Parse()
listener, err := net.Listen("tcp", "localhost:"+*port)
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err) // e.g., connection aborted
continue
}
go handleConn(*port, conn) // handle one connection at a time
}
}
func handleConn(port string, c net.Conn) {
defer c.Close()
var timeZone string
switch port {
case "8010":
timeZone = "US/Eastern "
case "8020":
timeZone = "Asia/Tokyo "
case "8030":
timeZone = "Europe/London "
}
for {
_, err := io.WriteString(c, "TZ="+timeZone+time.Now().Format("15:04:05\n"))
if err != nil {
return // e.g., client disconnectedn
}
time.Sleep(1 * time.Second)
}
}
为了更方便的运行,写了个shell
#!/bin/zsh
TZ=US/Eastern go run clock2.go -port 8010 &
TZ=Asia/Tokyo go run clock2.go -port 8020 &
TZ=Europe/London go run clock2.go -port 8030 &
#clockwall NewYork=localhost:8010 Tokyo=localhost:8020 London=localhost:8030
客户端clockwall
根据题意,客户端同时访问多个服务端,并将多个返回结果一并返回。我在看到这个题目的时候其实挺疑惑的,并不是很清楚作者的要求是什么,是将结果直接返回至终端(乱序),还是按端口顺序一一排列的整行输出。根据作者对时钟墙的举例,我倾向于一一排列的整行输出(看过网上的几个案例,都没有实现)。在思考一段时间之后,发现仅用8.2节之前的内容无法实现(本人无法实现😮💨),最后使用channels来实现。
package main
import (
"bufio"
"fmt"
"log"
"net"
"os"
"strings"
"time"
)
//var list [3]string
func main() {
var chs = make([]chan string, 3)
for i, url := range os.Args[1:] {
chs[i] = make(chan string, 1)
go connet(url, chs[i])
}
for {
fmt.Println(<-chs[0], <-chs[1], <-chs[2])
}
}
func connet(url string, ch chan string) {
_, url, ok := strings.Cut(url, "=")
if !ok {
fmt.Println("url format error")
return
}
conn, err := net.Dial("tcp", url)
if err != nil {
log.Fatal(err)
}
sc := bufio.NewScanner(conn)
for sc.Scan() {
s := sc.Text()
ch <- s
}
time.Sleep(1 * time.Second)
defer close(ch)
defer conn.Close()
}
运行结果
最近开始Go语言,记录一下学习过程,有一起学Go的小伙伴可以和我交流啊