网络协议介绍 互联网的核心是一系列的协议,总称为互联网协议,这些协议规定了电脑如何连接和组成网络。
互联网分层模型 互联网的逻辑实现被分为好几层,每一层都有自己的功能。
Socket编程 Socket是BSD UNIX的进程通信机制,也称作套接字,用于描述IP地址和端口,是一个通信链的句柄。Socket可以理解为TCP/IP网络的API,它定义了许多函数或例程,可以使用它他们开发TCP/IP网络上的应用程序。
socket图解 Socket是应用层与TCP/IP协议族通信的中间软件抽象层。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在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) { 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])) } } func main () { listen, err := net.Listen("tcp" , "127.0.0.1:20000" ) if err != nil { fmt.Println("start server on failed" , err) return } for { conn, err := listen.Accept() if err != nil { fmt.Println("accept failed, err" , err) return } go process(conn) } }
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 () { Scan() } func Scan () { 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 } conn.Write([]byte (msg)) } conn.Close() } func osArgs () { 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 ] } 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 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])) } } func main () { listen, err := net.Listen("tcp" , "127.0.0.1:2000" ) if err != nil { fmt.Println("Listen Failed" , err) return } defer listen.Close() for { 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 () { 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 func EnCode (message string ) ([]byte , error) { 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 } 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 } 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) 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) } } func main () { listen, err := net.Listen("tcp" , "127.0.0.1:2000" ) if err != nil { fmt.Println("Listen Failed" , err) return } defer listen.Close() for { 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 () { 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() request.ParseForm() fmt.Println(request.PostForm) fmt.Println(request.PostForm.Get("naem" ), request.PostForm.Get("age" )) 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/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() request.ParseForm() fmt.Println(request.PostForm) fmt.Println(request.PostForm.Get("naem" ), request.PostForm.Get("age" )) 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.Handlers := &http.Server{ Addr: ":8080" , Handler: myHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20 , } log.Fatal(s.ListenAndServe())