《Go语言圣经》练习8.1 clock

《Go语言圣经》练习8.1 多个server对1个client

题目如下:

练习 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

解析:

直接写出来非常简单,主要key如下:

  1. client只需要对 处理来自server的函数 加上 go 变成goroutine即可。
  2. client获取命令行输入的port参数
  3. 命令行开启多个server

难点:

花了进2个小时才找到原因所在,每次运行了client 就直接退出。

主要原因是:主程序半天没有获取到来自server的输出,直接退出。(server有设置延迟等待)

因此,我们需要在client的main()函数在执行之前加入 flag.Parse() 等待获取参数

下面是代码:

clockwall.go

func main() {
	flag.Parse() // 暂停获取参数!!!
	for _, port := range os.Args[1:] { //获取命令行输入 ./clockwall 8002 8003 8004
		go dail(port) // 处理每个端口连接
	}
	for {
		time.Sleep(5 * time.Second)
	}

}
  /*下面的不太是重点*/
func dail(port string) {
	port_ := "localhost:" + port
	fmt.Print(port_)
	conn, err := net.Dial("tcp", port_)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	mustCopy(os.Stdout, conn)
}
func mustCopy(dst io.Writer, src io.Reader) {
	if _, err := io.Copy(dst, src); err != nil {
		//从连接中读取数据,并将读到的内容写到标准输出中,直到遇到end of file的条件或者发生错误。
		log.Fatal(err)
	}
}

clock.go

// Clock1 is a TCP server that periodically writes the time.
package main

import (
	"io"
	"log"
	"net"
	"os"
	"time"
)

/*server*/
func main() {
	if len(os.Args) > 0 {
		port := os.Args[1]
		listen(port)
	}
}

func listen(port string) {
	port = "localhost:" + port
	listener, err := net.Listen("tcp", port) // 监听端口
	/*listener 是监听时创建的对象, 有一个accept方法*/
	if err != nil {
		log.Fatal(err)
	}

	for { //一直循环,直到创建新的对象,这样得到原来的对象
		conn, err := listener.Accept()
		/*直到新的链接创建,conn表示原来的链接链接对象*/
		if err != nil {
			log.Print(err) // e.g., connection aborted
			continue
		}
		//连接成功,解决链接 : 只允许一个client
		handleConn(conn, port) // handle one connection at a time
	}
}
func handleConn(c net.Conn, port string) {
	/*handleConn函数会处理一个完整的客户端连接*/
	defer c.Close() //关闭客户端链接
	for {
		ss := time.Now().Format("15:04:05  ") + port + "\n"
		_, err := io.WriteString(c, ss)
		// time.Time.Format方法提供了一种格式化日期和时间信息的方式。
		if err != nil {
			return // e.g., client disconnected
		}
		time.Sleep(3 * time.Second)
	}

}

练习8.1 多个server对1个client

题目如下:

练习 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

解析:

直接写出来非常简单,主要key如下:

  1. client只需要对 处理来自server的函数 加上 go 变成goroutine即可。
  2. client获取命令行输入的port参数
  3. 命令行开启多个server

难点:

花了进2个小时才找到原因所在,每次运行了client 就直接退出。

主要原因是:主程序半天没有获取到来自server的输出,直接退出。(server有设置延迟等待)

因此,我们需要在client的main()函数在执行之前加入 flag.Parse() 等待获取参数

下面是代码:

clockwall.go

func main() {
	flag.Parse() // 暂停获取参数!!!
	for _, port := range os.Args[1:] { //获取命令行输入 ./clockwall 8002 8003 8004
		go dail(port) // 处理每个端口连接
	}
	for {
		time.Sleep(5 * time.Second)
	}

}
  /*下面的不太是重点*/
func dail(port string) {
	port_ := "localhost:" + port
	fmt.Print(port_)
	conn, err := net.Dial("tcp", port_)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	mustCopy(os.Stdout, conn)
}
func mustCopy(dst io.Writer, src io.Reader) {
	if _, err := io.Copy(dst, src); err != nil {
		//从连接中读取数据,并将读到的内容写到标准输出中,直到遇到end of file的条件或者发生错误。
		log.Fatal(err)
	}
}

clock.go

// Clock1 is a TCP server that periodically writes the time.
package main

import (
	"io"
	"log"
	"net"
	"os"
	"time"
)

/*server*/
func main() {
	if len(os.Args) > 0 {
		port := os.Args[1]
		listen(port)
	}
}

func listen(port string) {
	port = "localhost:" + port
	listener, err := net.Listen("tcp", port) // 监听端口
	/*listener 是监听时创建的对象, 有一个accept方法*/
	if err != nil {
		log.Fatal(err)
	}

	for { //一直循环,直到创建新的对象,这样得到原来的对象
		conn, err := listener.Accept()
		/*直到新的链接创建,conn表示原来的链接链接对象*/
		if err != nil {
			log.Print(err) // e.g., connection aborted
			continue
		}
		//连接成功,解决链接 : 只允许一个client
		handleConn(conn, port) // handle one connection at a time
	}
}
func handleConn(c net.Conn, port string) {
	/*handleConn函数会处理一个完整的客户端连接*/
	defer c.Close() //关闭客户端链接
	for {
		ss := time.Now().Format("15:04:05  ") + port + "\n"
		_, err := io.WriteString(c, ss)
		// time.Time.Format方法提供了一种格式化日期和时间信息的方式。
		if err != nil {
			return // e.g., client disconnected
		}
		time.Sleep(3 * time.Second)
	}

}

运行结果
运行结果

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页

打赏作者

死美子

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值