• Go 语言高级网络编程


    深入探讨 Go 语言的网络编程

    img

    简介

    Go(Golang)中的网络编程具有易用性、强大性和乐趣。本指南深入探讨了网络编程的复杂性,涵盖了协议、TCP/UDP 套接字、并发等方面的内容,并附有详细的注释。

    关键概念

    1. 网络协议

    • TCP(传输控制协议):确保可靠的数据传输。
    • UDP(用户数据报协议):更快,但不保证数据传递。

    2. 套接字

    • TCP 套接字:用于面向连接的通信。
    • UDP 套接字:用于无连接通信。

    3. 并发

    • Goroutines(协程):允许在代码中实现并行处理。
    • Channels(通道):用于协程之间的通信。

    示例

    示例 1:TCP 服务器和客户端

    TCP 服务器和客户端示例演示了TCP通信的基础。

    服务器

    package main
    
    import (
     "net"
     "fmt"
    )
    
    func main() {
     // Listen on TCP port 8080 on all available unicast and
     // any unicast IP addresses.
     listen, err := net.Listen("tcp", ":8080")
     if err != nil {
      fmt.Println(err)
      return
     }
     defer listen.Close()
    
     // Infinite loop to handle incoming connections
     for {
      conn, err := listen.Accept()
      if err != nil {
       fmt.Println(err)
       continue
      }
      // Launch a new goroutine to handle the connection
      go handleConnection(conn)
     }
    }
    
    func handleConnection(conn net.Conn) {
     defer conn.Close()
     buffer := make([]byte, 1024)
     // Read the incoming connection into the buffer.
     _, err := conn.Read(buffer)
     if err != nil {
      fmt.Println(err)
      return
     }
     // Send a response back to the client.
     conn.Write([]byte("Received: " + string(buffer)))
    }
    
    • 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

    客户端

    package main
    
    import (
     "net"
     "fmt"
    )
    
    func main() {
     // Connect to the server at localhost on port 8080.
     conn, err := net.Dial("tcp", "localhost:8080")
     if err != nil {
      fmt.Println(err)
      return
     }
     defer conn.Close()
    
     // Send a message to the server.
     conn.Write([]byte("Hello, server!"))
     buffer := make([]byte, 1024)
     // Read the response from the server.
     conn.Read(buffer)
     fmt.Println(string(buffer))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    服务器在端口8080上等待连接,读取传入的消息并发送响应。客户端连接到服务器,发送消息并打印服务器的响应。

    示例 2:UDP 服务器和客户端

    与TCP不同,UDP是无连接的。以下是UDP服务器和客户端的实现。

    服务器

    package main
    
    import (
     "net"
     "fmt"
    )
    
    func main() {
     // Listen for incoming UDP packets on port 8080.
     conn, err := net.ListenPacket("udp", ":8080")
     if err != nil {
      fmt.Println(err)
      return
     }
     defer conn.Close()
    
     buffer := make([]byte, 1024)
     // Read the incoming packet data into the buffer.
     n, addr, err := conn.ReadFrom(buffer)
     if err != nil {
      fmt.Println(err)
      return
     }
     fmt.Println("Received: ", string(buffer[:n]))
     // Write a response to the client's address.
     conn.WriteTo([]byte("Message received!"), addr)
    }
    
    • 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

    客户端

    package main
    
    import (
     "net"
     "fmt"
    )
    
    func main() {
     // Resolve the server's address.
     addr, err := net.ResolveUDPAddr("udp", "localhost:8080")
     if err != nil {
      fmt.Println(err)
      return
     }
    
     // Dial a connection to the resolved address.
     conn, err := net.DialUDP("udp", nil, addr)
     if err != nil {
      fmt.Println(err)
      return
     }
     defer conn.Close()
    
     // Write a message to the server.
     conn.Write([]byte("Hello, server!"))
     buffer := make([]byte, 1024)
     // Read the response from the server.
     conn.Read(buffer)
     fmt.Println(string(buffer))
    }
    
    • 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

    服务器从任何客户端读取消息并发送响应。客户端发送消息并等待响应。

    示例 3:并发 TCP 服务器

    并发允许同时处理多个客户端。

    package main
    
    import (
     "net"
     "fmt"
    )
    
    func main() {
     // Listen on TCP port 8080.
     listener, err := net.Listen("tcp", ":8080")
     if err != nil {
      fmt.Println(err)
      return
     }
     defer listener.Close()
    
     for {
      // Accept a connection.
      conn, err := listener.Accept()
      if err != nil {
       fmt.Println(err)
       continue
      }
      // Handle the connection in a new goroutine.
      go handleConnection(conn)
     }
    }
    
    func handleConnection(conn net.Conn) {
     defer conn.Close()
     buffer := make([]byte, 1024)
     // Read the incoming connection.
     conn.Read(buffer)
     fmt.Println("Received:", string(buffer))
     // Respond to the client.
     conn.Write([]byte("Message received!"))
    }
    
    • 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

    通过为每个连接使用新的 goroutine,多个客户端可以同时连接。

    示例 4:带有 Gorilla Mux 的 HTTP 服务器

    Gorilla Mux 库简化了 HTTP 请求路由。

    package main
    
    import (
     "fmt"
     "github.com/gorilla/mux"
     "net/http"
    )
    
    func main() {
     // Create a new router.
     r := mux.NewRouter()
     // Register a handler function for the root path.
     r.HandleFunc("/", homeHandler)
     http.ListenAndServe(":8080", r)
    }
    
    func homeHandler(w http.ResponseWriter, r *http.Request) {
     // Respond with a welcome message.
     fmt.Fprint(w, "Welcome to Home!")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    这段代码设置了一个 HTTP 服务器,并为根路径定义了一个处理函数。

    示例 5:HTTPS 服务器

    实现 HTTPS 服务器可以确保安全通信。

    package main
    
    import (
     "net/http"
     "log"
    )
    
    func main() {
     http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      // Respond with a message.
      w.Write([]byte("Hello, this is an HTTPS server!"))
     })
     // Use the cert.pem and key.pem files to secure the server.
     log.Fatal(http.ListenAndServeTLS(":8080", "cert.pem", "key.pem", nil))
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    服务器使用 TLS(传输层安全性)来加密通信。

    示例 6:自定义 TCP 协议

    可以使用自定义的 TCP 协议进行专门的通信。

    package main
    
    import (
     "net"
     "strings"
    )
    
    func main() {
     // Listen on TCP port 8080.
     listener, err := net.Listen("tcp", ":8080")
     if err != nil {
      panic(err)
     }
     defer listener.Close()
    
     for {
      // Accept a connection.
      conn, err := listener.Accept()
      if err != nil {
       panic(err)
      }
      // Handle the connection in a new goroutine.
      go handleConnection(conn)
     }
    }
    
    func handleConnection(conn net.Conn) {
     defer conn.Close()
     buffer := make([]byte, 1024)
     // Read the incoming connection.
     conn.Read(buffer)
     // Process custom protocol command.
     cmd := strings.TrimSpace(string(buffer))
     if cmd == "TIME" {
      conn.Write([]byte("The current time is: " + time.Now().String()))
     } else {
      conn.Write([]byte("Unknown command"))
     }
    }
    
    • 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

    这段代码实现了一个简单的自定义协议,当客户端发送命令“TIME”时,它会回复当前时间。

    示例 7:使用 Gorilla WebSocket 进行 WebSockets

    WebSockets 提供了通过单一连接的实时全双工通信。

    package main
    
    import (
     "github.com/gorilla/websocket"
     "net/http"
    )
    
    var upgrader = websocket.Upgrader{
     ReadBufferSize:  1024,
     WriteBufferSize: 1024,
    }
    
    func handler(w http.ResponseWriter, r *http.Request) {
     conn, err := upgrader.Upgrade(w, r, nil)
     if err != nil {
      http.Error(w, "Could not open websocket connection", http.StatusBadRequest)
      return
     }
     defer conn.Close()
    
     for {
      messageType, p, err := conn.ReadMessage()
      if err != nil {
       return
      }
      // Echo the message back to the client.
      conn.WriteMessage(messageType, p)
     }
    }
    
    func main() {
     http.HandleFunc("/", handler)
     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
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    WebSocket 服务器会将消息回传给客户端。

    示例 8:连接超时

    可以使用 context 包来管理连接超时。

    package main
    
    import (
     "context"
     "fmt"
     "net"
     "time"
    )
    
    func main() {
     // Create a context with a timeout of 2 seconds
     ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
     defer cancel()
    
     // Dialer using the context
     dialer := net.Dialer{}
     conn, err := dialer.DialContext(ctx, "tcp", "localhost:8080")
     if err != nil {
      panic(err)
     }
    
     buffer := make([]byte, 1024)
     _, err = conn.Read(buffer)
     if err == nil {
      fmt.Println("Received:", string(buffer))
     } else {
      fmt.Println("Connection error:", err)
     }
    }
    
    • 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

    这段代码为从连接读取数据设置了两秒的截止时间。

    示例 9:使用 golang.org/x/time/rate 进行速率限制

    速率限制控制请求的速率。

    package main
    
    import (
     "golang.org/x/time/rate"
     "net/http"
     "time"
    )
    
    // Define a rate limiter allowing two requests per second with a burst capacity of five.
    var limiter = rate.NewLimiter(2, 5)
    
    func handler(w http.ResponseWriter, r *http.Request) {
     // Check if request is allowed by the rate limiter.
     if !limiter.Allow() {
      http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
      return
     }
     w.Write([]byte("Welcome!"))
    }
    
    func main() {
     http.HandleFunc("/", handler)
     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
    • 23
    • 24

    此示例使用速率限制器,将请求速率限制为每秒两个请求,突发容量为五个。

  • 相关阅读:
    linux下mv命令移动目录的二种情况
    Typescript 笔记:初识Typescript
    Mysql表的约束
    C++函数参数匹配规则
    Linux学习-02-Linux的安装
    “逗鹅冤”出续集,冒充老干妈员工诈骗腾讯案主犯被判刑12年
    进程的调度
    《Mission Red in Slience》——这本“连环画”值得一看
    负数二进制转成十进制
    大模型应用疯狂加速,洗牌却在静悄悄进行了
  • 原文地址:https://blog.csdn.net/csjds/article/details/133639025