我正在尝试学习 Go,我尝试的第一件事是使用 TCP 在客户端和服务器之间发送文件。我使用 建立连接net.Listen
,并使用 连接net.Dial
。我的客户逻辑是:
binary.Write
使用描述文件名的大小发送 int64- 使用发送文件名
io.WriteString
- 发送一个描述文件大小的 int64
- 使用发送文件
io.CopyN
我的服务器逻辑是:
- 使用 读取 8 个字节
binary.Read
,另存为N
- 读取
N
字节以获取文件名,该文件名被读入随后调用它bytes.NewBuffer(make([]byte, filenameSize))
的aString()
- 读取8个字节得到文件大小,另存为
M
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())
}
}
bytes.NewBuffer
可用于调整内部缓冲区的大小以进行写入。为此, buf 应该具有所需的容量,但长度为零。例如,长度为零,容量为 filenameSize,错误地,您分配了长度和容量为 filenameSize 的 buf,