如何在Go中为每个连接写入发送单独的数据包?

时间:2017-10-13 11:34:05

标签: sockets networking go tcp

问题

我想运行每秒大量请求的负载测试。我在Go中写了一个套接字发送器和一个接收器。发送方向端口7357发送大量数据包,每个数据包包含当前时间(以纳秒为单位)。接收方侦听端口7357并解析每条消息,计算延迟。

问题是,在阅读时,我会在一个conn.Read()中获得多个数据包。我理解这意味着我实际上每个数据包发送多个消息:每个conn.Write()都不发送套接字数据包,但它会等待一段时间,然后在发送之前与下一个(或接下来的几个)合并

问题

如何确保每个conn.Write()作为单独的数据包通过套接字单独发送? 注意:我不想重新发明TCP,我只想模拟每个发送邮件的多个外部实体的负载。

采取的步骤

我搜索了文档,但似乎没有conn.Flush()或类似的。我尝试过使用缓冲编写器:

writer := bufio.NewWriter(conn)
...
bytes, err := writer.Write(message)
err = writer.Flush()

没有错误,但我仍然在接收端收到混合数据包。我也尝试在每conn.Read()之后做一个假的conn.Write() 0字节,但它也没有工作。发送消息终止符(例如\r\n)似乎没有任何区别。最后,默认情况下禁用Nagle算法,但我已调用tcp.SetNoDelay(true)进行测量。

在Node.js中,我设法在每个setImmediate()之后使用socket.write()执行此操作:setImmediate()等待所有I / O完成后再继续。如何在Go中执行相同操作以便获取单独的数据包?

代码段

发送:

func main() {
  conn, _ := net.Dial("tcp", ":7357")
  defer conn.Close()
  for {
    timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
    conn.Write([]byte(timestamp))
    conn.Read(buff)
  }
}

接收:

func main() {
  listen, _ := net.Listen("tcp4", ":7357")
  defer listen.Close()
  for {
    conn, _ := listen.Accept()
    go handler(conn)
  }
}
func handler(conn net.Conn) {
  defer conn.Close()
  var buf = make([]byte, 1024)
  for {
    conn.Read(buf)
    data := string(buf[:n])
    timestamp, _ := strconv.ParseInt(data, 10, 64)
    elapsed := timestamp - time.Now().UnixNano()
    log.Printf("Elapsed %v", elapsed)
  }
}

为了清晰起见,已删除错误处理,但在实际代码中对其进行了彻底检查。它在第一次运行strconv.ParseInt()时崩溃,出现value out of range错误,因为它收到了很多合并的时间戳。

2 个答案:

答案 0 :(得分:2)

曾经有一条规则,在允许任何人编写任何使用TCP的代码之前,他们需要从内存中重复以下句子并解释它的含义:" TCP不是消息协议,它是一种可靠的字节流协议,不保留应用程序消息边界。"

除了使用TCP无法可靠地建议的解决方案之外,它不是减少延迟的解决方案。如果网络不堪重负,使用更多数据包发送相同数据只会使延迟变得更糟。

TCP是字节流协议。它提供的服务是一个字节流。周期。

您似乎想要一种适用于TCP的低延迟消息协议。大。设计一个并实现它。

获得低延迟的主要技巧是使用应用程序级别的确认。 TCP ACK标志将捎带到确认上,提供低延迟。

不要禁用Nagling。只有在您无法设计出适合使用TCP的协议时,才能实现这一目标。在非理想条件下,由于同样的原因,即使有可能,您提出的解决方案也是一个糟糕的主意,这会使延迟变得更糟。

但是必须设计和实现消息协议或使用现有协议。您的代码期望TCP(不是消息协议)以某种方式向其传递消息。那段时间不会发生。

  

如何确保每个conn.Write()作为单独的数据包通过套接字单独发送?注意:我不想重新发明TCP,我只想模拟每个发送消息的外部实体的负载。

即使你可以,也不会做你想做的事情。即使它们是以单独的数据包发送的,也不能保证另一方的read不会合并。如果要发送和接收消息,则需要一个TCP不支持的消息协议。

  

在Node.js中,我设法在每个socket.write()之后用setImmediate()完成这个技巧:setImmediate()在继续之前等待所有I / O完成。如何在Go中执行相同操作以便获取单独的数据包?

你可能已经改变了#34;发生了不工作"当我尝试它时,#14;碰巧工作了#34;但由于我已经解释过的原因,你永远无法使这项工作可靠而且你是一个傻瓜的差事。

如果您想发送和接收消息,则需要准确定义"消息"将是并编写代码来发送和接收它们。没有可靠的捷径。 TCP是字节流协议,句号。

如果您关心延迟和吞吐量,请设计优化的消息协议,以通过TCP进行分层,从而优化这些协议。不要禁用Nagle,因为需要Nagle来防止病理行为。只有当您无法更改协议并且使用未设计为在TCP之上进行分层的协议时,才应禁用它。禁用Nagle将一只手绑在背后,通过增加发送数据所需的数据包数量,在网络状况不佳的情况下导致延迟和吞吐量显着降低,即使没有任何意义。

您可能需要/需要应用程序级别的确认。这与TCP很好地协同工作,因为TCP ACK将依赖于应用程序级别的确认。

答案 1 :(得分:-1)

您可以在每次迭代时从套接字读取预定义的字节数,这可能会有所帮助,但您需要创建自己的协议,该协议将由您的应用程序处理。没有proto就不可能保证一切都能稳定运行,因为在接收器上你无法理解开始的位置和消息的结束位置。