simple-server-0.0.3: Simple Server interface

Safe HaskellNone




The goal of SimpleServer, as its name implies, is to make it easy to build simple message passing servers by puting a layer between the programmer and the concurrent operations between it and the network layer connecting it to multiple clients.


Type Synonyms

To start using simple server, the programmer simply needs to define the callbacks that occur when a client connects, disconnects, or sends a message to the server. These three callbacks have type synonyms defined: ConnectionHandler, DisconnectHandler, and CmdHandler.

To create a ConnectionHandler that notifies all clients that a new client has connected and respond to the new client with a simple welcome message one might define the following:

simpleConnect :: ConnectionHandler
simpleConnect server client = do
  broadcast server "A new user has joined."
  respond client "Welcome!"

When a connection is received, a new ClientConn is created and added to the servers clientList. At this point, the ConnectionHandler is notified and a message is broadcasted to all connected clients. After this occurs, the server responds to the new client.

To create a DisconnectHandler that notifies all clients that a user has disconnected and send a goodbye message to the disconnecting client, one might define the following:

simpleDisconnect :: DisconnectHandler
simpleDisconnect server client = do
  broadcast server "A user has left the room."
  respond client "Goodbye!"

When a user disconnects, the server will call the DisconnectHandler so any cleanup may be done.

To create a CmdHandler that repeats a received message to all connected clients, one might define the following:

repeatHandler :: CmdHandler
repeatHandler (cmd:msg) server client = do
  name <- lookup client "name"
  broadcast server $ name ++ >  ++ (unwords msg)

The message CmdHandler is passed a list of strings which is the message received from the client. The first element of the list is the "command" that triggered the callback.

type ConnectionHandler = Server -> ClientConn -> IO ()Source

Each server has one ConnectionHandler that is called each time a client connects to the server.

type DisconnectHandler = Server -> ClientConn -> IO ()Source

A DisconnectHandler is called each time a client is disconnected from the server.

type CmdHandler = [String] -> Server -> ClientConn -> IO ()Source

A server may have any number of CmdHandlers. When a CmdHandler is called it is passed a list of strings representing the message the server received, the server that received it, and the client that send the message. The first part element of the list is the string that triggered the CmdHandler.

Server Construction

To start using a simple server, one simply specifies the ConnectionHandler, DisconnectHandler, and port that the server will use.

 server <- new simpleConnect simpleDisconnect 10010

The server does not start after construction, instead one may now register any number of CmdHandlers on the server using addCommand.

 addCommand server "/repeat" repeatHandler

Once all of the desired CmdHandlers have been registered on the server the server may be started using start

 start server

If the port specified for the server is available, a new thread is started and the server will now accept incoming connections. The control will then be returned to the thread that called start.

The incomming connections will be handed off to the specified ConnectionHandler and incoming messages will be handed to the appropriate CmdHandler. If an incoming message has no CmdHandler the message "Invalid command: cmd" will be sent to the client. If a client does not communicate with the server for more than 60 seconds the client is disconnected from the server.

To stop a server use stop

 stop server

All client buffers are flushed and disconnected from the server. All threads and handles that have been created by the server are killed and closed and then the control is returned to the thread that called stop.

data Server Source

A Simple Server

new :: ConnectionHandler -> DisconnectHandler -> Int -> IO ServerSource

Creates a new server with the specified ConnectionHandler and DisconnectHandler. On a call to start, the server will attempt to connect on the specified Port. If a client does not talk to a server for more than 60 seconds it will be disconnected.

addCommand :: Server -> String -> CmdHandler -> IO ()Source

Given a server, a command, and a command handler, adds the command to the server. If the command already exists, it will be overwritten.

start :: Server -> IO ()Source

Starts a server if it is currently not started. Otherwise, does nothing. The server will be started on a new thread and control will be returned to the thread that called this function.

stop :: Server -> IO ()Source

Stops a server if it is running sending a disconnect message to all clients and killing any threads that have been spawned. Otherwise, does nothing. Any shutdown operations should be run before this is called.

Client Interaction

Each time a connection is made to a server a new ClientConn is created and added to the servers clientList. The server then invokes a call to its ConnectionHandler.

Each ClientConn has a unique cid and property table which can be accessed with lookup and modified with modify. Each stored property is defined by a String and stores an associated String. If a property has never been set using modify it will be the empty string on lookup.

If you wanted to write a CmdHandler that allowed a user to specify the value stored in their table called "name", one might define:

nameHandler :: CmdHandler
nameHandler (_:msg) server client = do
  case msg of
    [] -> respond client "You did not provide a name to change to."
    msg -> do
      before <- lookup client "name"
      let name = unwords msg
          message = before ++ " is now known as " ++ name
      modify client "name" name
      broadcast server message

This CmdHandler first checks that the client specified a name that was not entirely white space. Then, it generates a message stating the name they were using and the name they are now using with lookup. Then using modify the clients property "name"is changed to the value specified. Finally, the generated message is broadcast to all connected clients.

data ClientConn Source

Describes a Clients connection and provides an interface for storing data associated with the client. Each client will be given a unique cid and are Eq if their cid's are Eq.

A ClientConn comes packaged with two functions for storing additional information in Strings, lookup and modify. The lookup function takes a key and returns the current value of the key or the empty string if it has never been set. The modify function takes a key and value and updates it such that the next call to lookup with that key will return the value provided.


cid :: ClientConn -> IntegerSource

The Unique ID for this client

lookup :: ClientConn -> String -> IO StringSource

Looks up a property for this client. By default, all properties are the empty string.

modify :: ClientConn -> String -> String -> IO ()Source

Modifies a client property. Given a property string, and a value string, the next call to lookup for the given property will result in the value.

respond :: ClientConn -> String -> IO ()Source

Adds a message to the clients message queue to be handled eventually.

broadcast :: Server -> String -> IO ()Source

Adds a message to all clients message queue to be handled eventually.

disconnect :: ClientConn -> IO ()Source

Disconnects the client if they are still connected to the server.

clientList :: Server -> IO [ClientConn]Source

Returns a list of all clients that are currently connected to the server