mirror of
https://github.com/unknwon/the-way-to-go_ZH_CN.git
synced 2025-08-12 05:11:49 +08:00
@@ -5,6 +5,7 @@
|
|||||||
服务器代码,单独的一个文件:
|
服务器代码,单独的一个文件:
|
||||||
|
|
||||||
示例 15.1 [server.go](examples/chapter_15/server.go)
|
示例 15.1 [server.go](examples/chapter_15/server.go)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ func doServerStuff(conn net.Conn) {
|
|||||||
客户端代码写在另外一个文件client.go中:
|
客户端代码写在另外一个文件client.go中:
|
||||||
|
|
||||||
示例 15.2 [client.go](examples/chapter_15/client.go)
|
示例 15.2 [client.go](examples/chapter_15/client.go)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -107,6 +109,7 @@ func main() {
|
|||||||
然后开启2个或者3个独立的控制台窗口,然后分别输入client回车启动客户端程序
|
然后开启2个或者3个独立的控制台窗口,然后分别输入client回车启动客户端程序
|
||||||
|
|
||||||
以下是服务器的输出:
|
以下是服务器的输出:
|
||||||
|
|
||||||
```
|
```
|
||||||
Starting the Server ...
|
Starting the Server ...
|
||||||
Received data: IVO says: Hi Server, what's up ?
|
Received data: IVO says: Hi Server, what's up ?
|
||||||
@@ -114,6 +117,7 @@ Received data: CHRIS says: Are you busy server ?
|
|||||||
Received data: MARC says: Don't forget our appointment tomorrow !
|
Received data: MARC says: Don't forget our appointment tomorrow !
|
||||||
```
|
```
|
||||||
当客户端输入 Q 并结束程序时,服务器会输出以下信息:
|
当客户端输入 Q 并结束程序时,服务器会输出以下信息:
|
||||||
|
|
||||||
```
|
```
|
||||||
Error reading WSARecv tcp 127.0.0.1:50000: The specified network name is no longer available.
|
Error reading WSARecv tcp 127.0.0.1:50000: The specified network name is no longer available.
|
||||||
```
|
```
|
||||||
@@ -122,6 +126,7 @@ Error reading WSARecv tcp 127.0.0.1:50000: The specified network name is no long
|
|||||||
下边这个示例先使用TCP协议连接远程80端口,然后使用UDP协议连接,最后使用TCP协议连接IPv6类型的地址:
|
下边这个示例先使用TCP协议连接远程80端口,然后使用UDP协议连接,最后使用TCP协议连接IPv6类型的地址:
|
||||||
|
|
||||||
示例 15.3 [dial.go](examples/chapter_15/dial.go)
|
示例 15.3 [dial.go](examples/chapter_15/dial.go)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// make a connection with www.example.org:
|
// make a connection with www.example.org:
|
||||||
package main
|
package main
|
||||||
@@ -151,6 +156,7 @@ func checkConnection(conn net.Conn, err error) {
|
|||||||
下边也是一个使用net包从socket中打开,写入,读取数据的例子:
|
下边也是一个使用net包从socket中打开,写入,读取数据的例子:
|
||||||
|
|
||||||
示例 15.4 [socket.go](examples/chapter_15/socket.go)
|
示例 15.4 [socket.go](examples/chapter_15/socket.go)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -183,7 +189,10 @@ func main() {
|
|||||||
con.Close()
|
con.Close()
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**练习 15.1** 编写新版本的客户端和服务器([client1.go](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](examples/chapter_15/template_validation.go)中它是如何被解决的
|
* 增加一个检查错误的函数`checkError(error)`;讨论如下方案的利弊:为什么这个重构可能并没有那么理想?看看在[示例15.14](examples/chapter_15/template_validation.go)中它是如何被解决的
|
||||||
* 使客户端可以通过发送一条命令SH来关闭服务器
|
* 使客户端可以通过发送一条命令SH来关闭服务器
|
||||||
* 让服务器可以保存已经连接的客户端列表(他们的名字);当客户端发送WHO指令的时候,服务器将显示如下列表:
|
* 让服务器可以保存已经连接的客户端列表(他们的名字);当客户端发送WHO指令的时候,服务器将显示如下列表:
|
||||||
@@ -199,6 +208,7 @@ User CHRIS is 1
|
|||||||
下边这个版本的 simple_tcp_server.go 从很多方面优化了第一个tcp服务器的示例 server.go 并且拥有更好的结构,它只用了80行代码!
|
下边这个版本的 simple_tcp_server.go 从很多方面优化了第一个tcp服务器的示例 server.go 并且拥有更好的结构,它只用了80行代码!
|
||||||
|
|
||||||
示例 15.5 [simple_tcp_server.go](examples/chapter_15/simple_tcp_server.go):
|
示例 15.5 [simple_tcp_server.go](examples/chapter_15/simple_tcp_server.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Simple multi-thread/multi-core TCP server.
|
// Simple multi-thread/multi-core TCP server.
|
||||||
package main
|
package main
|
||||||
@@ -286,7 +296,9 @@ func checkError(error error, info string) {
|
|||||||
(**译者注:应该是由于go版本的更新,会提示os.EAGAIN undefined ,修改后的代码:[simple_tcp_server_v1.go](examples/chapter_15/simple_tcp_server_v1.go)**)
|
(**译者注:应该是由于go版本的更新,会提示os.EAGAIN undefined ,修改后的代码:[simple_tcp_server_v1.go](examples/chapter_15/simple_tcp_server_v1.go)**)
|
||||||
|
|
||||||
都有哪些改进?
|
都有哪些改进?
|
||||||
|
|
||||||
* 服务器地址和端口不再是硬编码,而是通过命令行传入参数并通过`flag`包来读取这些参数。这里使用了`flag.NArg()`检查是否按照期望传入了2个参数:
|
* 服务器地址和端口不再是硬编码,而是通过命令行传入参数并通过`flag`包来读取这些参数。这里使用了`flag.NArg()`检查是否按照期望传入了2个参数:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
if flag.NArg() != 2{
|
if flag.NArg() != 2{
|
||||||
panic("usage: host port")
|
panic("usage: host port")
|
||||||
|
@@ -8,6 +8,7 @@ Http是一个比tcp更高级的协议,它描述了客户端浏览器如何与
|
|||||||
`http.URL`描述了web服务器的地址,内含存放了url字符串的`Path`属性;`http.Request`描述了客户端请求,内含一个`URL`属性
|
`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"]的第一个返回参数,就像这样:
|
如果`req`请求是一个POST类型的html表单,“var1”就是html表单中一个输入属性的名称,然后用户输入的值就可以通过GO代码:`req.FormValue("var1")`获取到(请看[章节15.4](15.4.md))。还有一种方法就是先执行`request.ParseForm()`然后再获取`request.Form["var1"]的第一个返回参数,就像这样:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
var1, found := request.Form["var1"]
|
var1, found := request.Form["var1"]
|
||||||
```
|
```
|
||||||
@@ -24,6 +25,7 @@ Http是一个比tcp更高级的协议,它描述了客户端浏览器如何与
|
|||||||
总结:第一个参数是请求的路径,第二个参数是处理这个路径请求的函数的引用。
|
总结:第一个参数是请求的路径,第二个参数是处理这个路径请求的函数的引用。
|
||||||
|
|
||||||
示例 15.6 [hello_world_webserver.go](examples/chapter_15/hello_world_webserver.go):
|
示例 15.6 [hello_world_webserver.go](examples/chapter_15/hello_world_webserver.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
返回状态码会被打印出来。
|
返回状态码会被打印出来。
|
||||||
|
|
||||||
示例 15.7 [poll_url.go](examples/chapter_15/poll_url.go):
|
示例 15.7 [poll_url.go](examples/chapter_15/poll_url.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -32,12 +33,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
输出为:
|
输出为:
|
||||||
|
|
||||||
```
|
```
|
||||||
http://www.google.com/ : 302 Found
|
http://www.google.com/ : 302 Found
|
||||||
http://golang.org/ : 200 OK
|
http://golang.org/ : 200 OK
|
||||||
http://blog.golang.org/ : 200 OK
|
http://blog.golang.org/ : 200 OK
|
||||||
```
|
```
|
||||||
***译者注*** 由于国内的网络环境现状,很有可能见到如下超时错误提示:
|
***译者注*** 由于国内的网络环境现状,很有可能见到如下超时错误提示:
|
||||||
|
|
||||||
```
|
```
|
||||||
Error: http://www.google.com/ Head http://www.google.com/: dial tcp 216.58.221.100:80: connectex: A connection attempt failed because the connected pa
|
Error: http://www.google.com/ Head http://www.google.com/: dial tcp 216.58.221.100:80: connectex: A connection attempt failed because the connected pa
|
||||||
rty did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
|
rty did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
|
||||||
@@ -45,6 +48,7 @@ rty did not properly respond after a period of time, or established connection f
|
|||||||
在下边的程序中我们使用`http.Get()`获取网页内容; `Get`的返回值`res`中的`Body`属性包含了网页内容,然后我们用`ioutil.ReadAll`把它读出来:
|
在下边的程序中我们使用`http.Get()`获取网页内容; `Get`的返回值`res`中的`Body`属性包含了网页内容,然后我们用`ioutil.ReadAll`把它读出来:
|
||||||
|
|
||||||
示例 15.8 [http_fetch.go](examples/chapter_15/http_fetch.go):
|
示例 15.8 [http_fetch.go](examples/chapter_15/http_fetch.go):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -79,6 +83,7 @@ func checkError(err error) {
|
|||||||
在下边的程序中,我们获取一个twitter用户的状态,通过`xml`包将这个状态解析成为一个结构:
|
在下边的程序中,我们获取一个twitter用户的状态,通过`xml`包将这个状态解析成为一个结构:
|
||||||
|
|
||||||
示例 15.9 [twitter_status.go](examples/chapter_15/twitter_status.go)
|
示例 15.9 [twitter_status.go](examples/chapter_15/twitter_status.go)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -110,12 +115,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
输出:
|
输出:
|
||||||
|
|
||||||
```
|
```
|
||||||
status: Robot cars invade California, on orders from Google: Google has been testing self-driving cars ... http://bit.ly/cbtpUN http://retwt.me/97p<exit code="0" msg="process exited normally"/>
|
status: Robot cars invade California, on orders from Google: Google has been testing self-driving cars ... http://bit.ly/cbtpUN http://retwt.me/97p<exit code="0" msg="process exited normally"/>
|
||||||
```
|
```
|
||||||
**译者注** 和上边的示例相似,你可能无法获取到xml数据,另外由于go版本的更新,`xml.Unmarshal`函数的第一个参数需是[]byte类型,而无法传入`Body`。
|
**译者注** 和上边的示例相似,你可能无法获取到xml数据,另外由于go版本的更新,`xml.Unmarshal`函数的第一个参数需是[]byte类型,而无法传入`Body`。
|
||||||
|
|
||||||
我们会在[章节15.4](15.4.md)中用到`http`包中的其他重要的函数:
|
我们会在[章节15.4](15.4.md)中用到`http`包中的其他重要的函数:
|
||||||
|
|
||||||
* `http.Redirect(w ResponseWriter, r *Request, url string, code int)`:这个函数会让浏览器重定向到url(是请求的url的相对路径)以及状态码。
|
* `http.Redirect(w ResponseWriter, r *Request, url string, code int)`:这个函数会让浏览器重定向到url(是请求的url的相对路径)以及状态码。
|
||||||
* `http.NotFound(w ResponseWriter, r *Request)`:这个函数将返回网页没有找到,HTTP 404错误。
|
* `http.NotFound(w ResponseWriter, r *Request)`:这个函数将返回网页没有找到,HTTP 404错误。
|
||||||
* `http.Error(w ResponseWriter, error string, code int)`:这个函数返回特定的错误信息和HTTP代码。
|
* `http.Error(w ResponseWriter, error string, code int)`:这个函数返回特定的错误信息和HTTP代码。
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
下边的程序在端口8088上启动了一个网页服务器;`SimpleServer`会处理`/test1`url使它在浏览器输出`hello world`。`FormServer`会处理`/test2`url:如果url最初由浏览器请求,那么它就是一个`GET`请求,并且返回一个`form`常量,包含了简单的`input`表单,这个表单里有一个文本框和一个提交按钮。当在文本框输入一些东西并点击提交按钮的时候,会发起一个`POST`请求。`FormServer`中的代码用到了`switch`来区分两种情况。在`POST`情况下,使用`request.FormValue("inp")`通过文本框的`name`属性`inp`来获取内容,并写回浏览器页面。在控制台启动程序并在浏览器中打开url`http://localhost:8088/text2`来测试这个程序:
|
下边的程序在端口8088上启动了一个网页服务器;`SimpleServer`会处理`/test1`url使它在浏览器输出`hello world`。`FormServer`会处理`/test2`url:如果url最初由浏览器请求,那么它就是一个`GET`请求,并且返回一个`form`常量,包含了简单的`input`表单,这个表单里有一个文本框和一个提交按钮。当在文本框输入一些东西并点击提交按钮的时候,会发起一个`POST`请求。`FormServer`中的代码用到了`switch`来区分两种情况。在`POST`情况下,使用`request.FormValue("inp")`通过文本框的`name`属性`inp`来获取内容,并写回浏览器页面。在控制台启动程序并在浏览器中打开url`http://localhost:8088/text2`来测试这个程序:
|
||||||
|
|
||||||
示例 15.10 [simple_webserver.go](examples/chapter_15/simple_webserver.go)
|
示例 15.10 [simple_webserver.go](examples/chapter_15/simple_webserver.go)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -59,3 +60,7 @@ func main() {
|
|||||||
编写一个网页程序,可以让用户输入一连串的数字,然后将它们打印出来,计算出这些数字的均值和中值,就像下边这张截图一样:
|
编写一个网页程序,可以让用户输入一连串的数字,然后将它们打印出来,计算出这些数字的均值和中值,就像下边这张截图一样:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
- [目录](directory.md)
|
||||||
|
- 上一章:[访问并读取页面](15.4.md)
|
||||||
|
- 下一节:[常见的陷阱与错误](16.0.md)
|
||||||
|
@@ -10,9 +10,10 @@ if err != nil {
|
|||||||
```
|
```
|
||||||
|
|
||||||
或者:
|
或者:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(“ERROR occurred: “ + err.Error())
|
panic(“ERROR occurred: “ + err.Error())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
# 18.4 结构体
|
# 18.4 结构体
|
||||||
|
|
||||||
创建:
|
创建:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type struct1 struct {
|
type struct1 struct {
|
||||||
field1 type1
|
field1 type1
|
||||||
@@ -11,6 +12,7 @@ ms := new(struct1)
|
|||||||
```
|
```
|
||||||
|
|
||||||
初始化:
|
初始化:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
ms := &struct1{10, 15.5, "Chris"}
|
ms := &struct1{10, 15.5, "Chris"}
|
||||||
```
|
```
|
||||||
|
Reference in New Issue
Block a user