HTTP网络编程
# HTTP 协议
HTTP 协议是超文本传输协议,工作在应用层,它是基于 TCP/IP 通信协议来传输数据。
HTTP 协议工作于客户端-服务端架构为上。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。Web 服务器根据接收到的请求后,向客户端发送响应信息。
主要特点有:
- 1、简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有 GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于 HTTP 协议简单,使得 HTTP 服务器的程序规模小,因而通信速度很快。
- 2、灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记。
- 3.无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
- 4.无状态:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
- 5、支持 B/S 及 C/S 模式。
对其实现主要是客户端和服务端得实现,在 Go 语言中使用net/http
包来实现服务端和客户端。
# HTTP 服务端
服务端的处理流程与 Socket 一样:
- 监听
- 等待请求
- 处理请求
# 默认 Server
ListenAndServe 使用指定的监听地址和处理器启动一个 HTTP 服务端。处理器参数通常是 nil,这表示采用包变量 DefaultServeMux 作为处理器。
Handle 和 HandleFunc 函数可以向 DefaultServeMux 添加处理器。
http.Handle("/foo", fooHandler)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080", nil))
1
2
3
4
5
2
3
4
5
示例:
package main
import (
"fmt"
"net/http"
)
func helloWorld(response http.ResponseWriter, request *http.Request) {
fmt.Println("Hello World")
response.Write([]byte("你好"))
}
func main() {
// 接受请求并处理
http.HandleFunc("/", helloWorld)
// 监听
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Println("http server listen failed. err", err)
return
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
然后启动服务,在浏览器访问:
# HTTP 客户端
客户端主要是发一些GET
、POST
请求等,在 Go 语言中使用Get
、Head
、Post
、PostForm
函数来发送 HTTP/HTTPS 请求。
resp, err := http.Get("http://example.com/")
...
resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
...
resp, err := http.PostForm("http://example.com/form",
url.Values{"key": {"Value"}, "id": {"123"}})
1
2
3
4
5
6
2
3
4
5
6
程序在使用完 response 后必须关闭回复的主体。
resp, err := http.Get("http://example.com/")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
// ...
1
2
3
4
5
6
7
2
3
4
5
6
7
# Get 请求
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main(){
resp,err:=http.Get("https://www.baidu.com")
if err != nil {
fmt.Println("请求失败. err:",err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status)
// 读取王内容
content,err:=ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取文件内容失败. err",err)
return
}
fmt.Println(string(content))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 带参数的 Get 请求
用上面的方法也可以带参数,如下:
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func main(){
resp,err:=http.Get("https://baike.baidu.com/item/%E6%96%B0%E5%9E%A3%E7%BB%93%E8%A1%A3/8035884?fr=aladdin")
if err != nil {
fmt.Println("请求失败. err:",err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status)
// 读取王内容
content,err:=ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取文件内容失败. err",err)
return
}
fmt.Println(string(content))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
但是这种很多就不灵活。Go 语言中net/url
包可以用来处理加参数的请求。
如下:
Client 端 :
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
func main() {
// 请求的url
reqURL := "http://127.0.0.1:9000/get"
// 构造请求数据
data := url.Values{}
// 向里面放入值
data.Set("name", "joker")
data.Set("age", "20")
// 解析上面的url
u, err := url.ParseRequestURI(reqURL)
if err != nil {
fmt.Println("url解析失败. err:", err)
return
}
// 对需要传输的数据进行编码
u.RawQuery = data.Encode()
fmt.Println(u.String())
// 发送请求
resp, err := http.Get(u.String())
if err != nil {
fmt.Println("请求失败. err:", err)
return
}
fmt.Println(resp.Status)
defer resp.Body.Close()
// 读取请求到的内容
bn, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取内容失败。err:", err)
return
}
// 输出内容
fmt.Println(string(bn))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
Server 端 :
package main
import (
"fmt"
"net/http"
)
func helloWorld(response http.ResponseWriter, request *http.Request) {
fmt.Println("Hello World")
response.Write([]byte("你好"))
}
func getTest(response http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
fmt.Println(request.URL.Query())
answer := []byte("ok")
response.Write(answer)
}
func main() {
// 接受请求并处理
http.HandleFunc("/", helloWorld)
http.HandleFunc("/get", getTest)
// 监听
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Println("http server listen failed. err", err)
return
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# POST 请求
Client 端 :
package main
import (
"fmt"
"net/http"
"strings"
)
func main() {
// post请求url
postURL := "http://127.0.0.1:9000/post"
// 定义文件类型
contentType := "application/json"
// 定义post的数据
data := `{"name":"joker","age":20}`
// 发送post请求
resp, err := http.Post(postURL, contentType, strings.NewReader(data))
if err != nil {
fmt.Println("post请求失败。err:", err)
return
}
defer resp.Body.Close()
fmt.Println(resp.Status)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Server 端 :
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func helloWorld(response http.ResponseWriter, request *http.Request) {
fmt.Println("Hello World")
response.Write([]byte("你好"))
}
func getTest(response http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
fmt.Println(request.URL.Query())
answer := []byte("ok")
response.Write(answer)
}
func postTest(response http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
// 解析form数据
request.ParseForm()
fmt.Println(request.ParseForm())
fmt.Println(request.PostForm.Get("name"), request.PostForm.Get("age"))
b, err := ioutil.ReadAll(request.Body)
if err != nil {
fmt.Println("数据读取失败。err:", err)
return
}
fmt.Println(string(b))
}
func main() {
// 接受请求并处理
http.HandleFunc("/", helloWorld)
http.HandleFunc("/get", getTest)
http.HandleFunc("/post", postTest)
// 监听
err := http.ListenAndServe(":9000", nil)
if err != nil {
fmt.Println("http server listen failed. err", err)
return
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 自定义 Client
要管理 HTTP 客户端的头域、重定向策略和其他设置,创建一个 Client:
package main
import (
"net/http"
"strings"
"fmt"
"io/ioutil"
"log"
"encoding/json"
)
func main() {
client := &http.Client{}
req, err := http.NewRequest("POST", "http://www.maimaiche.com/loginRegister/login.do",
strings.NewReader("mobile=xxxxxxxxx&isRemberPwd=1"))
if err != nil {
log.Println(err)
return
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
resp, err := client.Do(req)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err)
return
}
fmt.Println(resp.Header.Get("Content-Type")) //application/json;charset=UTF-8
type Result struct {
Msg string
Status string
Obj string
}
result := &Result{}
json.Unmarshal(body, result) //解析json字符串
if result.Status == "1" {
fmt.Println(result.Msg)
} else {
fmt.Println("login error")
}
fmt.Println(result)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 自定义 Transport
要管理代理、TLS 配置、keep-alive、压缩和其他设置,创建一个 Transport:
tr := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool},
DisableCompression: true,
}
client := &http.Client{Transport: tr}
resp, err := client.Get("https://example.com")
1
2
3
4
5
6
2
3
4
5
6
Client 和 Transport 类型都可以安全的被多个 goroutine 同时使用。出于效率考虑,应该一次建立、尽量重用。
上次更新: 2025/07/18, 18:41:03