golang net.Conn 有新消息可以通知吗?

Can golang net.Conn get notified when there is a new message?

我尝试实现的

我正在实现一个发送和接收 unix 数据包并与已经用 C++ 实现的服务器交互的客户端。
至此,客户端可以成功接收和向服务器发送数据包。但是我用了一个for循环,一直调用Recv看有没有新消息

我做了什么

我实现了一个数据包class,这样客户端和服务器就可以按照相同的规则对消息进行编码和解码。 Transport 是一个包装 net.Conn 的结构体。客户端由传输、接收数据包和发送数据包组成。
我使用 for 循环 继续调用 Recv 以查看是否有新消息到达 net.Conn.

type Packet struct {
    Length      uint32
    Data        []byte
    ReadOffset  uint32
    WriteOffset uint32
}

type Transport struct {
    url   string
    conn  net.Conn
}

// connect to url
func (transport *Transport) Dial(url string) error {
    c, err := net.Dial("unixpacket", url)
    if err != nil {
        return err
    }
    transport.url = url
    transport.conn = c
    return nil
}

// sending Packet
func (transport *Transport) Send(p *Packet) bool {
    readBuffer := (p.Data)[p.ReadOffset : p.WriteOffset]
    _, err := transport.conn.Write(readBuffer)
    if err != nil {
        return false
    }
    return true
}

// receiving Packet
func (transport *Transport) Recv(p *Packet) bool {
    writeBuffer := (p.Data)[p.WriteOffset :]
    _, err := transport.conn.Read(writeBuffer)
    if err != nil {
        return false
    }
    return true
}

type Client struct {
    Transport  *Transport
    RecvPacket *Packet
    SendPacket *Packet
}

// keep checking transport
func (c *Client) ReadFromTransport() {
    for {
        time.Sleep(20 * time.Millisecond)
        if c.Transport.Recv(c.RecvPacket) == false {
            continue
        }
    }
}

问题

有没有一种方法可以让 net.Conn 在新消息到达时得到通知而不是使用 for 循环?

net.conn.Read 自动阻塞,直到连接上的数据可用。您不必循环等待数据出现。

有一个简单的套接字服务器(在python3中只是为了消除语言障碍)

import socket, os, sys

addr='/tmp/a_unix_socket'

if os.path.exists(addr):
  os.unlink(addr)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)

sock.bind(addr)
sock.listen(1)

while True:
  print("Waiting for connection", file=sys.stderr)
  conn, client_addr = sock.accept()
  data =conn.recv(128)
  print("Recived {}".format(data))
  conn.sendall(b'thank you')
  conn.close()

我可以这样制作一个golang客户端:

package main

import (
  "net"
  "fmt"
)

func main() {
  conn, err := net.Dial("unix", "/tmp/a_unix_socket")
  if err != nil {
    panic(err)
  }
  conn.Write([]byte("here is data"))
  result := make([]byte, 128)
  _, err = conn.Read(result)
  if err != nil {
    panic(err)
  }
  fmt.Println("Response: ", string(result))
  conn.Close()
}

服务器端说:

$ python3 ./t.py
Waiting for connection
Recived b'here is data'
Waiting for connection

客户端说:

$ go run t.go
Response:  thank you

net.Conn 这里没有配置读取超时,所以它愿意等待,只要它需要。

让我也提一下:

if err != nil {
    return false
}

这隐藏了未区分的 'false' 响应背后的错误。如果发生不可恢复的错误(假设服务器关闭连接),您的客户端将无限期循环(在此过程中消耗大量 CPU)。如果您需要公开错误,return 它们。如果你想懒惰地进行概念验证,panic() 如果必须的话,但不要 return 对所有这些结果都相同,否则你将无法正确处理个别错误情况.在您的情况下,这是一个有争议的问题,因为您无论如何都不需要 for 循环。

Is there a method to have the net.conn get notified when a new message arriving net.conn instead of using a for loop?

不,不在标准包中。

在循环中从 net.Conn 读取()是检测消息到达的常用方法。如果您需要执行多个并发任务,您可以将循环封装在写入通道的 goroutine 中,并包装一个 for 循环加上 select 围绕从该通道读取。

在标准包之外,您可以通过 https://github.com/tidwall/evio

等项目探索事件驱动的 IO