【问题标题】:The method of reading an integral network PDU in GoGo中读取一个完整的网络PDU的方法
【发布时间】:2014-07-09 15:50:59
【问题描述】:

我正在开发一个简单的 Go 服务器程序,它接收客户端的请求并处理它。并且代码简化为:

package main

import (
    "fmt"
    "net"
    "os"
)

const (
    pduLen = 32
)

func checkError(err error) {
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

func main() {
    var buffer [4096]byte
    var count int

    conn, err := net.Dial("tcp", fmt.Sprintf("%s:%s", os.Args[1], os.Args[2]))
    checkError(err)

    for count < pduLen {
        n, err := conn.Read(buffer[count:])
        checkError(err)
        count += n
    }
    ......

}

我假设每个请求的长度都是32 字节(只是一个例子)。因为TCP是流协议,所以我需要使用循环来检查是否读取了一个完整的PDU:

for count < pduLen {
    n, err := conn.Read(buffer[count:])
    checkError(err)
    count += n
}

是否有任何方法可以确保读取完整的 PDU?就个人而言,我认为循环代码有点难看。

【问题讨论】:

  • 像github.com/PreetamJinka/fickle/blob/… 这样的东西有帮助吗?即:读取大小,然后在一次Read() 调用中读取所有消息
  • @VonC:以前不知道的方法,谢谢!
  • 好的,我已经添加了一个答案,看看是否有效。

标签: tcp network-programming go


【解决方案1】:

这可能取决于您收到的 PDU 的确切性质,但this example 会查找大小,然后读取所有内容(使用io.ReadFul())。

func read(conn net.Conn, key string) string {
  fmt.Fprintf(conn, GenerateCommand(OP_GET, key))
  if verify(conn) {
    var size uint16
    binary.Read(conn, binary.LittleEndian, &size)
    b := make([]byte, size)
    // _, err := conn.Read(b)
    _, err := io.ReadFull(conn, b)
    if err == nil {
      return string(b)
    }
  }
  return ""
}

func verify(conn net.Conn) bool {
  b := make([]byte, 1)
  conn.Read(b)
  return b[0] == ERR_NO_ERROR
}

用于:

conn, err := net.Dial("tcp", ":12345")
if err != nil {
  t.Error(err)
}
write(conn, "foo", "bar")
if !verify(conn) {
  t.Error("Bad write!")
}
if r := read(conn, "foo"); r != "bar" {
  t.Errorf("Bad read! Got %v", r)
}

【讨论】:

  • 对不起,我想我的需要就像一个ReadFull,但读取可能会在读取大小字节之前返回。对吗?
  • @NanXiao 当然,您可以修改该代码以使用ReadFull。至于大小,它将始终被读取,因为您在尝试读取之前“验证”了conn。这就是verify() 的用途。
  • 但是net.Conn 不提供ReadFull,所以我使用循环来获取所有字节。
  • @NanXiao 您不应该:您应该只获取与您在conn 流开头读取的大小相对应的字节。
  • 是的。但是如果大小为30,则可以conn.Read只读取20个字节。对吗?
【解决方案2】:

在 golang-nuts 讨论这个问题后:How to read an integral network PDU? 代码应该是:

import "io"
......
pdu := make([]byte, pduLen)
io.ReadFull(conn, pdu) 

【讨论】:

  • 好的,我已将答案中的代码修改为使用io.ReadFull()
猜你喜欢
  • 1970-01-01
  • 2013-08-19
  • 1970-01-01
  • 2016-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-06
  • 1970-01-01
相关资源
最近更新 更多