通过 TCP 发送的字符串中添加了不需要的空格

通过 TCP 发送的字符串中添加了不需要的空格

IT小君   2022-11-12T01:14:56

我正在尝试学习 Go,我尝试的第一件事是使用 TCP 在客户端和服务器之间发送文件。我使用 建立连接net.Listen,并使用 连接net.Dial我的客户逻辑是:

  1. binary.Write使用描述文件名的大小发送 int64
  2. 使用发送文件名io.WriteString
  3. 发送一个描述文件大小的 int64
  4. 使用发送文件io.CopyN

我的服务器逻辑是:

  1. 使用 读取 8 个字节binary.Read,另存为N
  2. 读取N字节以获取文件名,该文件名被读入随后调用它bytes.NewBuffer(make([]byte, filenameSize))的aString()
  3. 读取8个字节得到文件大小,另存为M
  4. io.CopyN从连接到M字节的新文件对象。

我的问题对我来说完全是莫名其妙的,我无法解决或理解,并且我在 Google 或 SO 上找不到任何讨论或解决方案:即使文件名长度被准确传输,服务器总是收到一个文件名是预期长度的两倍,其中前半部分是空格。更令人费解的是,使用strings.TrimLeft(filename, " ")永远不会起作用,即使它适用于我自己创建的字符串。

所以我完全不知道发生了什么,文档、SO、Google、go-nuts 等都没有提到任何看起来相关的东西。不知道如何前进或考虑这个问题,我很想得到一些帮助。

我的服务器处理程序:

func handle(conn net.Conn) {
    defer conn.Close()
    conn.SetReadDeadline(time.Now().Add(time.Second * 30))

    var filenameSize int64
    err := binary.Read(conn, binary.LittleEndian, &filenameSize)
    check(err)

    filename := bytes.NewBuffer(make([]byte, filenameSize))
    bytesRead, err := io.CopyN(filename, conn, filenameSize)
    fmt.Printf("Expected %d bytes for filename, read %d bytes\n", filenameSize, bytesRead)
    str := filename.String()
    fmt.Println(strings.TrimLeft(str, " "))

    var filesize int64
    err = binary.Read(conn, binary.LittleEndian, &filesize)
    check(err)
    fmt.Printf("Expecting %d bytes in file\n", filesize)

    f, err := os.Create(str)
    check(err)
    bytesWritten, err := io.CopyN(f, conn, filesize)
    check(err)
    fmt.Printf("Transfer complete, expected %d bytes, wrote %d bytes", filesize, bytesWritten)
    if filesize != bytesWritten {
        fmt.Printf("ERROR! File doesn't match expected size!")
    }
}

我的客户:

func main() {
    name := "test_file.doc"

    conn, err := net.Dial("tcp", "localhost:8250")
    check(err)

    length := int64(len(name))
    err = binary.Write(conn, binary.LittleEndian, length)
    check(err)

    bytes, err := io.WriteString(conn, name)
    check(err)
    if bytes != len(name) {
        fmt.Printf("Error! Wrote %d bytes but length of name is %d!\n", bytes, length)
    }

    f, err := os.Open(name)
    check(err)

    stat, err := f.Stat()
    check(err)

    filesize := stat.Size()
    err = binary.Write(conn, binary.LittleEndian, filesize)
    check(err)

    bytesWritten, err := io.CopyN(conn, f, filesize)
    check(err)
    if bytesWritten != filesize {
        fmt.Printf("Error! Wrote %d bytes but length of file is %d!\n", bytes, stat.Size())
    }
}
服务器费用不足...
评论(2)
IT小君

Go 编程语言规范

分配

制作切片、地图和通道

Call             Type T     Result

make(T, n)       slice      slice of type T with length n and capacity n
make(T, n, m)    slice      slice of type T with length n and capacity m

包字节

import "bytes"

函数新缓冲区

func NewBuffer(buf []byte) *缓冲区

NewBuffer 使用 buf 作为初始内容创建并初始化一个新的 Buffer。新的 Buffer 拥有 buf 的所有权,调用者不应在此调用后使用 buf。NewBuffer 旨在准备一个 Buffer 来读取现有数据。它还可以用于调整内部缓冲区的大小以进行写入。为此, buf 应该具有所需的容量,但长度为零。

在大多数情况下, new(Buffer) (或者只是声明一个 Buffer 变量)就足以初始化一个 Buffer。


bytes.NewBuffer可用于调整内部缓冲区的大小以进行写入。为此, buf 应该具有所需的容量,但长度为零。例如,长度为零,容量为 filenameSize,

filename := bytes.NewBuffer(make([]byte, 0, filenameSize))

错误地,您分配了长度和容量为 filenameSize 的 buf,

filename := bytes.NewBuffer(make([]byte, filenameSize))
2022-11-12T01:14:56   回复
IT小君

服务器处理程序中的行

filename := bytes.NewBuffer(make([]byte, filenameSize))

错了,换成

var filename bytes.Buffer

该表达式make([]byte, filenameSize)创建一个初始 长度 filenameSize填充类型为空值的切片byte,因此0bytes.NewBuffer使用此切片的初始内容创建一个缓冲区并将附加到此。所以你没有得到太多,你从太多开始。

请参阅golang 语言规范 以获取make. 请参阅package bytes doc about bytes.newBuffer,它明确指出如果您打算为某些预分配方案提供缓冲区,则需要长度为零但容量为正。

2022-11-12T01:14:56   回复