我在go中编写了一个简单的UDP服务器。

当我执行go run udp.go时,它会打印我发送给它的所有程序包。 但是,当运行go run udp.go > out时,它将在客户端停止时停止将stdout传递给out文件。

客户端是发送10k请求的简单程序。 因此,在文件中,我大约有50%的已发送软件包。 当我再次运行客户端时,out文件再次增大,直到客户端脚本完成。

服务器代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
 "net"

 "fmt"
)

func main() {
  addr, _ := net.ResolveUDPAddr("udp",":2000")
  sock, _ := net.ListenUDP("udp", addr)

  i := 0
  for {
    i++
    buf := make([]byte, 1024)
    rlen, _, err := sock.ReadFromUDP(buf)
    if err != nil {
      fmt.Println(err)
    }
    fmt.Println(string(buf[0:rlen]))
    fmt.Println(i)
    //go handlePacket(buf, rlen)
  }
}

这是客户端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import (
 "net"

 "fmt"
)

func main() {

  num := 0
  for i := 0; i < 100; i++ {
    for j := 0; j < 100; j++ {
      num++
      con, _ := net.Dial("udp","127.0.0.1:2000")
      fmt.Println(num)
      buf := []byte("bla bla bla I am the packet")
      _, err := con.Write(buf)
      if err != nil {
        fmt.Println(err)
      }
    }
  }
}
  • 您将忽略客户端和服务器中的几种可能的错误。 运行客户端时,它立即引发运行时错误。 添加错误检查后:"拨号udp 127.0.0.1:2000:打开的文件太多"。 如果不知道您的问题是否相关,但是Id要求您添加检查所有可能的错误的方法,并且如果这不能为您解决问题,请返回结果:)
  • 我添加了错误检查,没有任何此类问题。 客户端正常工作,我在另一台服务器上进行了测试。 似乎重定向使套接字读取时滞,并且丢失了某些数据包...。

如您所怀疑,由于UDP的性质,似乎UDP数据包丢失。由于UDP是无连接的,因此客户端不关心服务器是否可用或准备接收数据。因此,如果服务器正在忙于处理,它将无法处理下一个传入的数据报。您可以使用netstat -u(应包括UDP数据包丢失信息)进行检查。我遇到了同样的事情,服务器(接收端)无法跟上发送的数据包的速度。

您可以尝试两件事(第二个例子对我有用):

调用SetReadBuffer。确保接收套接字具有足够的缓冲以处理您向其抛出的所有内容。

1
2
sock, _ := net.ListenUDP("udp", addr)
sock.SetReadBuffer(1048576)

在go例程中进行所有数据包处理。尝试通过确保服务器在您希望接收数据时不忙于其他工作来每秒增加数据报。即将处理工作移至go例程,因此您无需按住ReadFromUDP()。

1
2
3
4
5
//Reintroduce your go handlePacket(buf, rlen) with a count param
func handlePacket(buf []byte, rlen int, count int)
        fmt.Println(string(buf[0:rlen]))
        fmt.Println(count)
}

...

1
go handlePacket(buf, rlen, i)

最后一种选择:

最后,可能不是您想要的,您在客户中睡个好觉,这会减慢速度并消除问题。例如

1
2
3
buf := []byte("bla bla bla I am the packet")
time.Sleep(100 * time.Millisecond)
_, err := con.Write(buf)
  • 附带说明,ReadFromUDP可以在go例程中完成,这有时可以简化处理并节省通道。

尝试在write语句后同步标准输出。

1
os.Stdout.Sync()
  • 我认为那不是问题。 IMO stdout在收到的每个换行符上都会刷新,或者应用程序通常使用exit退出。 请参见手册页。"流stdout指向终端时是行缓冲的。只有在调用fflush(3)或exit(3)或打印换行符时,部分行才会出现。"
  • "当它指向一个终端时",起作用的情况就是那个,而当它不存在时,不起作用的情况。 由于服务器永不退出,因此Stdout不会在退出时被刷新。