我试图学习Go,我尝试过的第一件事是使用TCP在客户端和服务器之间发送文件。我使用net.Listen
设置了连接,并使用net.Dial
进行连接。我的客户逻辑是:
binary.Write
发送int64 io.WriteString
io.CopyN
我的服务器逻辑是:
binary.Read
读取8个字节,另存为N
N
个字节以获取文件名,该文件名会被读入bytes.NewBuffer(make([]byte, filenameSize))
,随后会调用String()
M
io.CopyN
从连接到M
字节的新文件对象。我的问题对我来说完全令人困惑,我无法解决或理解,而且我无法在Google或SO上找到任何讨论或解决方案:即使文件名长度准确传输,服务器总是接收两倍于预期长度的文件名,其中前半部分是空格。更令人费解的是,使用strings.TrimLeft(filename, " ")
永远不会起作用,即使它适用于我自己创建的字符串。
所以我完全不知道发生了什么,文档中没有任何东西,SO,Google,疯狂等提及任何看似相关的内容。不知道如何前进或思考这个问题,我会喜欢一些帮助。
我的服务器处理程序:
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())
}
}
答案 0 :(得分:2)
服务器处理程序中的行
filename := bytes.NewBuffer(make([]byte, filenameSize))
错误,按
重新说明var filename bytes.Buffer
表达式make([]byte, filenameSize)
创建一个带有initial的切片
length filenameSize
填充了byte
类型的空值,因此0
。 bytes.NewBuffer
创建一个缓冲区,其中包含此切片的初始内容,并将附加到此处。所以你没有收到太多,你从太多开始。
参见golang language spec
为make
。
请参阅package bytes doc关于bytes.newBuffer
,如果您打算为某些预分配方案提交缓冲区,则明确指出您需要长度零但正容量。
答案 1 :(得分:2)
The Go Programming Language Specification
制作切片,地图和频道
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)* Buffer
NewBuffer使用buf创建并初始化一个新的Buffer 初始内容。新的Buffer取得了buf的所有权 呼叫后,呼叫者不应使用buf。 NewBuffer旨在 准备一个缓冲区来读取现有数据。它也可用于尺寸 用于写入的内部缓冲区。要做到这一点,buf应该有 期望的容量,但长度为零。
在大多数情况下,new(Buffer)(或只是声明一个Buffer变量)是 足以初始化缓冲区。
bytes.NewBuffer
可用于调整内部缓冲区的大小以进行写入。要做到这一点,buf应该具有所需的容量,但长度为零。例如,长度为零,容量为filenameSize,
filename := bytes.NewBuffer(make([]byte, 0, filenameSize))
错误地,您分配了buf,其长度和容量为filenameSize,
filename := bytes.NewBuffer(make([]byte, filenameSize))