go语言圣经,关于第八章并发最后一节的chat.go有些点不太懂,求教

a0s1d3f4 · · 1019 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

```go // Copyright © 2016 Alan A. A. Donovan & Brian W. Kernighan. // License: https://creativecommons.org/licenses/by-nc-sa/4.0/ // See page 254. // Chat is a server that lets clients chat with each other. package main import ( "bufio" "fmt" "log" "net" ) type client chan<- string // an outgoing message channel var ( entering = make(chan client) leaving = make(chan client) messages = make(chan string) // all incoming client messages ) //负责广播的goroutine,抱有疑问,这个函数用一个for死循环嵌套了一个select,通过select去选择性地匹配某一个case,但这些case中好像没有与clientWriter的goroutine通过channel之间进行交流呢,我不太理解,从handleConn中接受的messages是如何通过broadcaster与clientWriter广播写入连接conn中去的呢? //entering与leaving在整个过程中又起到什么作用呢?如果去掉这两个通道又会如何? func broadcaster() { clients := make(map[client]bool) // all connected clients for { select { case msg := <-messages: // Broadcast incoming message to all // clients' outgoing message channels. for cli := range clients { cli <- msg } case cli := <-entering: clients[cli] = true case cli := <-leaving: delete(clients, cli) close(cli) } } } //处理连接的goroutine,每一个客户端与服务器建立的连接都会新开一个goroutine,相互独立。在这个函数中,又开了一个goroutine,并发执行clientWriter,将handleConn的局部变量写入conn中。 func handleConn(conn net.Conn) { ch := make(chan string) // outgoing client messages go clientWriter(conn, ch) who := conn.RemoteAddr().String() //1.用A客户端建立连接,“You are ‘主机ip’”传入局部通道ch,此时执行clientWriter的goroutine将结束阻塞,将传入通道的“You are ‘主机ip’”写入连接conn,“You are ‘主机ip’”会返回至A客户端终端中显示。 ch <- "You are " + who //2.“‘主机ip’ has arrived”传入全局通道messages中,“‘主机ip’ has arrived”不会返回至A客户端终端中,如果此时有B客户端与服务器保持连接,那么“‘主机ip’ has arrived”会返回至B客户端,这是为什么? messages <- who + " has arrived" //3.ch中的消息传给entering这个通道去,我不是很理解这一句话,ch是一个无缓冲通道,即容量为1,刚才注释第一点说了,clientWriter会将通道ch中的消息接收,这样ch中不就没有值了么,这样enter <- ch不就阻塞了么? entering <- ch //4.通过扫描conn中的客户端A写入conn中的信息,将其取出,传入messages通道中,即“‘主机ip’:‘A客户端写入conn的信息’”;最后这个字符串“‘主机ip’:‘A客户端写入conn的信息’”会在A,B客户端的终端显示,这是为什么呢?执行broadcaster()的goroutine并没有将messages的内容发送至某处并将其写入conn的操作啊? input := bufio.NewScanner(conn) for input.Scan() { messages <- who + ": " + input.Text() } // NOTE: ignoring potential errors from input.Err() //5.若客户端A写入连接“control+z”,那么ch会发送至通道leaving,但ch里面不是没有值么,不是会阻塞么?"主机A has left"会发送至通道messages,最后B客户端终端会显示"主机A has left"。 leaving <- ch messages <- who + " has left" conn.Close() } //负责写入conn的goroutine,将单向in通道ch的消息写入conn中(每条连接是相互独立的goroutine) func clientWriter(conn net.Conn, ch <-chan string) { for msg := range ch { fmt.Fprintln(conn, msg) // NOTE: ignoring network errors } } //三个goroutine(broadcaster,handleConn和clientWriter)通过channel进行通信。 func main() { listener, err := net.Listen("tcp", "localhost:8000") if err != nil { log.Fatal(err) } go broadcaster() for { conn, err := listener.Accept() if err != nil { log.Print(err) continue } go handleConn(conn) } } ``` **总结一下我的问题:** 1.(Line 54)“‘主机ip’ has arrived”传入全局通道messages中,“‘主机ip’ has arrived”不会返回至A客户端终端中,如果此时有B客户端与服务器保持连接,那么“‘主机ip’ has arrived”会返回至B客户端,这是为什么? 2.(Line56-59)通过扫描conn中的客户端A写入conn中的信息,将其取出,传入messages通道中,即“‘主机ip’:‘A客户端写入conn的信息’”;最后这个字符串“‘主机ip’:‘A客户端写入conn的信息’”会在A,B客户端的终端显示,这是为什么呢?执行broadcaster()的goroutine并没有将messages的内容发送至某处并将其写入conn的操作啊? 3.(Line23-40,function broadcaster)负责广播的goroutine,抱有疑问,这个函数用一个for死循环嵌套了一个select,通过select去选择性地匹配某一个case,但这些case中好像没有与clientWriter的goroutine通过channel之间进行交流呢,我不太理解,从handleConn中接受的messages是如何通过broadcaster与clientWriter广播写入连接conn中去的呢? 4.(Line55)ch中的消息传给entering这个通道去,我不是很理解这一句话,ch是一个无缓冲通道,即容量为1,而clientWriter会将通道ch中的消息接收,这样ch中不就没有值了么,这样enter <- ch不就阻塞了么? 5.(Line61)若客户端A写入连接“control+z”,那么ch会发送至通道leaving,但ch里面不是没有值么,不是会阻塞么?”主机A has left”会发送至通道messages,最后B客户端终端会显示”主机A has left”。 6.entering与leaving这两个全局通道在整个过程中又起到什么作用呢?如果去掉这两个通道又会如何? 谢谢,希望有哪位能回答我的疑惑~感激不尽!!

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1019 次点击  
加入收藏 微博
8 回复  |  直到 2018-03-07 14:24:51
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传