serialserver

package
v0.0.0-...-7847555 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2018 License: CC0-1.0 Imports: 20 Imported by: 0

Documentation

Overview

Package serialserver provides a server for synchronous client-server communication.

This work is licensed under CC0 1.0 Universal (CC0 1.0), Public Domain Dedication. For details see:

http://creativecommons.org/publicdomain/zero/1.0/

Online go-documentation is available at:

https://godoc.org/bitbucket.org/pcas/serverutil/serial/serialserver

The basic communication protocol is described in package bitbucket.org/pcas/serverutil/serial (https://godoc.org/bitbucket.org/pcas/serverutil/serial). In summary:

  1. client sends a task code specifying the task for the server to perform;

  2. client sends message data (implementation specific, encoded as described in https://godoc.org/bitbucket.org/pcas/serverutil/serial);

  3. server sends a response code, which is one of: - serverutil.OK - serverutil.Error - serverutil.Goodbye

    4a. if the response code is serverutil.OK, server sends response data (implementation-specific, encoded as described in https://godoc.org/bitbucket.org/pcas/serverutil/serial);

    4b. if the response code is serverutil.Error, server sends a serial.ErrorCode describing the error;

    4c. if the response code is serverutil.Goodbye then the server is closing the connection without handling the task.

The server is free to silently close the connection at any time.

To get a new server, do something like:

  S := serialserver.New(&serialserver.Timeout{
	Wait:  30 * time.Minute,
	Read:  5 * time.Second,
	Write: 5 * time.Second,
  })

where Wait, Read, and Write are, respectively, the time.Duration timeout to use whilst waiting for a new task, the rolling read timeout once a task has started, and the rolling write timeout.

You then need to register a task function of type TaskFunc for each task code that the server could be passed. You do this by calling S.Register(t,f) where t is the serverutil.TaskCode and f is the TaskFunc.

The task function f will be called whenever the task code received by the server in step 1 above is t. The task function is passed a binaryutil.BinaryReader 'in' that contains the message data (if any) sent by the client in step 2. The reader 'in' will automatically decode the message data; in particular, the user never needs to encode or decode data, and 'in' will automatically return io.EOF at the end of the message data. The client should return a io.Reader 'out', or an error. Any response data should be written to the returned io.Reader 'out'; this response data will be sent by the server as in step 4a. The contents of 'out' will automatically be encoded before being sent to the server; the user never needs to encode data.

If a non-nil error err is returned by the task function then the reader 'out' will be ignored. If err satisfies the the serverutil.ErrorCoder interface, then a serverutil.Error response code will be sent to the client, followed by the err.ErrorCode(), as in step 4b. If the returned error does not satisfy the serverutil.ErrorCoder interface then the situation is serious: the client will be placed in an error state, and the connection will be dropped.

This task function is passed a context.Context ctx that will be cancelled on server shutdown, and which could also for example contain session-specific data such as username or authentication credentials (these would have been negotiated during a handshake, which takes place outside the serialclient/serialserver framework). Any long-running or blocking tasks should make use of this context. The task function is also provided with a log.Interface logger lg, to which it should write useful feedback.

Once all task functions have been registered, the server can be used as follows:

// Listen on the port
listener, err := net.Listen("tcp", ":"+port)
if err != nil {
	// Handle the error
	// ...
}
defer listener.Close()
// Make a context to cancel everything
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Register the task handlers (see the discussion above)
// ...
// Listen for connections
for {
	conn, err := listener.Accept()
	if err != nil {
		// Handle the error
		// ...
	}
	// Do any handshake required and establish the byte
	// order e being used by the client
	// ...
	e := binary.BigEndian
	// Call the connection handler; updatedCtx is the
	// context ctx along with with any session-specific
	// data provided during the handshake.
	s.HandleConnection(updatedCtx, conn, e, args...)
}

Any variadic arguments 'args' passed to HandleConnection will be passed to any task functions called for this connection. This provides a way of passing in session-specific data without attaching them to the context.

See package bitbucket.org/pcas/logger/cmd/logd (https://godoc.org/bitbucket.org/pcas/logger/cmd/logd) for a real-world example of a server that uses the serialserver package.

Package serialserver provides a server for basic, synchronous client-server communication.

This work is licensed under CC0 1.0 Universal (CC0 1.0), Public Domain Dedication. For details see:

http://creativecommons.org/publicdomain/zero/1.0/

Online go-documentation is available at:

https://godoc.org/bitbucket.org/pcas/serverutil/serial/serialserver

/////////////////////////////////////////////////////////////////////// server.go ///////////////////////////////////////////////////////////////////////

A Server listens for tasks on a specified connection and responds appropriately, with cancellation provided by a context ctx. So typical usage looks like:

ln, err := net.Listen("tcp", ":8080")

if err != nil {
	// handle error
}

s := serialserver.New(timeouts)

for acceptingConnections {
	conn, err := ln.Accept()
	if err != nil {
		// handle error
	}
	// Typically some form of client handshake code goes here; this provides an
	// opportunity to determine the byte order e being used by the client.
	// ...
	// Handle the connection
	go s.HandleConnection(ctx, conn, e)
}

Here timeouts describes the *serialserver.Timeout durations (and can be nil, in which case the default timeouts will be used), and ctx is a context.Context that can be used to terminate the client connection. Typically you will install a signal handler which, on catching a signal that indicates we should shut down, sets acceptingConnections to false and calls the cancel function for the context ctx.

Index

Constants

View Source
const (
	DefaultWait  = 30 * time.Minute
	DefaultRead  = 5 * time.Second
	DefaultWrite = 5 * time.Second
)

The default timeout durations.

Variables

View Source
var (
	ErrGracefulDisconnect    = errors.New("Client requested to disconnect")  // ErrGracefulDisconnect indicates that a graceful disconnection was used to close the client connection.
	ErrTimeoutWaitingForTask = errors.New("Timeout whilst waiting for task") // ErrTimeoutWaitingForTask indicates that the server timed-out whilst waiting for a new task from the client.
)

Common errors.

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client describes a client.

func (*Client) ByteOrder

func (c *Client) ByteOrder() binary.ByteOrder

ByteOrder returns the byte order used in communication with the client.

func (*Client) Cancel

func (c *Client) Cancel()

Cancel cancels the current task. If no task is currently executing, or if the task has already been cancelled, this does nothing.

func (*Client) Close

func (c *Client) Close() error

Close is equivalent to Disconnect(0) and always returns nil.

func (*Client) Disconnect

func (c *Client) Disconnect(d time.Duration)

Disconnect begins a graceful disconnection. The connection will Close after duration d has elapsed, or as soon as the current task has finished being handled (whichever happens first). If Disconnect is called again, then the final Close will be performed after the shortest of the two durations has expired. This is a non-blocking method.

func (*Client) LocalAddr

func (c *Client) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Client) Log

func (c *Client) Log() log.Interface

Log returns the logger for the client.

func (*Client) Metadata

func (c *Client) Metadata() *serial.TaskMetadata

Metadata returns the metadata for the current task, or nil if no task is currently assigned.

func (*Client) Metrics

func (c *Client) Metrics() metrics.Interface

Metrics returns the metrics endpoint for the client.

func (*Client) RemoteAddr

func (c *Client) RemoteAddr() net.Addr

RemoteAddr returns the remote network address.

func (*Client) Server

func (c *Client) Server() *Server

Server returns the server handeling this client.

func (*Client) SetLogger

func (c *Client) SetLogger(lg log.Interface)

SetLogger sets the logger for this client. By default the logger is inherited from the server.

func (*Client) SetMetrics

func (c *Client) SetMetrics(m metrics.Interface)

SetMetrics sets the metrics endpoint for this client. By default this is inherited from the server.

func (*Client) String

func (c *Client) String() string

String returns a string description of the client of the form "remote addr->local addr".

func (*Client) TaskCodes

func (c *Client) TaskCodes() []serverutil.TaskCode

TaskCodes returns a slice of all assigned tasks codes.

func (*Client) Timestamp

func (c *Client) Timestamp() time.Time

Timestamp returns the connection timestamp for the client.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server handles serial communication with clients.

func New

func New(timeout *Timeout) *Server

New creates a new server. If timeout is nil then the default timeout durations will be used. This satisfies the serverutil.Server interface.

func (*Server) Clients

func (s *Server) Clients() []serverutil.Client

Clients returns all client connections currently being handled by this server. The returned slice is sorted by increasing timestamp. Note: The underlying type of the entries in the returned slice is *Client.

func (*Server) Disconnect

func (s *Server) Disconnect(d time.Duration)

Disconnect calls the Disconnect method for each client currently being handled by this server.

func (*Server) HandleConnection

func (s *Server) HandleConnection(ctx context.Context, conn net.Conn, e binary.ByteOrder, args ...interface{}) error

HandleConnection handles tasks from the given connection conn using the given byte order e. Use the given context to trigger a graceful disconnection. The connection will be closed on return. The given args will be passed to the task functions.

func (*Server) Log

func (s *Server) Log() log.Interface

Log returns the current logger for this server.

func (*Server) Metrics

func (s *Server) Metrics() metrics.Interface

Metrics returns the current metrics endpoint for this server.

func (*Server) NumClients

func (s *Server) NumClients() (n int)

NumClients returns the number of client connections currently being handled by this server.

func (*Server) Register

func (s *Server) Register(t serverutil.TaskCode, f TaskFunc, name string)

Register registers a task function for the given task code. If a function is already registered for this task code, it will be replaced with the new function. The given name is used purely for logging purposes, and will be ignored if empty. This will panic if the task code is in the reserved range.

func (*Server) RegisteredTaskCodes

func (s *Server) RegisteredTaskCodes() []serverutil.TaskCode

RegisteredTaskCodes returns a slice of all currently registered task codes.

func (*Server) SetLogger

func (s *Server) SetLogger(l log.Interface)

SetLogger sets the logger.

func (*Server) SetMetrics

func (s *Server) SetMetrics(m metrics.Interface)

SetMetrics sets the metrics endpoint.

func (*Server) TaskFunc

func (s *Server) TaskFunc(t serverutil.TaskCode) (TaskFunc, string, bool)

TaskFunc returns the task function and name associated with the task code. The final return value is true iff a task function is associated with the task code.

func (*Server) Wait

func (s *Server) Wait()

Wait blocks until no client connections are being handled by this server.

type TaskFunc

type TaskFunc func(ctx context.Context, c *Client, in binaryutil.BinaryReader, lg log.Interface, args ...interface{}) (out io.Reader, err error)

TaskFunc is the function that will run a task.

The message data sent by the client can be read from the binaryutil.BinaryReader 'in'. The contents of 'in' are automatically decoded as per the definition of client-server communication; the task function does not need to handle message data decoding. The response data (if any) should be written to the returned io.Reader 'out'. The contents of 'out' will be automatically encoded as per the definition of client-server communication before being sent to the client; the task function does not need to handle response data encoding. The variadic arguments 'args' are the arguments provided to the server's HandleConnection method when the client connected. Potentially long-running or blocking tasks should make use of the context ctx to allow the task to be cancelled. The task function should provide useful feedback via the logger lg.

If no error is returned (i.e. if err == nil), then success header

  • response code serverutil.OK (1 byte)

will be sent to the client, followed by the contents of the returned io.Reader 'out'. If the copying of data from 'out' to the client fails because of an error then the situation is serious: the client will be placed in an error state, and the connection will be dropped. If 'out' satisfies the io.Closer interface then its Close() method will be called once copying stops (for whatever reason). If you do not need to send any response data to the client, it is permitted for 'out' to be nil.

If a non-nil error is returned then the reader 'out' will be ignored. If err satisfies the the serverutil.ErrorCoder interface, then

  • response code serverutil.Error (1 byte)
  • error code (1 byte)

will be sent to the client, where the error code is given by err.ErrorCode(). If the returned error does not satisfy the serverutil.ErrorCoder interface then the situation is serious: the client will be placed in an error state, and the connection will be dropped.

type Timeout

type Timeout struct {
	Wait  time.Duration // Waiting for a new task.
	Read  time.Duration // Reading task data.
	Write time.Duration // Writing task data.
}

Timeout describes the various timeout durations when communicating with a client.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL