sockets – Can golang net.Conn get notified when there is a new message?-ThrowExceptions

Exception or error:

What I try to implement

I’m implementing a client sending and receiving unixpackets and interacting with a server which was already implemented in C++.
So far, the client can receive and send packet to server successfully. But I used a for loop to keep calling Recv to see if there is a new message.

What I did

I implemented a Packet class so that the client and server can follow the same rules to encode and decode the message. Transport is a struct wrapping a net.Conn. Client is consisted of Transport, receiving Packet and sending Packet.
I used a for loop to keep calling Recv to see if there’s a new message arriving at 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
        }
    }
}

Question

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

How to solve:

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

No, not in the standard packages.

Read()ing from net.Conn in a loop is the usual way to detect message arrival. If you need to perform multiple concurrent tasks, you can encapsulate the loop in a goroutine writing to a channel and wrap a for-loop plus select around reading from that channel.

Outside of standard packages, you can explore event-driven IO with projects like https://github.com/tidwall/evio

Answer´╝Ü

net.conn.Read automatically blocks until data is available on the connection. You don’t have to loop to wait for data to arise.

Having a simple socket server (in python3 just to make the language barrier clear)

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()

I can make a golang client as such:

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()
}

Server side says:

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

Client side says:

$ go run t.go
Response:  thank you

net.Conn isn’t configured with a read timeout here so it’s willing to wait for as long as it takes.

Let me also mention this:

if err != nil {
    return false
}

This hides errors behind undistinguished ‘false’ responses. In the case of an unrecoverable error (server closed connection, let’s say) your client will loop indefinitely (chewing up a lot of CPU in the process). If you need to expose errors, return them. If you want to be lazy for proof of concept, panic() them if you must, but don’t return the same result for all of them or you won’t be able to handle individual error cases properly. In your case it’s kind of a moot point as you don’t need a for loop anyway.

Leave a Reply

Your email address will not be published. Required fields are marked *