dpkt17803 2017-10-22 20:47
浏览 101
已采纳

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

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:

  1. Send an int64 using binary.Write describing the size of the filename
  2. Send the filename using io.WriteString
  3. Send an int64 describing the size of the file
  4. Send the file using io.CopyN

My server logic is:

  1. Read 8 bytes using binary.Read, save as N
  2. Read N bytes to get the filename, which is read into a bytes.NewBuffer(make([]byte, filenameSize)) that has String() subsequently called on it
  3. Read 8 bytes to get the filesize, save as M
  4. io.CopyN from the connection into a new file object for M 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())
    }
}
  • 写回答

2条回答 默认 最新

  • duanjiaolia97750 2017-10-22 21:28
    关注

    The Go Programming Language Specification

    Allocation

    Making slices, maps and channels

    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
    

    Package bytes

    import "bytes"
    

    func NewBuffer

    func NewBuffer(buf []byte) *Buffer

    NewBuffer creates and initializes a new Buffer using buf as its initial contents. The new Buffer takes ownership of buf, and the caller should not use buf after this call. NewBuffer is intended to prepare a Buffer to read existing data. It can also be used to size the internal buffer for writing. To do that, buf should have the desired capacity but a length of zero.

    In most cases, new(Buffer) (or just declaring a Buffer variable) is sufficient to initialize a Buffer.


    bytes.NewBuffer can be used to size the internal buffer for writing. To do that, buf should have the desired capacity but a length of zero. For example, with a length of zero and a capacity of filenameSize,

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

    In error, you allocated buf with a length and capacity of filenameSize,

    filename := bytes.NewBuffer(make([]byte, filenameSize))
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?