网络协议介绍

互联网的核心是一系列的协议,总称为互联网协议,这些协议规定了电脑如何连接和组成网络。

互联网分层模型

互联网的逻辑实现被分为好几层,每一层都有自己的功能。

ISO七层模型

Socket编程

Socket是BSD UNIX的进程通信机制,也称作套接字,用于描述IP地址和端口,是一个通信链的句柄。Socket可以理解为TCP/IP网络的API,它定义了许多函数或例程,可以使用它他们开发TCP/IP网络上的应用程序。

socket图解

Socket是应用层与TCP/IP协议族通信的中间软件抽象层。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket后面,对用户来说只需要调用Socket规定的相关函数,让Socket去组织符合指定的协议数据然后进行通信。

socket

Go语言实现TCP通信

TCP协议

TCP/IP(Transmission Controller Protocol/Internet Protocol)即传输控制协议/网间协议,是一种面向连接(连接导向)的,可靠的,基于字节流的传输层(Transport layer)通信协议,因为是面向连接的协议,数据像水流一样传输,会存在黏包问题。

TCP服务端

一个TCP服务端可以同时连接很多个客户端。由于Go语言中创建多个goroutine实现并发非常方便和高效,我们可以每次建立链接就创建一个goroutine去处理。

TCP服务端程序的处理流程:

  • 1、监听端口。
  • 2、接收客户端请求建立链接。
  • 3、创建goroutine处理链接。
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
//处理函数
func process(conn net.Conn) {
//3.与客户端通信
for {
var msg [128]byte
n, err := conn.Read(msg[:])
if err != nil {
fmt.Println("read from conn failed, err", err)
return
}
fmt.Println(string(msg[:n]))
}
}

//tcp server端
func main() {
//1.本地端口服务启动
listen, err := net.Listen("tcp", "127.0.0.1:20000")
if err != nil {
fmt.Println("start server on failed", err)
return
}
for {
//2.等待别人建立连接
conn, err := listen.Accept()
if err != nil {
fmt.Println("accept failed, err", err)
return
}
go process(conn) //启动一个goroutine处理连接
}
}

TCP客户端

TCP客户端程序的处理流程:

  • 1、建立与服务端的链接。
  • 2、进行数据收发。
  • 3、关闭链接。
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
func main() {
//osArgs()
Scan()
}

func Scan() {
//1.与server端建立连接
conn, err := net.Dial("tcp", "127.0.0.1:20000")
if err != nil {
fmt.Println("accept failed, err", err)
return
}
for {
fmt.Println("请输入消息:")
reader := bufio.NewReader(os.Stdin)
msg, err := reader.ReadString('\n')
msg = strings.TrimSpace(msg)
if err != nil {
fmt.Println("read msg failed, err", err)
continue
}
//2.发送数据
conn.Write([]byte(msg))
}
conn.Close()
}

func osArgs() {
//1.与server端建立连接
conn, err := net.Dial("tcp", "127.0.0.1:20000")
if err != nil {
fmt.Println("accept failed, err", err)
return
}
var msg string
if len(os.Args) < 2 {
msg = "hello world"
} else {
msg = os.Args[1]
}
//2.发送数据
conn.Write([]byte(msg))
conn.Close()
}

TCP黏包

TCP数据传递模式是流模式,在保持长连接时可以进行多次的收发。

“黏包”可发生在发送端,也可发生在接收端。

  • 1、由Nagle算法造成的发送端黏包:Nagle算法是一种改善网络传输效率的算法。简单来说就是当我么们提交一段数据给TCP发送时,TCP并不会立刻发送此数据,而是等待一小段时间看看在等待期间是否还要发送数据,若有则会一次把这两段数据发送出去。
  • 接收端接受不及时造成的接收端黏包:TCP会把接收到的数据存在自己的缓冲区中,然后通知应用层接收数据。当应用层由于某些原因不能及时把TCP数据取出来,就造成了TCP缓冲区中存放了几段数据。

