At the heart of this example is a listener socket. It uses a built-in method:
onAccept: acceptSocket
to accept connections up to a given limit and add them to its connections
property. The built-in rule method monitorSocket will establish the socket to
listen for connections on the port property.
A rule with an interpreted method is used to monitor the addition
of a socket to the listener socket connections list. The action of this
is to assign the onReceive property of the newly created socket to a lambda function
which displays any received textual input.
Note how the newly accepted socket is customised dynamically. There is no need for a new type or subclass to achieve the desired functionality.
Finally, bytesToString is a built-in function with signature:
bytesToString::(ByteString -> String)
It simply converts a vector of bytes to a null terminated string. In practice, a more sophisticated function would be used to convert non-printable characters appropriately.
-------------------------------------------------------------------------------
--
-- Name: socketDemo.is
--
-------------------------------------------------------------------------------
--
-- Description:
--
-- Demonstration IvoryScript configuration file
--
-- This script defines a simple application to display any textual
-- input received on a listening socket.
--
-------------------------------------------------------------------------------
let acceptedSocketRuleMethod rule::Rule event::Expr _::ADS _::EventPhase =
case event of {
AddRefEvent client::Socket listener::Socket #connections ->
if listener::Ref = rule.listenerSocket then
client.onReceive := (\socket::Socket ->
show ((bytesToString (socket.receive)) ++ "\n"))
}
in
def {
.socketRule = .Rule [
method: monitorSocket];,
.commsStore =.ADS [
ruleList: !RefList[]];
.commsStore.listenerSocket = .commsStore.Socket [
port: 81,
mode: Listener,
onAccept: acceptSocket,
connectionLimit: 2,
connections: !RefList[]];
.acceptedSocketRule = .Rule [
listenerSocket: !.commsStore.listenerSocket,
method: acceptedSocketRuleMethod];
.startADSGroup = !RefList [.commsStore];
.start = mapProcRefs (raiseEvent (StartEvent 0)) (.startADSGroup)
};
addRef (.socketRule) (.commsStore) #ruleList;
addRef (.acceptedSocketRule) (.commsStore) #ruleList;
The following script implements the built-in function onAccept:
module SocketAcceptance where
{
include "prelude.is"
include "socket.is"
!acceptSocket socket::Socket =
let acceptedSocket::Socket = Socket[mode: Accepted,
port: !(socket.port::Int),
onClose: closeSocket] (host socket)
in
if (socket.accept) acceptedSocket then {
if ¬(hasProperty socket #connectionLimit) |
length ((socket.connections)::RefList) < (socket.connectionLimit) then {
trace "Accepting connection";
addRef acceptedSocket socket #connections
} else {
error "acceptSocket: connection limit exceeded";
destroyObject acceptedSocket
}
}
else {
error "acceptSocket: accept failed";
destroyObject acceptedSocket
}
};
!closeSocket socket::Socket =
case socket.mode of {
Accepted ->
let removeClient ref::Ref =
case ref of {
referrer::Socket ->
if hasProperty referrer #connections then
removeRef socket referrer #connections
}
in {
trace "Closing connection";
mapProcRefs removeClient (revRefs socket);
destroyObject socket
}
}
Last update: 11 October, 2005