Go 语言在 net 这个强大 package 的帮助下提供了一系列的 package 来做这件事情,使用这些包可以更简单地用网络收发信息,还可以建立更底层的网络连接,编写服务器程序。
一、获取URL
来源于 Linux 的 curl 命令的灵感,通过下面程序 urlfetch 将会获取对应的 url, 并且将其原文本打印出来
相关源代码如下:
//Fetch prints the content found at a url
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
for _, url := range os.Args[1:] {
resp, err := http.Get(url)
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: %v\n", err)
os.Exit(1)
}
b, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
fmt.Fprintf(os.Stderr, "fetch: reading %s: %v\n", url, err)
os.Exit(1)
}
fmt.Printf("%s", b)
}
}
运行路径:
$GOPATH/src/gopl/ch1/urlfetch
运行命令:
$ go run urlfetch.go http://www.baidu.com
运行结果:
250 运行后的结果将会是一大堆,HTML 的源代码
二、 程序分析
1. http包
net/http 包, http.Get 函数是创建 HTTP 请求的函数,若获取过程没有出错,那么会在 resp 这个结构体中得到访问的请求结果。resp 的 Body字段 包括一个可读的 服务器响应流 。 ioutil.ReadAll 函数从 response 中读取到全部内容;将其结果保存在变量 b 中。 resp.Body.Close 关闭resp的 Body流 ,防止资源泄露,Printf函数会将结果b写出到标准输出流中。
-
http.Get 函数:
返回了一个Response 的一个结构体指针func (c *Client) Get(url string) (resp *Response, err error)
-
关于 type Response
代表了一个 http 请求的回复,其中结构体成员 Body 是 io.ReadCloser 类型,而 Client类型 和 Transport类型 会保证 Body字段 总是 非nil的,即使回复没有主体或主体长度为0。type Response struct { Status string // 例如"200 OK" StatusCode int // 例如200 Proto string // 例如"HTTP/1.0" ProtoMajor int // 例如1 ProtoMinor int // 例如0 // Header保管头域的键值对。 // 如果回复中有多个头的键相同,Header中保存为该键对应用逗号分隔串联起来的这些头的值 // (参见RFC 2616 Section 4.2) // 被本结构体中的其他字段复制保管的头(如ContentLength)会从Header中删掉。 // // Header中的键都是规范化的,参见CanonicalHeaderKey函数 Header Header // Body代表回复的主体。 // Client类型和Transport类型会保证Body字段总是非nil的,即使回复没有主体或主体长度为0。 // 关闭主体是调用者的责任。 // 如果服务端采用"chunked"传输编码发送的回复,Body字段会自动进行解码。 Body io.ReadCloser // ContentLength记录相关内容的长度。 // 其值为-1表示长度未知(采用chunked传输编码) // 除非对应的Request.Method是"HEAD",其值>=0表示可以从Body读取的字节数 ContentLength int64 // TransferEncoding按从最外到最里的顺序列出传输编码,空切片表示"identity"编码。 TransferEncoding []string // Close记录头域是否指定应在读取完主体后关闭连接。(即Connection头) // 该值是给客户端的建议,Response.Write方法的ReadResponse函数都不会关闭连接。 Close bool // Trailer字段保存和头域相同格式的trailer键值对,和Header字段相同类型 Trailer Header // Request是用来获取此回复的请求 // Request的Body字段是nil(因为已经被用掉了) // 这个字段是被Client类型发出请求并获得回复后填充的 Request *Request // TLS包含接收到该回复的TLS连接的信息。 对未加密的回复,本字段为nil。 // 返回的指针是被(同一TLS连接接收到的)回复共享的,不应被修改。 TLS *tls.ConnectionState }
-
io.ReadCloser
该类型是一个接口, 聚合了基本的读取和关闭操作type ReadCloser interface { Reader Closer }
2. io/ioutil
- ReadAll 从 r 读取数据直到 EOF 或遇到 error ,返回读取的数据和遇到的错误。成功的调用返回的 error 为 nil 而 非EOF 。因为本函数定义为读取 r 直到 EOF ,它不会将读取返回的EOF视为应报告的错误。.
func ReadAll(r io.Reader) ([]byte, error)
- 然而在程序中传入的是 io.ReadCloser 接口类型,而 io.ReadCloser 是对 io.Reader 和 io.Closer的进一步封装, 有点类似于C++中 io.ReadCloser 继承于 io.Reader 。
2. 更多参考文档:
https://golang.org/pkg/io/#ReadCloser
总结
- 熟悉 http 包
- io.ReadCloser 与 io.Reader 之间的关系