服务端:

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
//处理函数
func process(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
var msg [1024]byte
//3.与客户端通信
for {
n, err := reader.Read(msg[:])
if err == io.EOF {
break
}
if err != nil {
fmt.Println("read from vlient failed err: ", err)
}
fmt.Println("Receive data from client:", string(msg[:n]))
}
}

//tcp server端
func main() {
//1.本地端口服务启动
listen, err := net.Listen("tcp", "127.0.0.1:2000")
if err != nil {
fmt.Println("Listen Failed", err)
return
}
defer listen.Close()
//启动一个goroutine处理连接
for {
//2.等待别人建立连接
conn, err := listen.Accept()
if err != nil {
fmt.Println("Accept failed", err)
continue
}
go process(conn)
}
}

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
//1.与server端建立连接
conn, err := net.Dial("tcp", "127.0.0.1:2000")
if err != nil {
fmt.Println("get connect err : ", err)
return
}
defer conn.Close()
for i := 0; i < 20; i++ {
msg := `Hello, Hello. How are you?`
conn.Write([]byte(msg))
}
}

解决办法

出现黏包的主要原因在于接收方不确定将要传输的数据包大小,因此我们可以对数据包进行封包和拆包操作。

封包:封包就是给数据加上包头,这样一来数据包就分为包头和包体两部分内容(过滤非法包时封包会加入”句尾“内容)。包头部分的长度是固定的,并且它储存了包体的长度,根据包头固定长度及包头中含有包体长度的就能正确的拆分出一个完整的数据包。

可以自定义一个协议,比如数据包的前四个字节为包头,里面存放的是发送数据的长度。

自定义编码/解码方式:

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
//EnCode 将消息编码
func EnCode(message string) ([]byte, error) {
//读取消息的长度,转换成int32类型(占4字节)
var length = int32(len(message))
var pkg = new(bytes.Buffer)
//写入消息头
err := binary.Write(pkg, binary.LittleEndian, length)
if err != nil {
return nil, err
}
//写入消息体
err = binary.Write(pkg, binary.LittleEndian, []byte(message))
if err != nil {
return nil, err
}
return pkg.Bytes(), nil
}

//DeCode 解码消息
func DeCode(reader *bufio.Reader) (string, error) {
//读取消息的长度
//读取前四个字节的数据
lengthByte, err := reader.Peek(4)
lengthBuff := bytes.NewBuffer(lengthByte)
var length int32
err = binary.Read(lengthBuff, binary.LittleEndian, &length)
if err != nil {
return "", err
}
//Buffered返回缓冲区现有的可读取的字节数。
if int32(reader.Buffered()) < length+4 {
return "", err
}
//读取真正的消息数据
pack := make([]byte, int(length+4))
_, err = reader.Read(pack)
if err != nil {
return "", err
}
return string(pack[4:]), nil
}

服务端:

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
//处理函数
func process(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
//3.与客户端通信
for {
msg, err := protocol.DeCode(reader)
if err == io.EOF {
break
}
if err != nil {
fmt.Println("read from vlient failed err: ", err)
}
fmt.Println("Receive data from client:", msg)
}
}

//tcp server端
func main() {
//1.本地端口服务启动
listen, err := net.Listen("tcp", "127.0.0.1:2000")
if err != nil {
fmt.Println("Listen Failed", err)
return
}
defer listen.Close()
//启动一个goroutine处理连接
for {
//2.等待别人建立连接
conn, err := listen.Accept()
if err != nil {
fmt.Println("Accept failed", err)
continue
}
go process(conn)
}
}

客户端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
//1.与server端建立连接
conn, err := net.Dial("tcp", "127.0.0.1:2000")
if err != nil {
fmt.Println("get connect err : ", err)
return
}
defer conn.Close()
for i := 0; i < 20; i++ {
msg := `Hello, Hello. How are you?`
msgByte, err := protocol.EnCode(msg)
if err != nil {
fmt.Println("EnCode err: ", err)
break
}
conn.Write(msgByte)
}
}

