
本文详解如何在 Go 中构建支持持续连接、接收多条独立消息的 TCP 服务端,通过 bufio.Scanner 或 ReadString('\n') 实现消息边界识别,并提供完整可运行的服务端/客户端示例及关键注意事项。
本文详解如何在 go 中构建支持持续连接、接收多条独立消息的 tcp 服务端,通过 `bufio.scanner` 或 `readstring('\n')` 实现消息边界识别,并提供完整可运行的服务端/客户端示例及关键注意事项。
在 TCP 协议中,数据是以字节流(byte stream)形式传输的,没有天然的消息边界。原始代码中使用 io.Copy(&buf, c) 会一直阻塞等待连接关闭(EOF),导致客户端不主动调用 end() 或 destroy() 时,服务端无法感知单条消息结束——这正是“收不到消息”的根本原因。
要实现在同一 TCP 连接中发送多条消息而不关闭连接,必须约定一种消息分隔机制。最常用、简洁且适合文本类协议的方式是:以换行符 \n 作为消息边界(即 Line-Based Protocol)。Go 标准库提供了高效支持该模式的工具。
✅ 推荐实现:使用 bufio.NewReader + ReadString('\n')
以下是优化后的 Go 服务端代码(已修复资源泄漏、并发安全与错误处理):
package main
import (
"bufio"
"log"
"net"
"strings"
)
func main() {
l, err := net.Listen("tcp", ":1200")
if err != nil {
log.Fatal("监听失败:", err)
}
defer l.Close()
log.Println("TCP 服务器启动,监听 :1200")
for {
conn, err := l.Accept()
if err != nil {
log.Printf("接受连接失败: %v", err)
continue // 不终止主循环
}
go handleConnection(conn) // 启动独立 goroutine 处理每个连接
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
// ReadString 会读取直到遇到 '\n'(包含该字符)
message, err := reader.ReadString('\n')
if err != nil {
if strings.Contains(err.Error(), "EOF") || strings.Contains(err.Error(), "use of closed network connection") {
log.Printf("客户端断开连接: %s", conn.RemoteAddr())
} else {
log.Printf("读取消息失败: %v", err)
}
return // 退出当前连接处理
}
// 去除末尾换行符(可选,便于日志和后续解析)
message = strings.TrimSuffix(message, "\n")
log.Printf("收到消息(%d 字节): %q", len(message), message)
}
}
? 客户端配合要点(Node.js 示例)
客户端必须严格遵守协议:每条完整消息后追加 \n,且避免调用 end() 过早关闭写入端(否则服务端会立即收到 EOF):
const net = require('net');
const packet = {
Target: { Host: "", Port: "9000" },
Name: { Family: "Newmarch", Personal: "Jan" },
Email: [
{ Kind: "home", Address: "jan@newmarch.name" },
{ Kind: "work", Address: "j.newmarch@boxhill.edu.au" }
]
};
const client = net.createConnection(1200, '127.0.0.1', () => {
console.log('✅ 已连接到服务器');
// 发送 5 条独立 JSON 消息(每条以 \n 结尾)
for (let i = 0; i {
console.log(`? 服务端响应: ${data.toString().trim()}`);
});
client.on('error', (err) => {
console.error('❌ 客户端错误:', err.message);
});
client.on('close', () => {
console.log('? 连接已关闭');
});
? 进阶提示:如需更高性能或二进制协议支持,可考虑:
- 使用 bufio.Scanner(自动处理 \n 分割,更简洁);
- 自定义长度前缀协议(如 4 字节大端整数表示后续消息长度);
- 引入序列化框架(Protocol Buffers + 自定义帧头)。
⚠️ 关键注意事项
- 不要在循环内重复创建 bufio.Reader:应在 handleConnection 中初始化一次,复用提高性能;
- 显式处理 EOF 和网络错误:避免 goroutine 泄漏;
- 客户端需确保 \n 是 ASCII 字符(十进制 10),而非 Windows 风格 \r\n(除非服务端也适配);
- JSON 字符串本身不可含未转义换行符,否则会破坏消息边界(生产环境建议校验或改用长度前缀);
- 超时控制:为防止空连接长期占用资源,建议对 conn 设置 SetReadDeadline。
通过以上设计,你即可构建一个健壮、可扩展的 TCP 消息服务端,支撑高并发、长连接、多消息交互场景。










