修改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:]会包括程序本身的路径 + 空格分隔的参数