UDP通信

UDP协议

UDP协议(User Datagram Protocol)中文名称是用户数据报协议,是OSI(Open System Interconnection,开放式系统互联)模型参考中一种无连接的传输层协议,不需要建立连接就能直接进行数据的发送和接收,属于不可靠,没有时序的通信,但是UDP协议的实时性比较好,通常用于视频直播相关领域。

UDP服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func main() {
conn, err := net.ListenUDP("udp", &net.UDPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: 30000,
})
if err != nil {
fmt.Println("ListenUDP failed, err: ", err)
return
}
defer conn.Close()
for {
var data [1024]byte
n, addr, err := conn.ReadFromUDP(data[:])
if err != nil {
fmt.Println("read udp failed, err: ", err)
continue
}
fmt.Printf("data: %v addr: %v count: %v", string(data[:n]), addr, n)
if err != nil {
fmt.Println("write to udp failed, err: ", err)
continue
}
}
}

客户端

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
func main() {
conn, err := net.DialUDP("udp", nil, &net.UDPAddr{
IP: net.IPv4(0, 0, 0, 0),
Port: 30000,
})
if err != nil {
fmt.Println("connection server failed, err: ", err)
return
}
defer conn.Close()
reader := bufio.NewReader(os.Stdin)
for {
msg, err := reader.ReadString('\n')
if err != nil {
fmt.Println("read stdin failed, err: ", err)
return
}
msg = strings.TrimSpace(msg)
_, err = conn.Write([]byte(msg))
if err != nil {
fmt.Println("Send msg failed, err: ", err)
return
}
data := make([]byte, 4096)
n, addr, err := conn.ReadFromUDP(data)
if err != nil {
fmt.Println("read udp failed, err: ", err)
return
}
fmt.Printf("data: %v addr: %v count: %v", string(data[:n]), addr, n)
}
}

HTTP协议

超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准。设计HTTP最初的目的是为了提供一种发布和接收HTML的方法。

HTTP客户端

基本的HTTP/HTTPS请求

Get、Head、Post和PostForm函数发出HTTP/HTTPS请求。

1
2
3
http.Get("http://example.com/")
http.Post("http://example.com/", "image/jpeg", &buf)
http.PostForm("http://example.com/" ,url.Values{"key": {"value"}, "id" : {"123"}})

程序使用完response后必须关闭回复的主体

1
2
3
4
5
response, err := http.Get("http://127.0.0.1:20000/index")
if err != nil {
fmt.Println("get url failed, err:",err)
}
defer response.Body.Close()

GET请求示例

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
response, err := http.Get("http://127.0.0.1:20000/index")
if err != nil {
fmt.Println("get url failed, err:",err)
}
defer response.Body.Close()
msg, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Println("read response.Body failed, err", err)
}
fmt.Println(string(msg))
}

带参数的GET请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func getForValues() {
apiUrl := "http://127.0.0.1:20000/get"
data := url.Values{}
data.Set("name", "张三")
data.Set("age", "18")
uri, err := url.ParseRequestURI(apiUrl)
if err != nil {
fmt.Printf("pase requestUrl dailed, err: %v", err)
}
uri.RawQuery = data.Encode()
fmt.Println(uri.String())
response, err := http.Get(uri.String())
if err != nil {
fmt.Printf("get url failed, err: %v", err)
}
defer response.Body.Close()
msg, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Printf("read response.Body failed, err: %v", err)
}
fmt.Println(string(msg))
}

Server端

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
http.HandleFunc("/get", get)
http.ListenAndServe("127.0.0.1:20000", nil)
}

func get(writer http.ResponseWriter, request *http.Request) {
defer request.Body.Close()
query := request.URL.Query()
fmt.Println(query.Get("name"))
fmt.Println(query.Get("age"))
answer := `{"status": "OK"}`
writer.Write([]byte(answer))
}

