Procházet zdrojové kódy

Fix 14.5, 14.6, 15.1 (#283)

* Update 07.3.md

* Update 07.3.md

* Update 07.6.md

* fix few problems

* Update 10.6 and 10.8

* Update 11.6 and 11.7

* fix some problems before Chap13

* add a dot

* Update 13.9

* little improve

* Thoughts about time.After

* Thoughts about time.After

* Fix 14.6,15.1
王耀 před 9 roky
rodič
revize
f2a3e873fe

+ 17 - 2
eBook/14.5.md

@@ -137,7 +137,7 @@ select {
 
 第二种形式:取消耗时很长的同步调用
 
-也可以使用 `time.After()` 函数替换 `timeout-channel`。可以在 `select` 中使用以发送信号超时或停止协程的执行。以下代码,在 `timeoutNs` 纳秒后执行 `select` 的 `timeout` 分支时,`client.Call` 不会给通道 `ch` 返回值:
+也可以使用 `time.After()` 函数替换 `timeout-channel`。可以在 `select` 中通过 `time.After()` 发送的超时信号来停止协程的执行。以下代码,在 `timeoutNs` 纳秒后执行 `select` 的 `timeout` 分支后,执行`client.Call` 的协程也随之结束,不会给通道 `ch` 返回值:
 
 ```go
 ch := make(chan error, 1)
@@ -151,7 +151,22 @@ case <-time.After(timeoutNs):
 }
 ```
 
-注意缓冲大小设置为 1 是必要的,可以避免协程死锁以及确保超时的通道可以被垃圾回收。
+注意缓冲大小设置为 1 是必要的,可以避免协程死锁以及确保超时的通道可以被垃圾回收。此外,需要注意在有多个 `case` 符合条件时, `select` 对 `case` 的选择是伪随机的,如果上面的代码稍作修改如下,则 `select` 语句可能不会在定时器超时信号到来时立刻选中 `time.After(timeoutNs)` 对应的 `case`,因此协程可能不会严格按照定时器设置的时间结束。
+
+```go
+ch := make(chan int, 1)
+go func() { for { ch <- 1 } } ()
+L:
+for {
+    select {
+    case <-ch:
+        // do something
+    case <-time.After(timeoutNs):
+        // call timed out
+        break L
+    }
+}
+```
 
 第三种形式:假设程序从多个复制的数据库同时读取。只需要一个答案,需要接收首先到达的答案,`Query` 函数获取数据库的连接切片并请求。并行请求每一个数据库并返回收到的第一个响应:
 

+ 1 - 1
eBook/14.6.md

@@ -21,7 +21,7 @@ func safelyDo(work *Work) {
 
 上边的代码,如果 `do(work)` 发生 panic,错误会被记录且协程会退出并释放,而其他协程不受影响。
 
-因为 `recover` 总是返回 `nil`,除非直接在 `defer` 修饰的函数中调用,`defer` 修饰的代码可以调用那些自身可以使用 `panic` 和 `recover` 避免失败的库例程(库函数)。举例,`safelyDo()` 中 `deffer` 修饰的函数可能在调用 `recover` 之前就调用了一个 `logging` 函数,`panicking` 状态不会影响 `logging` 代码的运行。因为加入了恢复模式,函数 `do`(以及它调用的任何东西)可以通过调用 `panic` 来摆脱不好的情况。但是恢复是在 `panicking` 的协程内部的:不能被另外一个协程恢复。
+因为 `recover` 总是返回 `nil`,除非直接在 `defer` 修饰的函数中调用,`defer` 修饰的代码可以调用那些自身可以使用 `panic` 和 `recover` 避免失败的库例程(库函数)。举例,`safelyDo()` 中 `defer` 修饰的函数可能在调用 `recover` 之前就调用了一个 `logging` 函数,`panicking` 状态不会影响 `logging` 代码的运行。因为加入了恢复模式,函数 `do`(以及它调用的任何东西)可以通过调用 `panic` 来摆脱不好的情况。但是恢复是在 `panicking` 的协程内部的:不能被另外一个协程恢复。
 
 
 ## 链接

+ 5 - 5
eBook/15.1.md

@@ -35,18 +35,18 @@ func main() {
 func doServerStuff(conn net.Conn) {
 	for {
 		buf := make([]byte, 512)
-		_, err := conn.Read(buf)
+		len, err := conn.Read(buf)
 		if err != nil {
 			fmt.Println("Error reading", err.Error())
 			return //终止程序
 		}
-		fmt.Printf("Received data: %v", string(buf))
+		fmt.Printf("Received data: %v", string(buf[:len]))
 	}
 }
 
 ```
 
-我们在`main()`创建了一个`net.Listener`的变量,他是一个服务器的基本函数:用来监听和接收来自客户端的请求(来自localhost即IP地址为127.0.0.1端口为50000基于TCP协议)。这个`Listen()`函数可以返回一个`error`类型的错误变量。用一个无限for循环的`listener.Accept()`来等待客户端的请求。客户端的请求将产生一个`net.Conn`类型的连接变量。然后一个独立的携程使用这个连接执行`doServerStuff()`,开始使用一个512字节的缓冲`data`来读取客户端发送来的数据并且把它们打印到服务器的终端;当客户端发送的所有数据都被读取完成时,携程就结束了。这段程序会为每一个客户端连接创建一个独立的携程。必须先运行服务器代码,再运行客户端代码。
+我们在`main()`创建了一个`net.Listener`的变量,他是一个服务器的基本函数:用来监听和接收来自客户端的请求(来自localhost即IP地址为127.0.0.1端口为50000基于TCP协议)。这个`Listen()`函数可以返回一个`error`类型的错误变量。用一个无限for循环的`listener.Accept()`来等待客户端的请求。客户端的请求将产生一个`net.Conn`类型的连接变量。然后一个独立的携程使用这个连接执行`doServerStuff()`,开始使用一个512字节的缓冲`data`来读取客户端发送来的数据并且把它们打印到服务器的终端,`len`获取客户端发送的数据字节数;当客户端发送的所有数据都被读取完成时,携程就结束了。这段程序会为每一个客户端连接创建一个独立的携程。必须先运行服务器代码,再运行客户端代码。
 
 客户端代码写在另外一个文件client.go中:
 
@@ -106,7 +106,7 @@ func main() {
 
 然后开启2个或者3个独立的控制台窗口,然后分别输入client回车启动客户端程序
 
-以下是服务器的输出(在移除掉512字节的字符串中内容为空的区域后)
+以下是服务器的输出:
 ```
 Starting the Server ...
 Received data: IVO says: Hi Server, what's up ?
@@ -229,7 +229,7 @@ func main() {
 func initServer(hostAndPort string) *net.TCPListener {
 	serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
 	checkError(err, "Resolving address:port failed: '"+hostAndPort+"'")
-	listener, err := net.ListenTCP("tcp", serverAddr)
+	listener, err := net.Listen("tcp", serverAddr)
 	checkError(err, "ListenTCP: ")
 	println("Listening to: ", listener.Addr().String())
 	return listener

+ 2 - 2
eBook/examples/chapter_15/server.go

@@ -27,11 +27,11 @@ func main() {
 func doServerStuff(conn net.Conn) {
 	for {
 		buf := make([]byte, 512)
-		_, err := conn.Read(buf)
+		len, err := conn.Read(buf)
 		if err != nil {
 			fmt.Println("Error reading", err.Error())
 			return //终止程序
 		}
-		fmt.Printf("Received data: %v", string(buf))
+		fmt.Printf("Received data: %v", string(buf[:len]))
 	}
 }

+ 2 - 2
eBook/examples/chapter_15/simple_tcp_server.go

@@ -4,8 +4,8 @@ package main
 import (
 	"flag"
 	"fmt"
-	"os"
 	"net"
+	"os"
 )
 
 const maxRead = 25
@@ -27,7 +27,7 @@ func main() {
 func initServer(hostAndPort string) *net.TCPListener {
 	serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
 	checkError(err, "Resolving address:port failed: '"+hostAndPort+"'")
-	listener, err := net.ListenTCP("tcp", serverAddr)
+	listener, err := net.Listen("tcp", serverAddr)
 	checkError(err, "ListenTCP: ")
 	println("Listening to: ", listener.Addr().String())
 	return listener

+ 1 - 1
eBook/examples/chapter_15/simple_tcp_server_v1.go

@@ -26,7 +26,7 @@ func main() {
 func initServer(hostAndPort string) *net.TCPListener {
 	serverAddr, err := net.ResolveTCPAddr("tcp", hostAndPort)
 	checkError(err, "Resolving address:port failed: '"+hostAndPort+"'")
-	listener, err := net.ListenTCP("tcp", serverAddr)
+	listener, err := net.Listen("tcp", serverAddr)
 	checkError(err, "ListenTCP: ")
 	println("Listening to: ", listener.Addr().String())
 	return listener