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

时间:2017-10-22 20:47:07

标签: go

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

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

    我的服务器逻辑是:

    1. 使用binary.Read读取8个字节,另存为N
    2. 读取N个字节以获取文件名,该文件名会被读入bytes.NewBuffer(make([]byte, filenameSize)),随后会调用String()
    3. 读取8个字节以获取文件大小,另存为M
    4. io.CopyN从连接到M字节的新文件对象。
    5. 我的问题对我来说完全令人困惑,我无法解决或理解,而且我无法在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())
          }
      }
      

2 个答案:

答案 0 :(得分:2)

服务器处理程序中的行

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

错误,按

重新说明
var filename bytes.Buffer

表达式make([]byte, filenameSize)创建一个带有initial的切片 length filenameSize填充了byte类型的空值,因此0bytes.NewBuffer创建一个缓冲区,其中包含此切片的初始内容,并将附加到此处。所以你没有收到太多,你从太多开始。

参见golang language specmake。 请参阅package bytes doc关于bytes.newBuffer,如果您打算为某些预分配方案提交缓冲区,则明确指出您需要长度零但正容量

答案 1 :(得分:2)

  

The Go Programming Language Specification

     

Allocation

     

制作切片,地图和频道

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使用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))