网络编程
2026/3/20大约 6 分钟
网络编程
一、net 包基础
1.1 TCP 服务端
package main
import (
"bufio"
"fmt"
"io"
"net"
"time"
)
func main() {
// 监听端口
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("监听失败:", err)
return
}
defer listener.Close()
fmt.Println("TCP 服务器启动在 :8080")
for {
// 接受连接
conn, err := listener.Accept()
if err != nil {
fmt.Println("接受连接失败:", err)
continue
}
// 处理连接(每个连接一个 goroutine)
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
clientAddr := conn.RemoteAddr().String()
fmt.Printf("[%s] 新连接\n", clientAddr)
// 设置读取超时
conn.SetReadDeadline(time.Now().Add(5 * time.Minute))
reader := bufio.NewReader(conn)
for {
// 读取一行数据
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
fmt.Printf("[%s] 连接关闭\n", clientAddr)
} else {
fmt.Printf("[%s] 读取错误: %v\n", clientAddr, err)
}
return
}
fmt.Printf("[%s] 收到: %s", clientAddr, line)
// 回显
response := fmt.Sprintf("服务器收到: %s", line)
conn.Write([]byte(response))
}
}
1.2 TCP 客户端
package main
import (
"bufio"
"fmt"
"net"
"time"
)
func main() {
// 连接服务器
conn, err := net.DialTimeout("tcp", "localhost:8080", 5*time.Second)
if err != nil {
fmt.Println("连接失败:", err)
return
}
defer conn.Close()
fmt.Println("已连接到服务器")
// 发送数据
messages := []string{"Hello\n", "World\n", "Goodbye\n"}
for _, msg := range messages {
_, err := conn.Write([]byte(msg))
if err != nil {
fmt.Println("发送失败:", err)
return
}
// 读取响应
response, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
fmt.Println("读取失败:", err)
return
}
fmt.Print("响应: ", response)
}
}
1.3 UDP 编程
package main
import (
"fmt"
"net"
)
// UDP 服务端
func udpServer() {
addr, _ := net.ResolveUDPAddr("udp", ":9090")
conn, err := net.ListenUDP("udp", addr)
if err != nil {
fmt.Println("监听失败:", err)
return
}
defer conn.Close()
fmt.Println("UDP 服务器启动在 :9090")
buf := make([]byte, 1024)
for {
n, clientAddr, err := conn.ReadFromUDP(buf)
if err != nil {
continue
}
fmt.Printf("[%s] 收到: %s\n", clientAddr, string(buf[:n]))
// 回复
response := fmt.Sprintf("收到 %d 字节", n)
conn.WriteToUDP([]byte(response), clientAddr)
}
}
// UDP 客户端
func udpClient() {
addr, _ := net.ResolveUDPAddr("udp", "localhost:9090")
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
fmt.Println("连接失败:", err)
return
}
defer conn.Close()
// 发送
conn.Write([]byte("Hello UDP"))
// 接收
buf := make([]byte, 1024)
n, _ := conn.Read(buf)
fmt.Println("响应:", string(buf[:n]))
}
二、HTTP 客户端
2.1 基础请求
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
)
func main() {
// ========== GET 请求 ==========
resp, err := http.Get("https://httpbin.org/get")
if err != nil {
fmt.Println("请求失败:", err)
return
}
defer resp.Body.Close()
fmt.Println("状态码:", resp.StatusCode)
body, _ := io.ReadAll(resp.Body)
fmt.Println("响应体:", string(body)[:100])
// ========== POST 表单 ==========
formData := url.Values{
"username": {"admin"},
"password": {"123456"},
}
resp, _ = http.PostForm("https://httpbin.org/post", formData)
defer resp.Body.Close()
body, _ = io.ReadAll(resp.Body)
fmt.Println("POST 表单:", string(body)[:100])
// ========== POST JSON ==========
data := map[string]interface{}{
"name": "web-01",
"port": 8080,
"alive": true,
}
jsonData, _ := json.Marshal(data)
resp, _ = http.Post(
"https://httpbin.org/post",
"application/json",
bytes.NewBuffer(jsonData),
)
defer resp.Body.Close()
// ========== 自定义请求 ==========
req, _ := http.NewRequest("GET", "https://httpbin.org/headers", nil)
req.Header.Set("User-Agent", "Go-HTTP-Client/1.0")
req.Header.Set("Authorization", "Bearer token123")
req.Header.Set("X-Custom-Header", "custom-value")
client := &http.Client{}
resp, _ = client.Do(req)
defer resp.Body.Close()
}
2.2 高级客户端配置
package main
import (
"context"
"crypto/tls"
"fmt"
"io"
"net"
"net/http"
"time"
)
func main() {
// 自定义 Transport
transport := &http.Transport{
// 连接配置
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
// 连接池
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
MaxConnsPerHost: 100,
IdleConnTimeout: 90 * time.Second,
// TLS 配置
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false, // 生产环境应为 false
},
TLSHandshakeTimeout: 10 * time.Second,
// HTTP/2
ForceAttemptHTTP2: true,
}
client := &http.Client{
Transport: transport,
Timeout: 30 * time.Second,
}
// 带超时的请求
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", "https://httpbin.org/delay/5", nil)
resp, err := client.Do(req)
if err != nil {
fmt.Println("请求失败:", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println("响应:", string(body)[:100])
}
// 运维场景:健康检查客户端
type HealthChecker struct {
client *http.Client
timeout time.Duration
}
func NewHealthChecker(timeout time.Duration) *HealthChecker {
return &HealthChecker{
client: &http.Client{
Timeout: timeout,
Transport: &http.Transport{
MaxIdleConnsPerHost: 10,
},
},
timeout: timeout,
}
}
func (hc *HealthChecker) Check(url string) (bool, time.Duration, error) {
start := time.Now()
ctx, cancel := context.WithTimeout(context.Background(), hc.timeout)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := hc.client.Do(req)
if err != nil {
return false, time.Since(start), err
}
defer resp.Body.Close()
return resp.StatusCode >= 200 && resp.StatusCode < 300, time.Since(start), nil
}
三、HTTP 服务端
3.1 基础服务器
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
)
func main() {
// 路由注册
http.HandleFunc("/", homeHandler)
http.HandleFunc("/health", healthHandler)
http.HandleFunc("/api/servers", serversHandler)
// 启动服务器
server := &http.Server{
Addr: ":8080",
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
log.Println("服务器启动在 :8080")
log.Fatal(server.ListenAndServe())
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "欢迎访问 Go HTTP 服务器")
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "healthy",
"time": time.Now().Format(time.RFC3339),
})
}
func serversHandler(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
servers := []map[string]interface{}{
{"name": "web-01", "ip": "10.0.0.1", "status": "running"},
{"name": "web-02", "ip": "10.0.0.2", "status": "running"},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(servers)
case "POST":
var server map[string]interface{}
json.NewDecoder(r.Body).Decode(&server)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(server)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
3.2 中间件模式
package main
import (
"log"
"net/http"
"time"
)
// 中间件类型
type Middleware func(http.Handler) http.Handler
// 日志中间件
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// 包装 ResponseWriter 以捕获状态码
wrapped := &statusRecorder{ResponseWriter: w, statusCode: 200}
next.ServeHTTP(wrapped, r)
log.Printf("%s %s %d %v",
r.Method,
r.URL.Path,
wrapped.statusCode,
time.Since(start),
)
})
}
type statusRecorder struct {
http.ResponseWriter
statusCode int
}
func (sr *statusRecorder) WriteHeader(code int) {
sr.statusCode = code
sr.ResponseWriter.WriteHeader(code)
}
// 恢复中间件
func RecoverMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("Panic recovered: %v", err)
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
// CORS 中间件
func CORSMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
// 认证中间件
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token != "Bearer secret-token" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
// 中间件链
func Chain(h http.Handler, middlewares ...Middleware) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
h = middlewares[i](h)
}
return h
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
mux.HandleFunc("/panic", func(w http.ResponseWriter, r *http.Request) {
panic("intentional panic")
})
// 应用中间件链
handler := Chain(mux,
RecoverMiddleware,
LoggingMiddleware,
CORSMiddleware,
)
log.Println("服务器启动在 :8080")
http.ListenAndServe(":8080", handler)
}
3.3 RESTful API 示例
package main
import (
"encoding/json"
"log"
"net/http"
"strconv"
"strings"
"sync"
)
type Server struct {
ID int `json:"id"`
Name string `json:"name"`
IP string `json:"ip"`
Status string `json:"status"`
}
type ServerStore struct {
mu sync.RWMutex
servers map[int]Server
nextID int
}
func NewServerStore() *ServerStore {
return &ServerStore{
servers: make(map[int]Server),
nextID: 1,
}
}
func (s *ServerStore) GetAll() []Server {
s.mu.RLock()
defer s.mu.RUnlock()
result := make([]Server, 0, len(s.servers))
for _, srv := range s.servers {
result = append(result, srv)
}
return result
}
func (s *ServerStore) Get(id int) (Server, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
srv, ok := s.servers[id]
return srv, ok
}
func (s *ServerStore) Create(srv Server) Server {
s.mu.Lock()
defer s.mu.Unlock()
srv.ID = s.nextID
s.nextID++
s.servers[srv.ID] = srv
return srv
}
func (s *ServerStore) Update(id int, srv Server) (Server, bool) {
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.servers[id]; !ok {
return Server{}, false
}
srv.ID = id
s.servers[id] = srv
return srv, true
}
func (s *ServerStore) Delete(id int) bool {
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.servers[id]; !ok {
return false
}
delete(s.servers, id)
return true
}
var store = NewServerStore()
func serversHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
// 解析路径获取 ID
path := strings.TrimPrefix(r.URL.Path, "/api/servers")
path = strings.TrimPrefix(path, "/")
if path == "" {
// /api/servers
switch r.Method {
case "GET":
json.NewEncoder(w).Encode(store.GetAll())
case "POST":
var srv Server
json.NewDecoder(r.Body).Decode(&srv)
created := store.Create(srv)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(created)
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
} else {
// /api/servers/{id}
id, err := strconv.Atoi(path)
if err != nil {
http.Error(w, "Invalid ID", http.StatusBadRequest)
return
}
switch r.Method {
case "GET":
if srv, ok := store.Get(id); ok {
json.NewEncoder(w).Encode(srv)
} else {
w.WriteHeader(http.StatusNotFound)
}
case "PUT":
var srv Server
json.NewDecoder(r.Body).Decode(&srv)
if updated, ok := store.Update(id, srv); ok {
json.NewEncoder(w).Encode(updated)
} else {
w.WriteHeader(http.StatusNotFound)
}
case "DELETE":
if store.Delete(id) {
w.WriteHeader(http.StatusNoContent)
} else {
w.WriteHeader(http.StatusNotFound)
}
default:
w.WriteHeader(http.StatusMethodNotAllowed)
}
}
}
func main() {
// 初始化数据
store.Create(Server{Name: "web-01", IP: "10.0.0.1", Status: "running"})
store.Create(Server{Name: "web-02", IP: "10.0.0.2", Status: "running"})
http.HandleFunc("/api/servers", serversHandler)
http.HandleFunc("/api/servers/", serversHandler)
log.Println("API 服务器启动在 :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
四、优雅关闭
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second) // 模拟处理
w.Write([]byte("Hello!"))
})
server := &http.Server{
Addr: ":8080",
Handler: mux,
}
// 在 goroutine 中启动服务器
go func() {
log.Println("服务器启动在 :8080")
if err := server.ListenAndServe(); err != http.ErrServerClosed {
log.Fatalf("服务器错误: %v", err)
}
}()
// 等待中断信号
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("正在关闭服务器...")
// 优雅关闭,等待处理完成
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatalf("关闭失败: %v", err)
}
log.Println("服务器已关闭")
}
五、本章小结
| 主题 | 核心要点 |
|---|---|
| TCP | net.Listen + Accept,每连接一个 goroutine |
| UDP | net.ListenUDP,无连接,ReadFromUDP/WriteToUDP |
| HTTP 客户端 | 自定义 Transport,连接池,超时控制 |
| HTTP 服务端 | ServeMux 路由,中间件模式 |
| RESTful API | 标准 HTTP 方法,JSON 编解码 |
| 优雅关闭 | signal.Notify + server.Shutdown |
运维开发建议
- HTTP 客户端复用连接池,设置合理超时
- 服务端使用中间件处理日志、恢复、认证
- 生产环境必须实现优雅关闭
- API 设计遵循 RESTful 规范