mudb
A database for real-time server-client applications.
A mudb application has one MuServer and several MuClients. Each node consists of one or more protocols which define different behaviors.
A protocol is essentially a collection of event handlers. To define a protocol, typically you need to specify
- a protocol schema
- a server protocol
- a client protocol
example
Here is a minimal chat room demo, heavily documented to show how to define a protocol using mudb.
schema.js
The first step of creating any applications with mudb is to specify a protocol schema using muschema, defining the data structures of different messages that all parties agree on.
var muschema = require('muschema')
var MuStruct = muschema.MuStruct
var MuString = muschema.MuString
exports.ChatSchema = {
client: {
chat: new MuStruct({
name: new MuString(),
text: new MuString(),
}),
},
server: {
say: new MuString(),
},
}
server.js
function randomName () {
return Math.random().toString(36).substr(2, 11)
}
module.exports = function (server) {
var serverProtocol = server.protocol(require('./schema').ChatSchema)
var clientNames = {}
serverProtocol.configure({
message: {
say: function (client, content) {
serverProtocol.broadcast.chat({
name: clientNames[client.sessionId],
text: content,
})
},
},
connect: function (client) {
clientNames[client.sessionId] = randomName()
serverProtocol.broadcast.chat({
name: 'server',
text: clientNames[client.sessionId] + ' joined the channel',
})
},
disconnect: function (client) {
serverProtocol.broadcast.chat({
name: 'server',
text: clientNames[client.sessionId] + ' left',
})
delete clientNames[client.sessionId]
},
})
server.start()
}
client.js
module.exports = function (client) {
var messageDiv = document.createElement('div')
var textLabel = document.createElement('label')
var textInput = document.createElement('input')
var clientProtocol = client.protocol(require('./schema').ChatSchema)
messageDiv.style.overflow = 'auto'
messageDiv.style.width = '400px'
messageDiv.style.height = '300px'
textLabel.textContent = 'message: '
textInput.type = 'text'
textInput.style.width = '400px'
textInput.style.padding = '0px'
textInput.style.margin = '0px'
document.body.appendChild(messageDiv)
document.body.appendChild(document.createElement('br'))
document.body.appendChild(textLabel)
document.body.appendChild(textInput)
clientProtocol.configure({
ready: function () {
textInput.addEventListener('keydown', function (ev) {
if (ev.keyCode === 13) {
clientProtocol.server.message.say(textInput.value)
textInput.value = ''
}
})
},
message: {
chat: function (data) {
var name = data.name
var text = data.text
var textNode = document.createTextNode(name + ": " + text)
messageDiv.appendChild(textNode)
messageDiv.appendChild(document.createElement('br'))
}
}
})
client.start()
}
To run the demo,
- cd into the directory containing the three files above
npm i mudo
mudo --socket websocket --open
table of contents
1 install
npm i mudb muweb-socket mulocal-socket
2 api
2.1 interfaces
Purely instructive types used to describe the API:
TableOf<T>: { [messageName:string]:T } | {}
ProtocolSchema: { server:TableOf<AnyMuSchema>, client:TableOf<AnyMuSchema> }
DispatchFn: (data, unreliable?:boolean) => undefined
SendRawFn: (data:Uint8Array|string, unreliable?:boolean) => undefined
ClientMessageHandler: (client:MuRemoteClient, data, unreliable?:boolean) => undefined
ServerMessageHandler: (data, unreliable:boolean) => undefined
To create a mudb application, a socket (i.e. a MuSocket instance) and a corresponding socket server (i.e. a MuSocketServer instance) are required. We try to make mudb extensible by allowing you to use a customized socket-server implementation.
A few socket modules are provided out of the box alongside mudb (e.g. muweb-socket). Make sure that you have checked those out before trying to create your own socket modules.
2.1.1 MuSocket
MuSockets are bidirectional sockets. They support both reliable, ordered streams and unreliable optimistic packet transfer. Any mudb sockets should implement the MuSocket interface as described below.
2.1.1.1 sessionId:string
Required property, a unique session id identifying the socket
2.1.1.2 open:boolean
Required property, a flag determining whether the socket is open
2.1.1.3 start(spec)
Required method, can be used to establish a connection to the server from the client side
spec:{ ready, message, close }
ready() should be called when the connection is ready
message(data:Uint8Array|string, unreliable:boolean) should be called when receiving messages
close(error?) should be called when the connection is closed
2.1.1.4 send(data:Uint8Array|string, unreliable?:boolean)
Required method, used to send messages to the other end of the connection
2.1.1.5 close()
Required method, used to close the connection
2.1.2 MuSocketServer
A MuSocketServer handles client communications coming through the corresponding MuSockets. Any mudb socket servers should implement the MuSocketServer interface as described below.
2.1.2.1 clients:MuSocket[]
Required property, server-side mocks of connected clients
2.1.2.2 open:boolean
Required property, a flag determining whether the socket server is running
2.1.2.3 start(spec)
Required method, used to launch the socket server
spec:{ ready, connection, close }
ready() should be called when the socket server is launched
connection(client:MuSocket) should be called when a client connects
close(error?) should be called when the socket server is closed
2.1.2.4 close()
Required method, used to shut down the socket server
2.2 MuServer(socketServer:MuSocketServer)
var httpServer = require('http').createServer()
var socketServer = new require('muweb-socket').MuWebSocketServer(httpServer)
var muServer = new require('mudb').MuServer(socketServer)
2.2.1 protocol(schema:ProtocolSchema) : MuServerProtocol
Adds a server protocol, and returns it to be configured.
A MuServer can have multiple protocols. Note that you cannot add any new protocols after the server is started.
2.2.2 start(spec?)
Launches server.
spec:{ ready?, close? }
ready(): called when the underlying socket server is launched
close(error?): called when the underlying socket server is closed
2.2.3 destroy()
Shuts down the underlying socket server and terminates all clients. Useful when having multiple instances of mudb.
2.3 MuServerProtocol
2.3.1 broadcast:TableOf<DispatchFn>
An object of methods, each of which broadcasts to all connected clients. Each message (delta encoded) will be handled by a specific handler. For example, the message sent by protocol.broadcast.shower(shampoo, true) will be handled by the shower method on the corresponding client protocol, as shower(shampoo, true). So this is effectively dispatching method calls to a remote object.
broadcastRaw:SendRawFn
A method that broadcasts to all connected clients with "raw" (not processed, contrary to delta) messages. The messages will be handled by the raw message handler of the corresponding client protocol.
2.3.2 configure(spec)
Each protocol should be configured before the server is started and can be configured only once, by specifying the event handlers in spec.
spec:{ message, ready?, connect?, raw?, disconnect?, close? }
message:TableOf<ClientMessageHandler> required
ready() called when the underlying socket server is launched
connect(client:MuRemoteClient) called when a client connects
raw(client:MuRemoteClient, data:Uint8Array|string, unreliable:boolean) called when a "raw" message is received
disconnect(client:MuRemoteClient) called when a client disconnects
close() called when the underlying socket server is closed
2.4 MuRemoteClient
A MuRemoteClient is the server-side representation of a client, used in the event handlers.
2.4.1 sessionId
A string representing a unique session id identifying the client.
2.4.2 message:TableOf<DispatchFn>
An object of methods, each of which sends messages (delta encoded) to the corresponding client.
2.4.3 sendRaw:SendRawFn
A method that sends "raw" messages to the corresponding client.
2.4.4 close()
Closes the reliable socket.
2.5 MuClient(socket:MuSocket)
var socket = new require('muweb-socket/socket').MuWebSocket(spec)
var muClient = new require('mudb').MuClient(socket)
2.5.1 protocol(schema:ProtocolSchema) : MuClientProtocol
Adds a client protocol, and returns it to be configured.
A MuClient can have multiple protocols. Note that you cannot add any new protocols after the client is started.
2.5.2 start(spec?)
Runs client.
spec:{ ready?, close? }
ready(error?:string) called when the client is ready to handle messages
close(error?:string) called when all sockets are closed
2.5.3 destroy()
Closes all sockets.
2.6 MuClientProtocol
2.6.1 server:MuRemoteServer
The client-side representation of the server.
2.6.2 configure(spec)
Each protocol should be configured before the client is started and can be configured only once, by specifying the event handlers in spec.
spec:{ message, ready?, raw?, close? }
message:TableOf<ServerMessageHandler> required
ready() called when the client is ready to handle messages
raw(data:Uint8Array|string, unreliable:boolean) called when receiving a "raw" message
close() called when all sockets are closed
2.7 MuRemoteServer
2.7.1 message:TableOf<DispatchFn>
An object of methods, each of which sends specific messages (delta encoded) to the server.
sendRaw:SendRawFn
A method that sends "raw" messages to the server.
3 usage tips
4 helpful modules
5 more examples
6 TODO
credits
Development supported by Shenzhen DianMao Digital Technology Co., Ltd.
Written in Shenzhen, China.
(c) 2017 Mikola Lysenko, Shenzhen DianMao Digital Technology Co., Ltd.