Go语言精髓 • 【第5章 go圣经上的一个并发小例子】

修改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

服务端代码,支持go run传参启动多个服务端

$ go run clock2.go -port 9090 -city beijing

$ go run clock2.go -port 9089 -city shanghai

/clock2.go

package main

import (
	"io"
	"log"
	"net"
	"time"
	"strconv"
	"fmt"
	"flag"
)
var port string
var city string

func init() {
    flag.StringVar(&port,"port","default","8090")
	flag.StringVar(&city,"city","default","default")
}

func main() {
	flag.Parse()//暂停获取参数
	fmt.Println(port)
	fmt.Println(city)
	listenConn(port)
}

func handleConn(conn net.Conn) {
	defer conn.Close()
	for {
		_, err := io.WriteString(conn, city + time.Now().Format("15:34:09\n"))
		if err != nil {
			return
		}
		time.Sleep(1 * time.Second)
	}
}

func listenConn(port string){
	listener, err := net.Listen("tcp", "localhost:" + port)
	if err != nil {
		log.Fatal(err)
		fmt.Print(err)
	}
	i := 0
	for {
		conn, err := listener.Accept()
		i++
		fmt.Println("get new conn" + strconv.Itoa(i))
		if err != nil {
			log.Print(err)
			continue
		}

		go handleConn(conn)
	}
}

客户端代码,先粘贴自己的第一次尝试代码

package main

import(
	"io"
	"log"
	"net"
	"os"
	"flag"
	"strings"
	"fmt"
)

var port string

func init() {
    flag.StringVar(&port,"port","default","8090")
	
}
func main(){
flag.Parse()//暂停获取参数
	a := strings.Split(port, ";")
	for _,v := range a{
		fmt.Println(v)
		go portDial(v)
	}
}

func mustCopy(dst io.Writer,src io.Reader){
	if _,err := io.Copy(dst,src);err != nil{
		log.Fatal(err)
	
	}
}

func portDial(po string){

	conn,err:= net.Dial("tcp","localhost:" + po)
	if err!= nil{
		log.Fatal(err)
	}
	
	defer conn.Close()
	mustCopy(os.Stdout,conn)
}

该次没有打打印出任何内容,因为主线程直接退出了,此处暂不考虑channel,将主线程死循环或者停顿10秒看效果,改进代码如下:

package main

import(
	"io"
	"log"
	"net"
	"os"
	"flag"
	"strings"
	"fmt"
	"time"
)

var port string

func init() {
    flag.StringVar(&port,"port","default","8090")
	
}
func main(){
flag.Parse()//暂停获取参数
fmt.Println(port)
	a := strings.Split(port, ",")
	fmt.Println(a)
	for _,v := range a{
		fmt.Println(v)
		go portDial(v)
	}
	
	for{
		time.Sleep(10 * time.Second)
	}
}

func mustCopy(dst io.Writer,src io.Reader){
	if _,err := io.Copy(dst,src);err != nil{
		log.Fatal(err)
	
	}
}

func portDial(po string){

	conn,err:= net.Dial("tcp","localhost:" + po)
	if err!= nil{
		log.Fatal(err)
	}
	
	defer conn.Close()
	mustCopy(os.Stdout,conn)
}

$ go run netcat.go -port 9090;9089

便得到预期结果:

另外在网上看到一篇差不多的客户端实现方式

// Netcat1 is a read-only TCP client.
package main
 
import (
        "io"
        "log"
        "net"
        "os"
        "strings"
        "time"
)
 
func main() {
        for _, v := range os.Args[1:] {
                keyValue := strings.Split(v, "=")
                go connTcp(keyValue[1])
        }  
        for {
                time.Sleep(1 * time.Second)
        }  
}
 
func connTcp(uri string) {
        conn, err := net.Dial("tcp", uri)
        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 {
                log.Fatal(err)
        }  
}

$ go run netcat1.go beijing=localhost:9090 hangzhou=localhost:9089

也可得到预期结果

注:

*io.Copy() 方法将副本从 src 复制到 dst ,直到 src 达到文件末尾 ( EOF ) 或发生错误,然后返回复制的字节数和复制时遇到的第一个错误( 如果有 );

func Copy(dst Writer, src Reader) (written int64, err error)

Stdin io.Reader 命令进程的输入
 Stdout io.Writer 命令进程的输出
 Stderr io.Writer 命令进程的错误输出

*os.Args[1:]类型是 []string ,也就是字符串切片。直接能获取到go run 后面的参数,以空格为多个元素的分隔符

os.Args[0:]会包括程序本身的路径 + 空格分隔的参数 

 https://www.cnblogs.com/taoshihan/p/8955365.html

发布了7 篇原创文章 · 获赞 5 · 访问量 272

猜你喜欢

转载自blog.csdn.net/weixin_38193228/article/details/105548388