net.Conn.Write()不能直接传struct,因该方法只接受[]byte,需先序列化并加4字节大端长度前缀以解决TCP粘包问题;gob不适用于跨语言通信,应选protobuf或msgpack。

不能直接替换 http.DefaultTransport,也不能让 net.Conn.Write() 接收 struct —— 这两类操作在 Go 里必报错或引发线上故障。
为什么 conn.Write(myStruct) 一定失败
因为 net.Conn.Write() 只接受 []byte,Go 不会自动把 struct 转成字节流。常见错误包括:
- 直接写
conn.Write(user)→ 编译报错cannot use user (type User) as type []byte - 用
fmt.Sprintf("%v", user)拼字符串再转[]byte→ 字段顺序、nilslice、time.Time格式全不可控 - 只做
json.Marshal(user)然后Write()→ 解决了序列化,但没解决 TCP 粘包,接收方无法判断消息边界
如何在 net.Conn 上安全收发变长消息
TCP 是字节流,没有天然消息边界。必须自己加长度前缀,否则一次 Read() 可能只读到半条请求,也可能读到两条拼在一起。
- 最简可靠方案是「4 字节大端长度头 + payload」:用
binary.BigEndian.PutUint32(header[:], uint32(len(payload)))写头 - 读包必须用
io.ReadFull(conn, header[:]),不能用裸conn.Read()—— 否则可能只读到 2 字节就返回 - 长度字段本身不能压缩或加密,否则接收方无法预判后续要读多少字节
- 示例解包函数中,
payload := make([]byte, length)后仍需io.ReadFull(conn, payload),确保读满
自定义 RoundTripper 时为什么不能直接赋值给 http.DefaultTransport
写 http.DefaultTransport = &myRT{} 看似简单,实则丢弃原 transport 的连接池、TLS 配置、超时策略,还会引发并发竞态。
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
立即学习“go语言免费学习笔记(深入)”;
-
http.DefaultTransport是已初始化的指针,直接覆盖会让所有使用http.Get或第三方库的请求行为异常 -
http.Transport内部字段(如MaxIdleConns、IdleConnTimeout)不是线程安全的裸赋值目标 - 正确做法是新建
http.Client,把自定义RoundTripper塞进其Transport字段 - 若需复用默认配置,应浅拷贝:
tr := &http.Transport{}; *tr = *http.DefaultTransport.(*http.Transport),再包装
跨语言通信时为什么别用 gob
gob 是 Go 专有格式,没有稳定跨语言规范,生产环境极易翻车。
- Python/PHP 几乎无法可靠解析;哪怕硬写了 decoder,Go 一次字段重排或类型升级(
int→int64)就直接 decode 失败 - Go 1.19+ 对未导出字段的编码行为有调整,旧客户端可能 panic
- 无 schema 版本控制:服务端加个字段,老客户端反序列化可能静默丢数据或报
unexpected EOF - 抓包看到的是不可读二进制,调试困难;而
protobuf或msgpack支持 schema 定义与多语言生成
真正上线的自定义协议,序列化和分帧必须同时解决;长度前缀不是“锦上添花”,而是 TCP 流式特性的硬性要求 —— 少一个环节,就等于把协议交给了网络抖动来决定是否能跑通。

















