conn.Write(myStruct) 直接报错因net.Conn.Write只接受[]byte,Go结构体无法隐式转换;正确方案需先序列化(如json.Marshal),再添加4字节大端长度头,并用io.ReadFull可靠读取。
conn.Write(myStruct) 为什么直接报错
因为 net.conn.write 的函数签名只接受 []byte,而 go 结构体是独立类型,无法隐式转换。编译器会直接报 cannot use mystruct (type mystruct) as type []byte。cursor 在你输入“发送结构体”后可能自动生成 json.marshal + write,这能过编译,但只是半截方案——它没解决 tcp 粘包,也没处理长度头、字节序、读端匹配等关键环节。
写包必须加 4 字节大端长度头
TCP 是流式协议,没有天然消息边界。writePacket 如果不带长度前缀,接收方根本不知道该读多少字节才算一条完整消息。正确做法是:先序列化 payload(如用 json.Marshal 或 gob.Encode),再用 binary.BigEndian.PutUint32 把长度写入 4 字节 header,最后 Write(append(header[:], payload...))。
- 别用
int或binary.LittleEndian——跨平台或对接 Python/Java 客户端时必错 - 长度字段建议加校验:
if len(payload) > 1024*1024 { return errors.New("payload too large") },否则恶意构造大长度可触发 OOM - Cursor 不会自动补全这层防护,得你自己加
读包必须用 io.ReadFull 而非 conn.Read
conn.Read 是底层 syscall,可能只读到部分 header 或 payload 就返回,比如只读到 2 字节长度头,后续解析全乱。正确读法是分两步:io.ReadFull(conn, header[:]) 先确保读满 4 字节,再按解析出的长度申请 buffer,再调一次 io.ReadFull(conn, payload)。
- 裸调
conn.Read(header)是常见错误,现象是偶尔 panic: "unexpected EOF" 或解包失败 -
io.ReadFull会阻塞直到填满 buffer 或返回 error,这才是可靠读取的语义 - writePacket 和 readPacket 必须严格对称:写端用 BigEndian,读端也得用;写端加了长度头,读端就必须先读头
json vs gob vs msgpack:选哪个序列化格式
Cursor 对序列化格式没有偏好,但它生成的代码不会提醒你格式的兼容性代价。
-
gob是 Go 专属,Python/PHP 客户端无法解码;结构体加字段或改字段类型,旧服务端一收包就 panic -
json表面简单,但time.Time默认序列化成带时区字符串,浮点数精度丢失,nil字段和空字符串行为不一致,调试时容易怀疑人生 - 生产环境真正稳健的选择是
protobuf(强 schema、多语言、向后兼容)或msgpack(轻量、多语言),但 Cursor 不会帮你生成 .proto 文件或注册类型
最常被忽略的一点:协议设计不是写完 writePacket 就结束。你得手动验证两端是否严格对称——比如用 nc -l 8080 抓包看前 4 字节是不是预期长度,再确认 payload 解析结果是否与原始结构体一致。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











