Go语言的网络编程进阶
Go语言的网络编程进阶
网络编程基础
Go语言提供了强大的网络编程支持,包括TCP/IP、UDP、HTTP等协议。本文将介绍Go语言网络编程的进阶概念和技巧,帮助开发者编写更复杂、更高效的网络应用。
TCP服务器和客户端
基本TCP服务器
package main import ( "fmt" "net" "os" ) func handleConnection(conn net.Conn) { defer conn.Close() buffer := make([]byte, 1024) for { n, err := conn.Read(buffer) if err != nil { fmt.Println("Error reading:", err) break } message := string(buffer[:n]) fmt.Printf("Received: %s", message) // 回显消息 _, err = conn.Write([]byte("Echo: " + message)) if err != nil { fmt.Println("Error writing:", err) break } } } func main() { port := "8080" listener, err := net.Listen("tcp", ":"+port) if err != nil { fmt.Println("Error listening:", err) os.Exit(1) } defer listener.Close() fmt.Printf("Server listening on port %s\n", port) for { conn, err := listener.Accept() if err != nil { fmt.Println("Error accepting:", err) continue } go handleConnection(conn) } }基本TCP客户端
package main import ( "fmt" "net" "os" "bufio" ) func main() { serverAddr := "localhost:8080" conn, err := net.Dial("tcp", serverAddr) if err != nil { fmt.Println("Error connecting:", err) os.Exit(1) } defer conn.Close() fmt.Println("Connected to server") // 读取用户输入并发送到服务器 scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { message := scanner.Text() _, err := conn.Write([]byte(message + "\n")) if err != nil { fmt.Println("Error writing:", err) break } // 读取服务器响应 buffer := make([]byte, 1024) n, err := conn.Read(buffer) if err != nil { fmt.Println("Error reading:", err) break } fmt.Printf("Server response: %s", string(buffer[:n])) } }UDP服务器和客户端
基本UDP服务器
package main import ( "fmt" "net" "os" ) func main() { port := "8080" addr, err := net.ResolveUDPAddr("udp", ":"+port) if err != nil { fmt.Println("Error resolving address:", err) os.Exit(1) } conn, err := net.ListenUDP("udp", addr) if err != nil { fmt.Println("Error listening:", err) os.Exit(1) } defer conn.Close() fmt.Printf("UDP server listening on port %s\n", port) buffer := make([]byte, 1024) for { n, clientAddr, err := conn.ReadFromUDP(buffer) if err != nil { fmt.Println("Error reading:", err) continue } message := string(buffer[:n]) fmt.Printf("Received from %s: %s", clientAddr, message) // 回显消息 _, err = conn.WriteToUDP([]byte("Echo: " + message), clientAddr) if err != nil { fmt.Println("Error writing:", err) continue } } }基本UDP客户端
package main import ( "fmt" "net" "os" "bufio" ) func main() { serverAddr := "localhost:8080" addr, err := net.ResolveUDPAddr("udp", serverAddr) if err != nil { fmt.Println("Error resolving address:", err) os.Exit(1) } conn, err := net.DialUDP("udp", nil, addr) if err != nil { fmt.Println("Error connecting:", err) os.Exit(1) } defer conn.Close() fmt.Println("Connected to UDP server") // 读取用户输入并发送到服务器 scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { message := scanner.Text() _, err := conn.Write([]byte(message + "\n")) if err != nil { fmt.Println("Error writing:", err) break } // 读取服务器响应 buffer := make([]byte, 1024) conn.SetReadDeadline(time.Now().Add(2 * time.Second)) n, _, err := conn.ReadFromUDP(buffer) if err != nil { fmt.Println("Error reading:", err) continue } fmt.Printf("Server response: %s", string(buffer[:n])) } }HTTP服务器进阶
自定义HTTP服务器
package main import ( "fmt" "net/http" "os" ) func helloHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) w.Write([]byte("Hello, HTTP!")) } func statusHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) w.Write([]byte(`{"status": "ok", "message": "Server is running"}`)) } func main() { http.HandleFunc("/", helloHandler) http.HandleFunc("/status", statusHandler) port := "8080" fmt.Printf("HTTP server starting on port %s\n", port) if err := http.ListenAndServe(":"+port, nil); err != nil { fmt.Println("Error starting server:", err) os.Exit(1) } }使用ServeMux
package main import ( "fmt" "net/http" "os" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("Home Page")) }) mux.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte("About Page")) }) mux.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`[{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]`)) }) port := "8080" fmt.Printf("HTTP server starting on port %s\n", port) if err := http.ListenAndServe(":"+port, mux); err != nil { fmt.Println("Error starting server:", err) os.Exit(1) } }网络编程技巧
超时处理
package main import ( "fmt" "net" "os" "time" ) func main() { // TCP连接超时 conn, err := net.DialTimeout("tcp", "localhost:8080", 5*time.Second) if err != nil { fmt.Println("Error connecting:", err) os.Exit(1) } defer conn.Close() // 设置读写超时 conn.SetReadDeadline(time.Now().Add(10 * time.Second)) conn.SetWriteDeadline(time.Now().Add(5 * time.Second)) fmt.Println("Connected with timeout settings") }连接池
package main import ( "fmt" "net" "sync" "time" ) type ConnectionPool struct { connections chan net.Conn mu sync.Mutex closed bool } func NewConnectionPool(size int, addr string) (*ConnectionPool, error) { pool := &ConnectionPool{ connections: make(chan net.Conn, size), closed: false, } // 初始化连接池 for i := 0; i < size; i++ { conn, err := net.Dial("tcp", addr) if err != nil { // 关闭已创建的连接 for conn := range pool.connections { conn.Close() } return nil, err } pool.connections <- conn } return pool, nil } func (p *ConnectionPool) Get() (net.Conn, error) { p.mu.Lock() if p.closed { p.mu.Unlock() return nil, fmt.Errorf("pool closed") } p.mu.Unlock() select { case conn := <-p.connections: // 检查连接是否有效 conn.SetReadDeadline(time.Now().Add(1 * time.Second)) _, err := conn.Read(make([]byte, 1)) if err != nil { // 连接无效,创建新连接 newConn, err := net.Dial("tcp", conn.RemoteAddr().String()) if err != nil { // 放回无效连接,让调用者处理 p.connections <- conn return nil, err } conn.Close() return newConn, nil } // 连接有效,重置超时 conn.SetReadDeadline(time.Time{}) return conn, nil case <-time.After(5 * time.Second): return nil, fmt.Errorf("timeout waiting for connection") } } func (p *ConnectionPool) Put(conn net.Conn) { p.mu.Lock() defer p.mu.Unlock() if p.closed { conn.Close() return } select { case p.connections <- conn: // 连接已放回池 default: // 池已满,关闭连接 conn.Close() } } func (p *ConnectionPool) Close() { p.mu.Lock() defer p.mu.Unlock() if p.closed { return } p.closed = true close(p.connections) for conn := range p.connections { conn.Close() } } func main() { pool, err := NewConnectionPool(5, "localhost:8080") if err != nil { fmt.Println("Error creating pool:", err) return } defer pool.Close() // 使用连接池 conn, err := pool.Get() if err != nil { fmt.Println("Error getting connection:", err) return } defer pool.Put(conn) // 使用连接... fmt.Println("Got connection from pool") }示例:聊天服务器
服务器端
package main import ( "fmt" "net" "os" "strings" "sync" ) type Client struct { conn net.Conn name string messages chan string } var ( clients = make(map[*Client]bool) broadcast = make(chan string) register = make(chan *Client) unregister = make(chan *Client) mu sync.Mutex ) func handleClient(client *Client) { defer func() { unregister <- client client.conn.Close() }() // 读取客户端消息 buffer := make([]byte, 1024) for { n, err := client.conn.Read(buffer) if err != nil { break } message := strings.TrimSpace(string(buffer[:n])) if message != "" { broadcast <- fmt.Sprintf("%s: %s", client.name, message) } } } func broadcastMessages() { for { select { case client := <-register: mu.Lock() clients[client] = true mu.Unlock() broadcast <- fmt.Sprintf("%s joined the chat", client.name) case client := <-unregister: mu.Lock() if _, ok := clients[client]; ok { delete(clients, client) close(client.messages) broadcast <- fmt.Sprintf("%s left the chat", client.name) } mu.Unlock() case message := <-broadcast: mu.Lock() for client := range clients { select { case client.messages <- message: default: close(client.messages) delete(clients, client) } } mu.Unlock() } } } func main() { go broadcastMessages() port := "8080" listener, err := net.Listen("tcp", ":"+port) if err != nil { fmt.Println("Error listening:", err) os.Exit(1) } defer listener.Close() fmt.Printf("Chat server starting on port %s\n", port) for { conn, err := listener.Accept() if err != nil { fmt.Println("Error accepting:", err) continue } // 获取客户端名称 conn.Write([]byte("Enter your name: ")) buffer := make([]byte, 1024) n, err := conn.Read(buffer) if err != nil { conn.Close() continue } name := strings.TrimSpace(string(buffer[:n])) if name == "" { conn.Close() continue } // 创建客户端 client := &Client{ conn: conn, name: name, messages: make(chan string, 256), } // 注册客户端 register <- client // 启动客户端处理协程 go handleClient(client) // 启动消息发送协程 go func() { for message := range client.messages { conn.Write([]byte(message + "\n")) } }() } }客户端
package main import ( "bufio" "fmt" "net" "os" ) func main() { serverAddr := "localhost:8080" conn, err := net.Dial("tcp", serverAddr) if err != nil { fmt.Println("Error connecting:", err) os.Exit(1) } defer conn.Close() // 读取服务器提示并输入名称 buffer := make([]byte, 1024) n, err := conn.Read(buffer) if err != nil { fmt.Println("Error reading:", err) return } fmt.Print(string(buffer[:n])) scanner := bufio.NewScanner(os.Stdin) scanner.Scan() name := scanner.Text() conn.Write([]byte(name + "\n")) // 启动读取协程 go func() { for { n, err := conn.Read(buffer) if err != nil { fmt.Println("Connection closed") os.Exit(0) } fmt.Print(string(buffer[:n])) } }() // 发送消息 for scanner.Scan() { message := scanner.Text() conn.Write([]byte(message + "\n")) } }网络编程最佳实践
- 使用超时处理避免连接阻塞
- 使用连接池提高连接复用率
- 合理设置缓冲区大小
- 处理网络错误和重试机制
- 使用goroutine处理并发连接
- 监控网络性能和错误率
- 实现优雅关闭
总结
Go语言的网络编程功能强大且易于使用,通过掌握TCP/UDP服务器、HTTP服务器和网络编程技巧,可以编写高效、可靠的网络应用。在实际开发中,应该根据具体场景选择合适的网络协议和编程模式,确保应用的性能和可靠性。
