I'm attempting to learn Go, and the first thing I've tried is sending files between a client and server using TCP. I set up a connection using net.Listen
, and connect using net.Dial
. My client logic is:
- Send an int64 using
binary.Write
describing the size of the filename - Send the filename using
io.WriteString
- Send an int64 describing the size of the file
- Send the file using
io.CopyN
My server logic is:
- Read 8 bytes using
binary.Read
, save asN
- Read
N
bytes to get the filename, which is read into abytes.NewBuffer(make([]byte, filenameSize))
that hasString()
subsequently called on it - Read 8 bytes to get the filesize, save as
M
-
io.CopyN
from the connection into a new file object forM
bytes.
My problem is something totally baffling to me, which I haven't been able to solve or understand, and for which I can find no discussion or solution on Google or SO: even though the filename length is transmitted accurately, the server always receives a filename of twice the expected length, where the first half is whitespace. Even more bafflingly, using strings.TrimLeft(filename, " ")
never works, even though it works for strings I create myself.
So I have absolutely no idea what's going on, and nothing in the docs, SO, Google, go-nuts, etc mentions anything that could seem relevant. No idea how to move forward or think about this problem, I'd love some help.
My server handler:
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
", 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
", 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!")
}
}
My client:
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!
", 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!
", bytes, stat.Size())
}
}