初级会员
  • 第 8368 位会员
  • a0s1d3f4
  • 185145379@qq.com
  • 2017-05-15 05:06:22
  • Offline
  • 21 65

最近发布的文章

    暂无

最近分享的资源

    暂无

最近发布的项目

    暂无

最近的评论

  • #1 @abin 明白了,谢谢您!菜鸟继续学习
  • #3 @tk103331 感谢,我想明白了整个过程!~
  • #2 @dong-hao 非常感谢耐心的回答,我想明白了~
  • #1 @jarlyyn 非常谢谢,彻底想明白这个程序了!
  • 首先,非常谢谢大家耐心的解答~ 我是新手。。接触go不是特别久,甚至刚参加工作不久,写的代码不是特别多。 1.第一个问题我明白了,就是先发消息再注册客户端的问题。 2.发送消息的ClientWriter我是理解的,即从ch这个通道中接收值再发送到conn中去的,只不过,一开始我所疑惑的点是,首先我们在handleConn里定义的一个string类型的局部通道: #Line44 `ch := make(chan string)` 然后,开goroutine并发执行ClientWriter,即从ch中取出string字符串写入连接中: #Line45 `go clientWriter(conn, ch)` #Line66-70 `for msg := range ch { fmt.Fprintln(conn, msg) }` 而每次写入的消息仅为handleConn里定义的局部通道ch里面接收到的string类型字符串: #Line48 `ch <- "You are " + who` 如果messages是通过ClientWriter写入conn的话,除此之外,我没有看到除了45行显示调用ClientWriter的其他代码 #Line45 `go clientWriter(conn, ch)` 就此45行,传入ClientWriter的也只有ch这个定义在handleConn里的局部通道而已,那么messages接收的一系列string字符串,如 #Line50 `messages <- who + " has arrived"` #Line61 `messages <- who + " has left"` 是如何传递给ClientWriter写入连接的呢? 在Broadcaster里,对messages的操作: #Line27-32 `case msg := <-messages:` ` for cli := range clients {` ` cli <- msg` `}` 一开始,在发布这个问题的时候,按照我的理解,首先将messages通道里面string类型的消息msg取出来,再将有登记注册到clients这个map里面的客户端cli取出来,然后将msg发送到cli这个通道中去,那么需要广播的消息再cli这个通道,这里并没有将cli里面的消息发送至所有客户端连接去的操作啊?也并没有显示调用ClientWriter?如何实现广播消息到每个客户端的呢? **随后,这个疑问在大家的帮助下,再经过一些思考,逐渐懂了: 确实除了第45行的显式调用ClientWriter以外,这个程序没有别的地方有显式调用ClientWriter了。但是,我们依旧是可以将每个客户端的messages发送到所有的conn里面去的,因为通过第52行`entering <- ch`,将ch这个代表连接的chan string类型的通道放到了专门用于存放客户端连接通道的通道中(chan chan string类型),假设有客户端A,B,C连接服务器,那么在主goroutine中会开三个handleConn的goroutine分别处理A,B, C的连接,就会有三个不同的客户端通道cha,chb,chc先后存放entering里面,然后从entering里面接收登记注册到Broadcaster里面的clients这个管理连接的map里面(map[chan string]bool),Broadcaster是由主goroutine里面开的另外一个的goroutine来执行的,所以会实时select(匹配),一有客户端消息写入连接,先在handleConn的三个goroutine中分别扫描客户端A,B, C的conn中的消息,一有消息扫描到就发送到messages这个通道中(#Line56)。然后Broadcaster这个goroutine会select到这个case(msg := <-messages),将messages中的消息取出来放到变量msg(string类型),再将所有通过entering登记注册到clients的客户端(chan string类型)cli取出来(这里的cli其实就是在每个客户端连接到服务器打开的用于执行handleConn的goroutine里面的ch,客户端A,B和C分别为cha,chb和chc,因为通过`entering <- ch`和` case cli <- entering: clients[cli] = true`这两句代码传递的通道是同一个通道,指向的是底层同一个地址),将msg放到cli里面(将每个客户端的msg通过遍历clients这个map发送到每个cli(即把messages中每个msg发送到每个客户端的ch通道里面,即广播)),在每个handleConn的goroutine里面又有ClientWriter这个goroutine会随时将每个客户端ch里面的消息写入到conn里面。** 3.第三个问题的理解同上 4.5.第四第五个问题看了回复里各位朋友的回答懂了不少 #Line48 `ch <- "You are " + who` #Line52 `entering <- ch` #Line66-70 `for msg := range ch { fmt.Fprintln(conn, msg) }` #Line33 `case cli := <-entering:` **这四句话并不会导致程序阻塞,第48行,将“You are 主机ip”发送至通道ch中,随后ClientWriter的Goroutine中(第66-70行),会通过for range接收ch里面的消息写入conn,这个消费行为消费的是ch(chan string)里的消息,一发一收不会造成阻塞;第52行:entering <- ch,也不会造成阻塞,entering这个通道(chan chan string)存放的元素是chan string类型的客户端通道,这句代码只是将ch这个chan string通道发送到entering通道里,ch这个通道是代表一个客户端的通道,而ch这个通道里存放的是此客户端的消息。52行和66行的接收并不会造成程序的阻塞。** 6.第六个问题,entering,leaving这两个通道是什么作用,以及怎么管理连接的我也明白了,主要是在handleConn和Broadcaster中登记注册上线和离线的客户端。 我一开始理解有问题,朋友们的回复,加上一些思考,最终理解了整个过程,非常感谢。 特别感谢:jarlyyn ,dong-hao ,tk103331。