Golang中TCP数据传输不全问题排查与解决

Golang中TCP数据传输不全问题排查与解决 你好,

我正在编写服务器和客户端程序,但在测试过程中发现了一个令人不快的问题:在某些时刻,客户端和服务器之间的通信会卡住。有时这种情况发生在第一对消息之后,有时则发生得更晚。任何地方都没有抛出错误,只是服务器发送了消息,但客户端没有收到。

未完成的代码:

服务器:https://gist.github.com/matejstrnad/a25247788c1d5cb9f34a9fc3485ac120

客户端:https://gist.github.com/matejstrnad/cc27f76fd69b929665a7a6542f2c8682

有时,这个问题发生在服务器第113行和客户端第57行的消息上;有时也发生在发送公钥后到达服务器的“ok”消息上,即服务器第59行和客户端第64行。

我该如何修复这个问题?错误在哪里?

另外,我为我的英语道歉,我正在学习它。


更多关于Golang中TCP数据传输不全问题排查与解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中TCP数据传输不全问题排查与解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


问题出在TCP消息边界处理上。你的代码假设每次Read()调用都能读取完整的消息,但TCP是流式协议,数据可能被拆分或合并传输。

以下是修复后的关键代码示例:

服务器端修复:

func readMessage(conn net.Conn) ([]byte, error) {
    // 先读取消息长度(4字节)
    lengthBuf := make([]byte, 4)
    _, err := io.ReadFull(conn, lengthBuf)
    if err != nil {
        return nil, err
    }
    
    length := binary.BigEndian.Uint32(lengthBuf)
    
    // 根据长度读取消息体
    messageBuf := make([]byte, length)
    _, err = io.ReadFull(conn, messageBuf)
    if err != nil {
        return nil, err
    }
    
    return messageBuf, nil
}

func writeMessage(conn net.Conn, data []byte) error {
    // 先发送消息长度(4字节)
    length := uint32(len(data))
    lengthBuf := make([]byte, 4)
    binary.BigEndian.PutUint32(lengthBuf, length)
    
    _, err := conn.Write(lengthBuf)
    if err != nil {
        return err
    }
    
    // 发送消息体
    _, err = conn.Write(data)
    return err
}

客户端修复:

// 使用相同的readMessage和writeMessage函数
// 替换原来的conn.Read和conn.Write调用

// 发送消息示例:
err = writeMessage(conn, []byte("ok"))
if err != nil {
    log.Printf("Write failed: %v", err)
    return
}

// 接收消息示例:
msg, err := readMessage(conn)
if err != nil {
    log.Printf("Read failed: %v", err)
    return
}

在你的代码中需要修改的地方:

  1. 服务器第59行附近:
// 原代码:
n, err := conn.Read(buffer)
// 改为:
msg, err := readMessage(conn)
  1. 服务器第113行附近:
// 原代码:
_, err = conn.Write([]byte("ok"))
// 改为:
err = writeMessage(conn, []byte("ok"))
  1. 客户端第57行和第64行附近进行同样的修改。

这个修复方案通过添加消息长度前缀来确保完整消息的传输。io.ReadFull会阻塞直到读取到指定数量的字节,避免了数据不完整的问题。

回到顶部