2
\$\begingroup\$

I'm currently working on a linux daemon and would like some feedback. The program is a simple unix socket server that sends around gob encoded messages. I'm still getting the hang of concurrency in go and I'm not terribly confidant the socket server is passing around messages properly especially on shutdown. Hopefully, I'm not doing anything too egregious.

EDIT: I rewrote most of the server code after getting a burst of inspiration from this post. I fell back to a previous iteration that didn't use channels to communicate errors and connections. Instead it just uses a callback for errors in much the same way it does for regular messages. Finally, I then wrote units tests for the whole thing.

I'll remove the old code. Any feedback would still be appreciated.

server.go

package network

import (
    "encoding/gob"
    "errors"
    "net"
    "sync"
)

var (
    ErrNoMessageHandler = errors.New("server: message handler not specified")
)

type Options struct {
    Network string
    Address string
}

type Server struct {
    opts  *Options
    lis   net.Listener
    mh    func(interface{}) interface{}
    eh    func(error)
    quit  bool
    conns sync.WaitGroup
}

func NewServer(opts *Options) *Server {
    return &Server{opts: opts}
}

func (s *Server) OnMessage(handler func(interface{}) interface{}) {
    s.mh = handler
}

func (s *Server) OnError(handler func(error)) {
    s.eh = handler
}

func (s *Server) Listen() error {
    lis, err := net.Listen(s.opts.Network, s.opts.Address)
    if err != nil {
        return err
    }
    s.lis = lis
    return nil
}

func (s *Server) Network() string {
    return s.lis.Addr().Network()
}

func (s *Server) Address() string {
    return s.lis.Addr().String()
}

func (s *Server) Serve() error {
    if s.mh == nil {
        return ErrNoMessageHandler
    }
    for {
        conn, err := s.lis.Accept()
        if err != nil {
            if s.quit {
                return nil
            }
            return err
        }
        s.conns.Add(1)
        go s.handle(conn)
    }
}

func (s *Server) Close() error {
    s.quit = true
    err := s.lis.Close()
    s.conns.Wait()
    return err
}

func (s *Server) handle(conn net.Conn) {
    defer s.cleanup(conn)

    dec := gob.NewDecoder(conn)
    var req interface{}
    if err := dec.Decode(&req); err != nil {
        if s.eh != nil {
            s.eh(err)
        }
        return
    }

    res := s.mh(req)

    enc := gob.NewEncoder(conn)
    if err := enc.Encode(&res); err != nil {
        if s.eh != nil {
            s.eh(err)
        }
        return
    }
}

func (s *Server) cleanup(conn net.Conn) {
    conn.Close()
    s.conns.Done()
}

client.go

package network

import (
    "encoding/gob"
    "net"
)

type Client struct {
    opts *Options
}

func NewClient(opts *Options) *Client {
    return &Client{opts: opts}
}

func (c *Client) Send(req interface{}) (res interface{}, err error) {
    conn, err := net.Dial(c.opts.Network, c.opts.Address)
    if err != nil {
        return nil, err
    }
    defer conn.Close()

    enc := gob.NewEncoder(conn)
    err = enc.Encode(&req)
    if err != nil {
        return nil, err
    }

    dec := gob.NewDecoder(conn)
    err = dec.Decode(&res)
    if err != nil {
        return nil, err
    }

    return res, nil
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.