Go中的Tcp编程为什么总是能看到handle?

0 阅读3分钟

Go中的Tcp编程为什么总是能看到handle?

先搞懂:handle/handler 到底是什么意思?

  • 英文本意
    • handle 作动词是 “处理、应对”,作名词是 “处理器、句柄”;
    • handler 是 “处理者、处理器”(名词形式)。
  • 编程中的含义:专门负责「处理某个特定事件 / 请求」的函数 / 结构体 / 接口。

在 TCP 编程中,这个 “事件 / 请求” 通常是:

  • 新的客户端连接建立;
  • 客户端发送过来的数据包;
  • 连接断开 / 出错等异常情况。

TCP 编程的 “框架 / 业务” 分离逻辑

TCP 服务端的核心逻辑可以拆成两部分:

  1. 通用框架层:负责底层的网络通信(监听端口、接收连接、读写数据、处理网络异常),这部分是固定的,所有 TCP 服务都需要;
  2. 业务逻辑层:负责处理具体的业务(比如解析客户端发的订单数据、返回查询结果),这部分是定制的,不同业务完全不同。

handle/handler 就是用来承载「业务逻辑层」的载体,框架层只负责 “调用” 它,不关心具体业务。


TCP 编程中 handler 的具体用法

TCP 服务端 - 处理单个连接
package main

import (
	"bufio"
	"fmt"
	"net"
)

// 核心:定义处理单个 TCP 连接的 handler 函数,所有和业务相关的逻辑,
func handleConn(conn net.Conn) {
	defer conn.Close()
	fmt.Printf("客户端 %s 已连接\n", conn.RemoteAddr())

	// 读取客户端发送的数据(框架层)
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		msg := scanner.Text()
		fmt.Printf("收到客户端 %s 的消息:%s\n", conn.RemoteAddr(), msg)

		// 业务逻辑层(handler 的核心职责)
		response := "服务器已收到:" + msg

		// 发送响应(框架层)
		conn.Write([]byte(response + "\n"))
	}

	fmt.Printf("客户端 %s 已断开连接\n", conn.RemoteAddr())
}

func main() {
	// 框架层:监听端口
	listener, err := net.Listen("tcp", ":8888")
	if err != nil {
		fmt.Println("监听失败:", err)
		return
	}
	defer listener.Close()
	fmt.Println("TCP 服务端已启动,监听端口 8888")

	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("接收连接失败:", err)
			continue
		}
		go handleConn(conn)
	}
}

定义成接口,让不同业务实现不同的 Handler,彻底解耦
package main

import (
	"bufio"
	"fmt"
	"net"
)

// 定义 Handler 接口:所有业务处理器都要实现这个接口
type TCPHandler interface {
	Handle(conn net.Conn) // 核心方法:处理连接
}

// 业务 1:echo 处理器(原样返回消息)
type EchoHandler struct{}

func (h *EchoHandler) Handle(conn net.Conn) {
	defer conn.Close()
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		msg := scanner.Text()
		conn.Write([]byte("Echo: " + msg + "\n"))
	}
}

// 业务 2:计算处理器(处理加法请求)
type CalcHandler struct{}

func (h *CalcHandler) Handle(conn net.Conn) {
	defer conn.Close()
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		msg := scanner.Text()
		// 模拟解析加法请求:比如 "10+20"
		conn.Write([]byte("计算结果:30\n")) // 简化逻辑
	}
}

// 通用 TCP 服务端(框架层)
func StartTCPServer(addr string, handler TCPHandler) {
	listener, _ := net.Listen("tcp", addr)
	defer listener.Close()
	fmt.Println("TCP 服务端启动:", addr)

	for {
		conn, _ := listener.Accept()
		// 调用传入的 handler 处理连接(控制反转)
		go handler.Handle(conn)
	}
}

func main() {
	// 启动 echo 服务(换业务只需要换 handler)
	// StartTCPServer(":8888", &EchoHandler{})

	// 启动计算服务
	StartTCPServer(":8888", &CalcHandler{})
}

四、为什么偏偏是 handle/handler

  1. 语义精准handler 直接点明 “这个东西是用来处理某个事件的”,比随便起个名字(比如 process/do)更清晰;
  2. 行业通用:不只是 Go 的 TCP 编程,Java 的 Netty、Python 的 Socket 编程、前端的事件处理(event handler)都用这个命名 —— 属于程序员的 “通用语言”,看到就知道是 “处理器”;
  3. 职责明确:看到 handleConn/TCPHandler,立刻能区分 “这是处理连接的业务逻辑”,和 “监听端口、读写数据的框架逻辑” 划清界限;
  4. 符合 Go 语言习惯:Go 标准库中大量使用 Handler 命名(比如 http.Handler),TCP 编程自然沿用这个习惯,保持一致性。