POST请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
http.HandleFunc("/post", postHandler)
http.ListenAndServe("127.0.0.1:2000", nil)
}

func postHandler(writer http.ResponseWriter,request *http.Request){
defer request.Body.Close()
//1.请求类型是application/x-www-form-urlEnCode时解析form数据
request.ParseForm()
//打印form数据
fmt.Println(request.PostForm)
fmt.Println(request.PostForm.Get("naem"), request.PostForm.Get("age"))
//2.请求类型时application/json时从request.Body读取数据
bytes, err := ioutil.ReadAll(request.Body)
if err != nil {
fmt.Printf("read request.Body failed, err: %v", err)
return
}
fmt.Println(string(bytes))
answer := `"status" : "OK"`
writer.Write([]byte(answer))
}

POST请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func post() {
url := "http://127.0.0.1:2000/post"
//表单数据
//contentType := "application/x-www-form-urlencode"
//data := `name=夏普王子&age=18`
//JSON数据
contentType := "application/json"
data := `{"name" : "小王子", "age" : "20"}`
response, err := http.Post(url, contentType, strings.NewReader(data))
if err != nil {
fmt.Printf("post failed, err: %v\n", err)
return
}
defer response.Body.Close()
bytes, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Printf("read request.Body failed, err: %v", err)
return
}
fmt.Println(string(bytes))
}

自定义Client

要管理HTTP请求的客户端的头域,重定向策略和其他设置,创建一个Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
client := &http.Client{
CheckRedirect: redirectPolicyFunc,
}
request, _ := http.NewRequest("GET", "http://127.0.0.1:2000", nil)
request.Header.Add("If-None-Match", `W/wyzzy`)
response, err := client.Do(request)
if err != nil {
fmt.Println("GET failed, err:", err)
}
defer response.Body.Close()
bytes, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Printf("read request.Body failed, err: %v", err)
return
}
fmt.Println(string(bytes))
}

自定义Transport

要管理代理、TLS配置、keep-alive、压缩和其他设置,创建一个Transport

1
2
3
4
5
6
7
8
9
func Transport(){
var pool *x509.CertPool
tr := &http.Transport{
TLSClientConfig: &tls.Config{RootCAs: pool},
DisableCompression: true,
}
client := &http.Client{Transport: tr}
client.Get("http://127.0.0.1:2000/get")
}

HTTP服务端

默认的Server

ListenAndServer使用指定的监听地址和处理器启动一个HTTP服务端。处理器参数通常是nil,这表示采用包变量DefaultServeMux作为处理器。

Handle和HandleFunc函数可以向DefaultServeMux添加处理器。

1
2
3
4
5
http.Handle("/foo", fooHandle)
http.HandleFunc("/foo", func(writer http.ResponseWriter, request *http.Request) {
fmt.Println(writer, "Hello ,%q", html.EscapeString(request.URL.Path))
})
log.Fatal(http.ListenAndServe(":8080", nil))

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
http.HandleFunc("/post", postHandler)
http.ListenAndServe("127.0.0.1:2000", nil)
}

func postHandler(writer http.ResponseWriter,request *http.Request){
defer request.Body.Close()
//1.请求类型是application/x-www-form-urlEnCode时解析form数据
request.ParseForm()
//打印form数据
fmt.Println(request.PostForm)
fmt.Println(request.PostForm.Get("naem"), request.PostForm.Get("age"))
//2.请求类型时application/json时从request.Body读取数据
bytes, err := ioutil.ReadAll(request.Body)
if err != nil {
fmt.Printf("read request.Body failed, err: %v", err)
return
}
fmt.Println(string(bytes))
answer := `"status" : "OK"`
writer.Write([]byte(answer))
}

自定义Server

1
2
3
4
5
6
7
8
9
var myHandler http.Handler
s := &http.Server{
Addr: ":8080",
Handler: myHandler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())