乔克视界 乔克视界
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档

乔克

云原生爱好者
首页
  • 运维
  • 开发
  • 监控
  • 安全
  • 随笔
  • Docker
  • Golang
  • Python
  • AIOps
  • 心情杂货
  • 读书笔记
  • 面试
  • 实用技巧
  • 博客搭建
友链
关于
收藏
  • 分类
  • 标签
  • 归档
  • Docker

  • Golang

    • Golang基础知识

      • 开发环境搭建
      • 常量与变量
      • 基本数据类型
      • 复合数据类型
      • 流程控制
      • 运算符
      • 位运算符详解
      • 指针
      • map
      • 函数
      • defer
      • 类型别名与自定义类型
      • 结构体
      • 接口
      • 包
      • 文件操作
      • 反射
      • 并发
      • socket网络编程
      • HTTP网络编程
        • 默认 Server
        • Get 请求
        • 带参数的 Get 请求
        • POST 请求
        • 自定义 Client
        • 自定义 Transport
      • 单元测试
      • 基准测试
      • 并发测试
      • 示例函数
      • 性能优化
      • go module
      • 在Go中使用Makefile
      • 部署Go项目
    • Golang进阶知识

    • Golang常用包

  • AIOps

  • 专栏
  • Golang
  • Golang基础知识
乔克
2025-07-13
目录

HTTP网络编程

# HTTP 协议

HTTP 协议是超文本传输协议,工作在应用层,它是基于 TCP/IP 通信协议来传输数据。

HTTP 协议工作于客户端-服务端架构为上。浏览器作为 HTTP 客户端通过 URL 向 HTTP 服务端即 WEB 服务器发送所有请求。Web 服务器根据接收到的请求后,向客户端发送响应信息。

5c07e82377357451bd28881c0461bec6 MD5

主要特点有:

  • 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

示例:

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

然后启动服务,在浏览器访问:

81ce07b503a76e52775ee4b77decfdd9 MD5

# 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

程序在使用完 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

# 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

# 带参数的 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

但是这种很多就不灵活。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

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

# 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

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

# 自定义 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

# 自定义 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

Client 和 Transport 类型都可以安全的被多个 goroutine 同时使用。出于效率考虑,应该一次建立、尽量重用。

上次更新: 2025/07/18, 18:41:03
socket网络编程
单元测试

← socket网络编程 单元测试→

最近更新
01
2025年,SRE在企业中可以做哪些事
07-18
02
SRE 如何提升自己在团队中的影响力
07-18
03
使用Go开发MCP服务
07-18
更多文章>
Theme by Vdoing | Copyright © 2019-2025 乔克 | MIT License | 渝ICP备20002153号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式