ch8/ch8-02 #155
Replies: 5 comments 6 replies
-
练习8.1感觉自己实现的不是很好, 调试了很长时间, 因为对协程机制不是很熟悉, 我太小白了5555~_~、、、 package main
import (
"fmt"
"io"
"log"
"net"
"os"
"strings"
"sync"
"time"
)
// 定义一个*data类型切片
var dSlice []*data
func main() {
// 1. 从控制台读取要记录的时区服务器地址
// 2. 启动一个goroutine,每隔一秒钟向服务器发送一个请求,获取当前时间
// 3. 将服务器返回的时间输出到控制台
var wg sync.WaitGroup
wg.Add(len(os.Args[1:]))
// 获取服务器地址
// Tokyo=localhost:8020 London=localhost:8030
var server = make(map[int][]string)
for i, v := range os.Args[1:] {
// 处理输入
server[i] = strings.Split(v, "=")
}
// 启动时钟
for i, server := range server {
// 创建一个data类型的指针
dSlice = append(dSlice, new(data))
// 启动时钟
go clock(server[1], dSlice[i], &wg)
}
// 输出
// $ go run 8.1.go China=localhost:8000 Tokyo=localhost:8010
//====================================
//Tokyo localhost:8010 13:55:32
//China localhost:8000 12:55:32
// 输出前等待1s, 保证数据已经接收到并且写入到data类型的指针中
time.Sleep(1 * time.Second)
for {
fmt.Println("====================================")
for i, s := range server {
go output(s, dSlice[i])
}
time.Sleep(1 * time.Second)
}
// 尽管上面的死循环会阻塞主协程
// 但是还是写上吧
// 调试也方便
wg.Wait()
}
func output(ser []string, data *data) {
fmt.Println(ser[0], ser[1], data)
}
// data 类型
type data string
// 输出*data的时候,直接输出解引用data的值
func (d *data) String() string {
return string(*d)
}
// 实现Writer接口, 用于将数据写入到data类型的指针中
func (d *data) Write(p []byte) (n int, err error) {
// 写入数据
*d = data(p)
//fmt.Println(d)
return len(p), nil
}
// 编写监听多个服务器的时钟函数
func clock(server string, data *data, wg *sync.WaitGroup) {
defer wg.Done()
//fmt.Println(server)
conn, err := net.Dial("tcp", server)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
mustCopy(data, conn)
}
func mustCopy(dst io.Writer, src io.Reader) {
if _, err := io.Copy(dst, src); err != nil {
log.Fatalln(err)
}
} |
Beta Was this translation helpful? Give feedback.
-
抱歉了,打扰到大佬了~B站里逛习惯了,感谢大佬翻译,我会控制下自己,把理解到的东西和补充发在评论区,再次感谢大佬回复,对于之前的评论深表歉意,您可以把没用的删掉
| |
zhangbest5
|
|
***@***.***
|
---- Replied Message ----
| From | ***@***.***> |
| Date | 1/13/2023 16:36 |
| To | ***@***.***> |
| Cc | ***@***.***> ,
***@***.***> |
| Subject | Re: [gopl-zh/gopl-zh.github.com] ch8/ch8-02 (Discussion #155) |
直接说问题比较好,抱怨也改变不了什么,毕竟开源社区大家都用爱发电。
PS:代码应该是原版保持一样的,中文翻译表达可能有待改进之处,欢迎提改进建议
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.Message ID: ***@***.***>
|
Beta Was this translation helpful? Give feedback.
-
8.1
server 服务端package main
import (
"flag"
"fmt"
"io"
"net"
"sync"
"time"
)
func main() {
var address string
// 通过命令行参数来获取端口
// flag来通过-port指定端口
flag.StringVar(&address, "port", ":8000", "端口号,默认8000")
flag.Parse()
fmt.Println(address)
// 开启服务
ListenAndServer(string(":" + address))
}
func ListenAndServer(address string) error {
// 建立连接(端口监听)
listener, err := net.Listen("tcp", address)
// 退出时关闭连接
//defer listener.Close()
if err != nil {
return err
}
// 等待组,防止函数退出时还有业务在执行
waitGroup := sync.WaitGroup{}
// 循环监听新连接(客户端连接)
for {
conn, err := listener.Accept()
// 新的连接等待组+1
waitGroup.Add(1)
if err != nil {
break
}
// 协程对应连接
go func(conn net.Conn) {
// 协程结束,等待组-1
defer waitGroup.Done()
// 向客户端写入时间以及当前服务器端口
io.WriteString(conn, time.Now().Format("15:04:05\n"))
}(conn)
}
waitGroup.Wait()
return nil
} client 客户端代码package main
import (
"io"
"net"
"os"
"strings"
"sync"
)
/*
时区id
China Standard Time
Eastern Standard Time
Hawaiian Standard Time
*/
func main() {
// 设置等待组
waitGroup := sync.WaitGroup{}
// 从命令行获取要访问的端口及其对应的时区
for _, val := range os.Args[1:] {
// 以等号分割
line := strings.Split(val, "=")
// 获取对应的时区及端口
tz, address := line[0], line[1]
// 等待组+1
waitGroup.Add(1)
go getTime(address, tz, &waitGroup)
}
waitGroup.Wait()
}
// 从指定服务器获取其时间
func getTime(address string, tz string, wait *sync.WaitGroup) {
// 等待组-1
defer wait.Done()
// 创建tcp client
conn, err := net.Dial("tcp", address)
if err != nil {
panic(err)
}
// 读取服务端发送过来的时间
time := make([]byte, 100)
conn.Read(time[:])
// 输出到标准输出流(时区=时间)
io.WriteString(os.Stdout, tz+"="+string(time)+"\n")
} 启动程序,开多个终端启动
结果
|
Beta Was this translation helpful? Give feedback.
-
提供一个比较完美的版本, 支持不滚动刷新: 客户端 import ( var citySlice []city type city struct { func main() {
} func printRow(city string, time *data) { func (d *data) String() string { func (d *data) Write(p []byte) (n int, err error) { func getTime(address string, data *data, wg *sync.WaitGroup) { func mustCopy(dst io.Writer, src io.Reader) {
显示效果如下:
|
Beta Was this translation helpful? Give feedback.
-
我来提供一个可以多次读取的版本,每次打印所有服务器的时间,一轮打完后再打下一轮 客户端 package main
import (
"fmt"
"log"
"net"
"sync"
"time"
)
type Server struct {
timeZone string
addr string
}
// CyclicBarrier的golang实现
// 功能类似于可循环使用的WaitGroup
type CyclicBarrier struct {
count int
waiting int
mutex sync.Mutex
cond *sync.Cond
}
func NewCyclicBarrier(count int) *CyclicBarrier {
cb := &CyclicBarrier{count: count}
cb.cond = sync.NewCond(&cb.mutex)
return cb
}
func (cb *CyclicBarrier) Await() {
cb.mutex.Lock()
defer cb.mutex.Unlock()
cb.waiting++
//到了一轮了
if cb.waiting == cb.count {
cb.waiting = 0
cb.cond.Broadcast() // 唤醒所有等待的协程
} else {
cb.cond.Wait() // 当前协程进入等待状态
}
}
var dataChan chan string
var barrier *CyclicBarrier
const LOOP_COUNT = 10
func main() {
//这里就直接初始化server了,不从os.Args或flag读了
servers := []Server{
{timeZone: "Shanghai",
addr: "127.0.0.1:8081"},
{timeZone: "NewYork",
addr: "127.0.0.1:8082"},
{timeZone: "London",
addr: "127.0.0.1:8083"},
{timeZone: "Tokyo",
addr: "127.0.0.1:8084"},
{timeZone: "HongKong",
addr: "127.0.0.1:8085"},
}
barrier = NewCyclicBarrier(len(servers) + 1)
dataChan = make(chan string, len(servers))
for _, server := range servers {
go conn(server)
}
for i := 0; i < LOOP_COUNT; i++ {
barrier.Await()
//一轮结束读取全部client写入chan的数据
for i := 0; i < len(servers); i++ {
fmt.Println(<-dataChan)
}
fmt.Print("==========================\n\n\n")
time.Sleep(2 * time.Second)
}
close(dataChan)
}
func conn(server Server) {
client, err := net.Dial("tcp", server.addr)
if err != nil {
log.Fatal(err)
}
defer client.Close()
for {
var data [100]byte
n, _ := client.Read(data[:])
dataChan <- server.timeZone + "===" + string(data[:n])
barrier.Await()
}
} 服务端 package main
import (
"flag"
"log"
"net"
"strconv"
"time"
)
func main() {
port := flag.Int("p", -1, "port")
flag.Parse()
server, err := net.Listen("tcp", ":"+strconv.Itoa(*port))
if err != nil {
log.Fatal(err)
}
for {
conn, _ := server.Accept()
go handleConn(conn)
}
}
func handleConn(conn net.Conn) {
defer conn.Close()
for {
conn.Write([]byte(time.Now().Format("2006-01-02 15:04:05")))
time.Sleep(10 * time.Second)
}
} 其中主要是用到了 type CyclicBarrier struct {
count int
waiting int
mutex sync.Mutex
release chan struct{}
}
func NewCyclicBarrier(count int) *CyclicBarrier {
return &CyclicBarrier{
count: count,
release: make(chan struct{}),
}
}
func (cb *CyclicBarrier) Await() {
cb.mutex.Lock()
cb.waiting++
if cb.waiting == cb.count {
cb.waiting = 0
close(cb.release)
cb.release = make(chan struct{})
cb.mutex.Unlock()
return
}
release := cb.release
cb.mutex.Unlock()
<-release
} 思路是一样的,一个靠condition |
Beta Was this translation helpful? Give feedback.
-
ch8/ch8-02
中文版
https://golang-china.github.io/gopl-zh/ch8/ch8-02.html
Beta Was this translation helpful? Give feedback.
All reactions