You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言TCP客户端经常丢失第二条JSON回复的问题排查

问题背景
  • 目标:编写Go TCP客户端,向本地127.0.0.1:60000端口的TCP服务端发送JSON RPC请求,服务端收到请求后会连续返回两条以换行符分隔的JSON响应
  • 现象:客户端大部分场景只能收到第一条响应,仅偶现能收到两条;用Python Twisted编写的同逻辑客户端可100%收到两条响应,已排除服务端故障
  • 原问题客户端代码:
package main

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

func main() {
    d := net.Dialer{ }
    c, err := d.Dial("tcp", "127.0.0.1:60000")

    if err != nil {
        log.Println(err)
        return
    }

    data := `{"id": 1,"method":"mining.configure","params": [["version-rolling"],{"version-rolling.mask": "1fffe000","version-rolling.min-bit-count": 16}]}`

    fmt.Fprintf(c, data+"\n")

    for {
        message, _ := bufio.NewReader(c).ReadString('\n')
        fmt.Println(message)
    }   
}
  • 发送的请求内容:
{
 "id": 1,
 "method":"mining.configure",
 "params": [["version-rolling"],
           {"version-rolling.mask": "1fffe000",
            "version-rolling.min-bit-count": 16}]
}
  • 预期收到的两条响应:
{"error":null,"id":1,"result":{"version-rolling":true,"version-rolling.mask":"1fffe000"}}
{"id":null,"method":"mining.set_version_mask","params":["1fffe000"]}
  • 实际大部分场景仅收到第一条:
{"error":null,"id":1,"result":{"version-rolling":true,"version-rolling.mask":"1fffe000"}}
根因分析

核心错误是在读取循环中重复创建bufio.Reader实例:

  1. bufio.Reader自带内部缓冲区,调用ReadString('\n')时,不会只读刚好到第一个换行符的数据,而是会一次性从底层TCP连接读取尽可能多的数据存入自身缓冲区,再从中拆分出第一条换行分隔的消息返回
  2. 第一次读取完成后,第二条响应大概率已经被拉取到第一个bufio.Reader的内部缓冲区中,但循环下一次迭代直接新建了一个全新的bufio.Reader,旧Reader缓冲区里缓存的第二条消息没有被处理就被垃圾回收,自然读不到
  3. 偶现能收到两条的场景,是TCP传输时刚好把两条响应拆成了两个独立的TCP包发送,第一次读取时缓冲区只拿到了第一条消息,新建的第二个Reader才能从连接中读到第二条消息,完全是概率事件。

另外原代码忽略了所有读写错误,连接异常断开时不会有任何提示,不利于排查问题。

修复代码

只需要在循环外创建一次bufio.Reader,补充错误处理和连接关闭逻辑即可:

package main

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

func main() {
	d := net.Dialer{}
	c, err := d.Dial("tcp", "127.0.0.1:60000")
	if err != nil {
		log.Fatalf("连接服务端失败: %v", err)
	}
	defer c.Close() // 程序退出前确保关闭连接

	data := `{"id": 1,"method":"mining.configure","params": [["version-rolling"],{"version-rolling.mask": "1fffe000","version-rolling.min-bit-count": 16}]}`
	// 检查写入错误
	_, err = fmt.Fprintf(c, data+"\n")
	if err != nil {
		log.Fatalf("发送请求失败: %v", err)
	}

	// 仅在循环外创建一次reader,禁止在循环里反复新建
	reader := bufio.NewReader(c)
	for {
		message, err := reader.ReadString('\n')
		if err != nil {
			log.Fatalf("读取响应失败: %v", err)
		}
		fmt.Print(message)
	}
}

修复后运行即可稳定收到两条响应。


内容的提问来源于stack exchange,提问作者Johann Suarez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 16:42:58