From 8b8d7cb48360f45c84452fa1d1c3676d7e283791 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Wed, 9 Dec 2015 21:18:32 +0800
Subject: [PATCH 01/12] Create 15.2.md
---
eBook/15.2.md | 14 ++++++++++++++
1 file changed, 14 insertions(+)
create mode 100644 eBook/15.2.md
diff --git a/eBook/15.2.md b/eBook/15.2.md
new file mode 100644
index 0000000..17ea62c
--- /dev/null
+++ b/eBook/15.2.md
@@ -0,0 +1,14 @@
+# 15.2 一个简单的网页服务器
+
+Http是一个比tcp更高级的协议,它描述了客户端浏览器如何与网页服务器进行通信。Go有自己的`net/http`包,我们来看看它。我们从一些简单的示例开始,
+首先编写一个“Hello world!”:[查看示例15.6](examples/chapter_15/hello_world_webserver.go)
+
+我们引入了`http`包并启动了网页服务器,和15.1的`net.Listen("tcp", "localhost:50000")`函数的tcp服务器是类似的,使用`http.ListenAndServe("localhost:8080", nil)`函数,如果成功会返回空,否则会返回一个错误(可以指定localhost为其他地址,8080是指定的端口号)
+
+
+
+## 链接
+
+- [目录](directory.md)
+- 上一章:[tcp服务器](15.1.md)
+- 下一节:[写一个简单的网页应用](15.3.md)
From 76129ffbb646f59c0cc92a13453e2f5cfdf1f164 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Wed, 9 Dec 2015 23:09:26 +0800
Subject: [PATCH 02/12] Update 15.2.md
---
eBook/15.2.md | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/eBook/15.2.md b/eBook/15.2.md
index 17ea62c..15057f8 100644
--- a/eBook/15.2.md
+++ b/eBook/15.2.md
@@ -5,6 +5,28 @@ Http是一个比tcp更高级的协议,它描述了客户端浏览器如何与
我们引入了`http`包并启动了网页服务器,和15.1的`net.Listen("tcp", "localhost:50000")`函数的tcp服务器是类似的,使用`http.ListenAndServe("localhost:8080", nil)`函数,如果成功会返回空,否则会返回一个错误(可以指定localhost为其他地址,8080是指定的端口号)
+`http.URL`描述了web服务器的地址,内含存放了url字符串的`Path`属性;`http.Request`描述了客户端请求,内含一个`URL`属性
+
+如果`req`请求是一个POST类型的html表单,“var1”就是html表单中一个输入属性的名称,然后用户输入的值就可以通过GO代码:`req.FormValue("var1")`获取到(请看[章节15.4](15.4.md))。还有一种方法就是先执行`request.ParseForm()`然后再获取`request.Form["var1"]的第一个返回参数,就像这样:
+```go
+ var1, found := request.Form["var1"]
+```
+第二个参数`found`就是`true`,如果`var1`并未出现在表单中,`found`就是`false`
+
+表单属性实际上是一个`map[string][]string`类型。网页服务器返回了一个`http.Response`,它是通过`http.ResponseWriter`对象输出的,这个对象整合了HTTP服务器的返回结果;通过对它写入内容,我们就讲数据发送给了HTTP客户端。
+
+现在我们还需要编写网页服务器必须执行的程序,它是如何处理请求的呢。这是在`http.HandleFunc`函数中完成的,就是在这个例子中当根路径“/”(url地址是http://localhost:8080)被请求的时候(或者这个服务器上的其他地址),`HelloServer`函数就被执行了。这个函数是`http.HandlerFunc`类型的,它们通常用使用Prehandler来命名,在前边加了一个Pref前缀。
+
+`http.HandleFunc`注册了一个处理函数(这里是`HelloServer`)来处理对应`/`的请求。
+
+`/`可以被替换为其他特定的url比如`/create`,`/edit`等等;你可以为每一个特定的url定义一个单独的处理函数。这个函数需要两个参数:第一个是`ReponseWriter`类型的`w`;第二个是请求`req`。程序向`w`写入了`Hello`和`r.URL.Path[1:]`组成的字符串后边的`[1:]`表示“创建一个从第一个字符到结尾的子切片”,用来丢弃掉路径开头的“/”,`fmt.Fprintf()`函数完成了本次写入(请看[章节12.8](12.8.md));另外一种写法是`io.WriteString(w, "hello, world!\n")`
+
+总结:第一个参数是请求的路径,第二个参数是处理这个路径请求的函数的引用。
+
+示例 15.6 [hello_world_webserver.go](examples/chapter_15/hello_world_webserver.go):
+```go
+```
+
## 链接
From 57df7df3975651fa0b93ccbb20d2af1b25b363b5 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Wed, 9 Dec 2015 23:17:02 +0800
Subject: [PATCH 03/12] Create hello_world_webserver.go
---
.../chapter_15/hello_world_webserver.go | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 eBook/examples/chapter_15/hello_world_webserver.go
diff --git a/eBook/examples/chapter_15/hello_world_webserver.go b/eBook/examples/chapter_15/hello_world_webserver.go
new file mode 100644
index 0000000..184f96b
--- /dev/null
+++ b/eBook/examples/chapter_15/hello_world_webserver.go
@@ -0,0 +1,20 @@
+package main
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+)
+
+func HelloServer(w http.ResponseWriter, req *http.Request) {
+ fmt.Println("Inside HelloServer handler")
+ fmt.Fprintf(w, "Hello,"+req.URL.Path[1:])
+}
+
+func main() {
+ http.HandleFunc("/", HelloServer)
+ err := http.ListenAndServe("localhost:8080", nil)
+ if err != nil {
+ log.Fatal("ListenAndServe: ", err.Error())
+ }
+}
From b28ed460debbc1e40b5a3a6e306c39f750dc5e4a Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Wed, 9 Dec 2015 23:38:41 +0800
Subject: [PATCH 04/12] Update 15.2.md
---
eBook/15.2.md | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/eBook/15.2.md b/eBook/15.2.md
index 15057f8..600d3b1 100644
--- a/eBook/15.2.md
+++ b/eBook/15.2.md
@@ -25,7 +25,48 @@ Http是一个比tcp更高级的协议,它描述了客户端浏览器如何与
示例 15.6 [hello_world_webserver.go](examples/chapter_15/hello_world_webserver.go):
```go
+package main
+
+import (
+ "fmt"
+ "log"
+ "net/http"
+)
+
+func HelloServer(w http.ResponseWriter, req *http.Request) {
+ fmt.Println("Inside HelloServer handler")
+ fmt.Fprintf(w, "Hello,"+req.URL.Path[1:])
+}
+
+func main() {
+ http.HandleFunc("/", HelloServer)
+ err := http.ListenAndServe("localhost:8080", nil)
+ if err != nil {
+ log.Fatal("ListenAndServe: ", err.Error())
+ }
+}
```
+使用命令行启动程序,会打开一个命令窗口显示如下文字:
+```
+Starting Process E:/Go/GoBoek/code_examples/chapter_14/hello_world_webserver.exe
+...
+```
+然后打开你的浏览器并输入url地址:`http://localhost:8080/world`,浏览器就会出现文字:`Hello, world`,网页服务器会响应你在`:8080/`后边输入的内容
+
+使用`fmt.Println`在控制台打印状态,在每个handler被请求的时候,在他们内部打印日志会很有帮助
+
+注:
+1)前两行(没有错误处理代码)可以替换成以下写法:
+```go
+http.ListenAndServe(":8080", http.HandlerFunc(HelloServer))
+```
+2)`fmt.Fprint`和`fmt.Fprintf`都是用来写入`http.ResponseWriter`的不错的函数(他们实现了`io.Writer`)。
+比如我们可以使用
+```go
+fmt.Fprintf(w, "
%s
%s
", title, body)
+```
+来构建一个非常简单的网页并插入`title`和`body`的值
+如果你需要更多复杂的替换,使用模板包(请看[章节15.7](15.7.md))
From 7485bf8ddc827a80f4e8e2ff41f3f8a0e8a6e1f4 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Thu, 10 Dec 2015 12:26:56 +0800
Subject: [PATCH 05/12] Update 15.2.md
---
eBook/15.2.md | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/eBook/15.2.md b/eBook/15.2.md
index 600d3b1..fe801a5 100644
--- a/eBook/15.2.md
+++ b/eBook/15.2.md
@@ -66,7 +66,45 @@ http.ListenAndServe(":8080", http.HandlerFunc(HelloServer))
fmt.Fprintf(w, "%s
%s
", title, body)
```
来构建一个非常简单的网页并插入`title`和`body`的值
+
如果你需要更多复杂的替换,使用模板包(请看[章节15.7](15.7.md))
+3)如果你需要使用安全的https连接,使用`http.ListenAndServeTLS()`代替`http.ListenAndServe()`
+4)`http.HandleFunc("/", Hfunc)`中的`HFunc`是一个处理函数,如下:
+```go
+func HFunc(w http.ResponseWriter, req *http.Request) {
+ ...
+}
+```
+也可以使用这种方式:`http.Handle("/", http.HandlerFunc(HFunc))`
+
+上边的`HandlerFunc`只是一个类型名称,它定义如下:
+```go
+type HandlerFunc func(ResponseWriter, *Request)
+```
+它是一个可以把普通的函数当做HTPP处理器的适配器。如果`f`函数声明的合适,`HandlerFunc(f)`就是一个执行了`f`函数的处理器对象。
+
+`http.Handle`的第二个参数也可以是`T`的一个obj对象:`http.Handle("/", obj)`给T提供了`SereHTTP`方法,实现了http的`Handler`接口:
+```go
+func (obj *Typ) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+ ...
+}
+```
+这个用法在[章节15.8](15.8.md)类型`Counter`和`Chan`上使用过。只要实现了`http.Handler`,`http`包就可以处理任何HTTP请求。
+
+练习 15.2:[webhello2.go](exercises/chapter_15/webhello2.go)
+
+>编写一个网页服务器监听端口9999,有如下处理函数:
+
+>i)当请求`http://localhost:9999/hello/Name`时,响应:`hello Name`(Name需是一个合法的姓,比如Chris或者Madeleine)
+
+>ii)当请求`http://localhost:9999/shouthello/Name`时,响应:`hello NAME`
+
+练习 15.3:[hello_server.go](exercises/chapter_15/hello_server.go)
+
+>
+
+
+
From fb61970fd1284e39789c8bc9a78740e3464161a7 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Thu, 10 Dec 2015 12:34:20 +0800
Subject: [PATCH 06/12] Update 15.1.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
修复部分格式
增加练习15.1的链接
---
eBook/15.1.md | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/eBook/15.1.md b/eBook/15.1.md
index 48f1764..e5a9590 100644
--- a/eBook/15.1.md
+++ b/eBook/15.1.md
@@ -183,7 +183,7 @@ func main() {
con.Close()
}
```
-**练习 15.1** 编写新版本的客户端和服务器(client1.to / server1.go):
+**练习 15.1** 编写新版本的客户端和服务器([client1.to](exercises/chapter_15/client1.go) / [server1.go](exercises/chapter_15/server1.go)):
* 增加一个检查错误的函数`checkError(error)`;讨论如下方案的利弊:为什么这个重构可能并没有那么理想?看看在示例15.14中它是如何被解决的
* 使客户端可以通过发送一条命令SH来关闭服务器
* 让服务器可以保存已经连接的客户端列表(他们的名字);当客户端发送WHO指令的时候,服务器将显示如下列表:
@@ -286,8 +286,7 @@ func checkError(error error, info string) {
(**译者注:应该是由于go版本的更新,会提示os.EAGAIN undefined ,修改后的代码:[simple_tcp_server_v1.go](examples/chapter_15/simple_tcp_server_v1.go)**)
都有哪些改进?
-
-(1)服务器地址和端口不再是硬编码,而是通过命令行传入参数并通过`flag`包来读取这些参数。这里使用了`flag.NArg()`检查是否按照期望传入了2个参数:
+* 服务器地址和端口不再是硬编码,而是通过命令行传入参数并通过`flag`包来读取这些参数。这里使用了`flag.NArg()`检查是否按照期望传入了2个参数:
```go
if flag.NArg() != 2{
panic("usage: host port")
@@ -297,11 +296,11 @@ func checkError(error error, info string) {
```go
hostAndPort := fmt.Sprintf("%s:%s", flag.Arg(0), flag.Arg(1))
```
-(2)在`initServer`函数中通过`net.ResolveTCPAddr`指定了服务器地址和端口,这个函数最终返回了一个`*net.TCPListener`
-(3)每一个连接都会以携程的方式运行`connectionHandler`函数。这些开始于当通过`conn.RemoteAddr()`获取到客户端的地址
-(4)它使用`conn.Write`发送改进的go-message给客户端
-(5)它使用一个25字节的缓冲读取客户端发送的数据并一一打印出来。如果读取的过程中出现错误,代码会进入`switch`语句的`default`分支关闭连接。如果是操作系统的`EAGAIN`错误,它会重试。
-(6)所有的错误检查都被重构在独立的函数'checkError'中,用来分发出现的上下文错误。
+* 在`initServer`函数中通过`net.ResolveTCPAddr`指定了服务器地址和端口,这个函数最终返回了一个`*net.TCPListener`
+* 每一个连接都会以携程的方式运行`connectionHandler`函数。这些开始于当通过`conn.RemoteAddr()`获取到客户端的地址
+* 它使用`conn.Write`发送改进的go-message给客户端
+* 它使用一个25字节的缓冲读取客户端发送的数据并一一打印出来。如果读取的过程中出现错误,代码会进入`switch`语句的`default`分支关闭连接。如果是操作系统的`EAGAIN`错误,它会重试。
+* 所有的错误检查都被重构在独立的函数'checkError'中,用来分发出现的上下文错误。
在命令行中输入`simple_tcp_server localhost 50000`来启动服务器程序,然后在独立的命令行窗口启动一些client.go的客户端。当有两个客户端连接的情况下服务器的典型输出如下,这里我们可以看到每个客户端都有自己的地址:
```
@@ -320,6 +319,7 @@ package net
type Error interface{
Timeout() bool // 错误是否超时
Temporary() bool // 是否是临时错误
+}
```
通过类型断言,客户端代码可以用来测试`net.Error`,从而区分哪些临时发生的错误或者必然会出现的错误。举例来说,一个网络爬虫程序在遇到临时发生的错误时可能会休眠或者重试,如果是一个必然发生的错误,则他会放弃继续执行。
```go
From 78b3f5fd9e224fd60b61aa262d63f0cfc8f08412 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Thu, 10 Dec 2015 12:38:59 +0800
Subject: [PATCH 07/12] Update 15.1.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
拼写错误
---
eBook/15.1.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/eBook/15.1.md b/eBook/15.1.md
index e5a9590..ee2ab7a 100644
--- a/eBook/15.1.md
+++ b/eBook/15.1.md
@@ -183,7 +183,7 @@ func main() {
con.Close()
}
```
-**练习 15.1** 编写新版本的客户端和服务器([client1.to](exercises/chapter_15/client1.go) / [server1.go](exercises/chapter_15/server1.go)):
+**练习 15.1** 编写新版本的客户端和服务器([client1.go](exercises/chapter_15/client1.go) / [server1.go](exercises/chapter_15/server1.go)):
* 增加一个检查错误的函数`checkError(error)`;讨论如下方案的利弊:为什么这个重构可能并没有那么理想?看看在示例15.14中它是如何被解决的
* 使客户端可以通过发送一条命令SH来关闭服务器
* 让服务器可以保存已经连接的客户端列表(他们的名字);当客户端发送WHO指令的时候,服务器将显示如下列表:
From d779d1fe2273f30465420f0becd7a87bf8b581e3 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Thu, 10 Dec 2015 17:16:55 +0800
Subject: [PATCH 08/12] Update 15.1.md
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
增加示例15.14的链接
---
eBook/15.1.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/eBook/15.1.md b/eBook/15.1.md
index ee2ab7a..c9354cd 100644
--- a/eBook/15.1.md
+++ b/eBook/15.1.md
@@ -184,7 +184,7 @@ func main() {
}
```
**练习 15.1** 编写新版本的客户端和服务器([client1.go](exercises/chapter_15/client1.go) / [server1.go](exercises/chapter_15/server1.go)):
-* 增加一个检查错误的函数`checkError(error)`;讨论如下方案的利弊:为什么这个重构可能并没有那么理想?看看在示例15.14中它是如何被解决的
+* 增加一个检查错误的函数`checkError(error)`;讨论如下方案的利弊:为什么这个重构可能并没有那么理想?看看在[示例15.14](examples/chapter_15/template_validation.go)中它是如何被解决的
* 使客户端可以通过发送一条命令SH来关闭服务器
* 让服务器可以保存已经连接的客户端列表(他们的名字);当客户端发送WHO指令的时候,服务器将显示如下列表:
From 6a419a7024e213d33d970577f8de0b00781e07f5 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Thu, 10 Dec 2015 23:23:57 +0800
Subject: [PATCH 09/12] Update 15.2.md
complete 15.2
---
eBook/15.2.md | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/eBook/15.2.md b/eBook/15.2.md
index fe801a5..138a790 100644
--- a/eBook/15.2.md
+++ b/eBook/15.2.md
@@ -93,19 +93,15 @@ func (obj *Typ) ServeHTTP(w http.ResponseWriter, req *http.Request) {
练习 15.2:[webhello2.go](exercises/chapter_15/webhello2.go)
->编写一个网页服务器监听端口9999,有如下处理函数:
+编写一个网页服务器监听端口9999,有如下处理函数:
->i)当请求`http://localhost:9999/hello/Name`时,响应:`hello Name`(Name需是一个合法的姓,比如Chris或者Madeleine)
+* 当请求`http://localhost:9999/hello/Name`时,响应:`hello Name`(Name需是一个合法的姓,比如Chris或者Madeleine)
->ii)当请求`http://localhost:9999/shouthello/Name`时,响应:`hello NAME`
+* 当请求`http://localhost:9999/shouthello/Name`时,响应:`hello NAME`
练习 15.3:[hello_server.go](exercises/chapter_15/hello_server.go)
->
-
-
-
-
+创建一个空结构`hello`并使它实现`http.Handler`。运行并测试。
## 链接
From 0a1cd6db8b814b4cae4c1df194259c22d430ea03 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Thu, 10 Dec 2015 23:27:54 +0800
Subject: [PATCH 10/12] =?UTF-8?q?=E5=8A=A0=E5=85=A5=E7=BF=BB=E8=AF=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
加入翻译
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index 9c4dba9..759845c 100644
--- a/README.md
+++ b/README.md
@@ -36,6 +36,7 @@
- [@domainname](https://github.com/domainname)
- [@leisore](https://github.com/leisore)
- [@dake](https://github.com/dake)
+ - [@glight2000](https://github.com/glight2000)
## 授权许可
From a9e4a9c3088e0ad126ba53f0197efb598c1a1979 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Thu, 10 Dec 2015 23:30:21 +0800
Subject: [PATCH 11/12] Update README_gc.md
---
README_gc.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/README_gc.md b/README_gc.md
index d8d56f0..1b35b2a 100644
--- a/README_gc.md
+++ b/README_gc.md
@@ -18,6 +18,7 @@
- [@domainname](https://github.com/domainname)
- [@leisore](https://github.com/leisore)
- [@dake](https://github.com/dake)
+- [@glight2000](https://github.com/glight2000)
## 适用人群
@@ -31,3 +32,5 @@ Golang 编程:245386165
|更新日期 |更新内容
|----------|------------------
|2015-11-25|13.10 性能调试:分析并优化 Go 程序
+|----------|------------------
+|2015-12-10|15.0 15.1 15.2
From eeb2b6ee0a09244848fa948a5a78fe5b4046e0e1 Mon Sep 17 00:00:00 2001
From: glight2000 <173959153@qq.com>
Date: Thu, 10 Dec 2015 23:30:51 +0800
Subject: [PATCH 12/12] Update README_gc.md
---
README_gc.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/README_gc.md b/README_gc.md
index 1b35b2a..3dc7b19 100644
--- a/README_gc.md
+++ b/README_gc.md
@@ -32,5 +32,4 @@ Golang 编程:245386165
|更新日期 |更新内容
|----------|------------------
|2015-11-25|13.10 性能调试:分析并优化 Go 程序
-|----------|------------------
|2015-12-10|15.0 15.1 15.2