mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-10-21 22:21:46 +00:00
第8章,部分字词修订,语序调整。
This commit is contained in:
@@ -55,9 +55,9 @@ func broadcaster() {
|
||||
}
|
||||
```
|
||||
|
||||
broadcaster监听来自全局的entering和leaving的channel来获知客户端的到来和离开事件。当其接收到其中的一个事件时,会更新clients集合,当该事件是离开行为时,它会关闭客户端的消息发出channel。broadcaster也会监听全局的消息channel,所有的客户端都会向这个channel中发送消息。当broadcaster接收到什么消息时,就会将其广播至所有连接到服务端的客户端。
|
||||
broadcaster监听来自全局的entering和leaving的channel来获知客户端的到来和离开事件。当其接收到其中的一个事件时,会更新clients集合,当该事件是离开行为时,它会关闭客户端的消息发送channel。broadcaster也会监听全局的消息channel,所有的客户端都会向这个channel中发送消息。当broadcaster接收到什么消息时,就会将其广播至所有连接到服务端的客户端。
|
||||
|
||||
现在让我们看看每一个客户端的goroutine。handleConn函数会为它的客户端创建一个消息发出channel并通过entering channel来通知客户端的到来。然后它会读取客户端发来的每一行文本,并通过全局的消息channel来将这些文本发送出去,并为每条消息带上发送者的前缀来标明消息身份。当客户端发送完毕后,handleConn会通过leaving这个channel来通知客户端的离开并关闭连接。
|
||||
现在让我们看看每一个客户端的goroutine。handleConn函数会为它的客户端创建一个消息发送channel并通过entering channel来通知客户端的到来。然后它会读取客户端发来的每一行文本,并通过全局的消息channel来将这些文本发送出去,并为每条消息带上发送者的前缀来标明消息身份。当客户端发送完毕后,handleConn会通过leaving这个channel来通知客户端的离开并关闭连接。
|
||||
|
||||
```go
|
||||
func handleConn(conn net.Conn) {
|
||||
@@ -87,7 +87,7 @@ func clientWriter(conn net.Conn, ch <-chan string) {
|
||||
}
|
||||
```
|
||||
|
||||
另外,handleConn为每一个客户端创建了一个clientWriter的goroutine来接收向客户端发出消息channel中发送的广播消息,并将它们写入到客户端的网络连接。客户端的读取方循环会在broadcaster接收到leaving通知并关闭了channel后终止。
|
||||
另外,handleConn为每一个客户端创建了一个clientWriter的goroutine,用来接收向客户端发送消息的channel中的广播消息,并将它们写入到客户端的网络连接。客户端的读取循环会在broadcaster接收到leaving通知并关闭了channel后终止。
|
||||
|
||||
下面演示的是当服务器有两个活动的客户端连接,并且在两个窗口中运行的情况,使用netcat来聊天:
|
||||
|
||||
@@ -99,8 +99,7 @@ $ ./netcat3
|
||||
You are 127.0.0.1:64208 $ ./netcat3
|
||||
127.0.0.1:64211 has arrived You are 127.0.0.1:64211
|
||||
Hi!
|
||||
127.0.0.1:64208: Hi!
|
||||
127.0.0.1:64208: Hi!
|
||||
127.0.0.1:64208: Hi! 127.0.0.1:64208: Hi!
|
||||
Hi yourself.
|
||||
127.0.0.1:64211: Hi yourself. 127.0.0.1:64211: Hi yourself.
|
||||
^C
|
||||
@@ -113,12 +112,12 @@ You are 127.0.0.1:64216 127.0.0.1:64216 has arrived
|
||||
127.0.0.1:64211 has left”
|
||||
```
|
||||
|
||||
当与n个客户端保持聊天session时,这个程序会有2n+2个并发的goroutine,然而这个程序却并不需要显式的锁(§9.2)。clients这个map被限制在了一个独立的goroutine中,broadcaster,所以它不能被并发地访问。多个goroutine共享的变量只有这些channel和net.Conn的实例,两个东西都是并发安全的。我们会在下一章中更多地解决约束,并发安全以及goroutine中共享变量的含义。
|
||||
当与n个客户端保持聊天session时,这个程序会有2n+2个并发的goroutine,然而这个程序却并不需要显式的锁(§9.2)。clients这个map被限制在了一个独立的goroutine中,broadcaster,所以它不能被并发地访问。多个goroutine共享的变量只有这些channel和net.Conn的实例,两个东西都是并发安全的。我们会在下一章中更多地讲解约束,并发安全以及goroutine中共享变量的含义。
|
||||
|
||||
**练习 8.12:** 使broadcaster能够将arrival事件通知当前所有的客户端。为了达成这个目的,你需要有一个客户端的集合,并且在entering和leaving的channel中记录客户端的名字。
|
||||
|
||||
**练习 8.13:** 使聊天服务器能够断开空闲的客户端连接,比如最近五分钟之后没有发送任何消息的那些客户端。提示:可以在其它goroutine中调用conn.Close()来解除Read调用,就像input.Scanner()所做的那样。
|
||||
|
||||
**练习 8.14:** 修改聊天服务器的网络协议这样每一个客户端就可以在entering时可以提供它们的名字。将消息前缀由之前的网络地址改为这个名字。
|
||||
**练习 8.14:** 修改聊天服务器的网络协议,这样每一个客户端就可以在entering时提供他们的名字。将消息前缀由之前的网络地址改为这个名字。
|
||||
|
||||
**练习 8.15:** 如果一个客户端没有及时地读取数据可能会导致所有的客户端被阻塞。修改broadcaster来跳过一条消息,而不是等待这个客户端一直到其准备好写。或者为每一个客户端的消息发出channel建立缓冲区,这样大部分的消息便不会被丢掉;broadcaster应该用一个非阻塞的send向这个channel中发消息。
|
||||
**练习 8.15:** 如果一个客户端没有及时地读取数据可能会导致所有的客户端被阻塞。修改broadcaster来跳过一条消息,而不是等待这个客户端一直到其准备好读写。或者为每一个客户端的消息发送channel建立缓冲区,这样大部分的消息便不会被丢掉;broadcaster应该用一个非阻塞的send向这个channel中发消息。
|
||||
|
Reference in New Issue
Block a user