Go 语言高级网络编程

2023年 11月 6日 55.0k 0

一、简介

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)))
}

客户端:

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))
}

服务器在端口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)
}

客户端:

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))
}

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

示例 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!"))
}

通过为每个连接使用新的 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!")
}

这段代码设置了一个 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))
}

服务器使用 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"))
 }
}

这段代码实现了一个简单的自定义协议,当客户端发送命令“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)
}

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)
 }
}

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

示例 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)
}

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

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论