Contents

Vibe.d Websockets Tutorial

This tutorial is extended from here. Instead of a simple counter, this provides a functioning web chat application.

WebSockets overview

WebSocket: First and foremost, a WebSocket starts with a secret WebSocket handshake for you. This secret handshake basically allows us to treat our previous HTTP(S) connection as a stateful connection. Having a stateful connection allows us to do things previously unavailable on our webpage. This connection could handle something from the simple chat client in this example, to an entire realtime video game. Additionally, the entire connection is handled over port 80, which is generally open, so there's little concern there. As of May 16, 2015, all major browsers support the upgrade request required to establish the socket connection.

NOTE The URI Scheme changes from http/https to ws/wss respectively. Without this, you're not using websockets.

Vibe API

Vibe handles everything within vibe.http.websockets. When you implement a Websocket listener you need to specify scope. This is because you're dealing with a single websocket.

The main methods you use on a websocket are:

read(): Read incoming data from the connection.

send(): Send data to the connection.

In the example we send all our data as text.

Code

As with any code example, you've probably skipped the text above, and just want a quick example of how things work. This is perfectly fine, just read the javascript function connect() and handleWebSocketConnection() from the d code.

Files

index.html

<!DOCTYPE html><html><head><title>WebSockets Client</title><style>#chatLog{height:400px;padding-bottom:20px;overflow-y:scroll;}</style></head><body><h1>WebSockets Client</h1><divid="chatLog"></div><!-- this is where we'll insert chat into --><divid="login"><!-- {which: 13} is a hack to make the button function the same as onkeypress --><inputid="name"type="text"placeholder="Your Name"onkeypress="startConnection(event);"/><buttonid="connect"onclick="startConnection({which: 13});">Connect</button></div><divid="chat"style="display:none;"><inputid="text"type="text"placeholder="press enter to submit"onkeypress="chat(event);"/><buttonid="send"onclick="chat({which: 13});">Send</button><br/><buttonid="disconnect"onclick="endConnection();">Disconnect</button></div><scriptsrc="/scripts/websocket.js"></script></body></html>

websocket.js

// Keep socket variable at global levelvarsocket={};functionconnect(name){try{// Test if websocket doesn't exist, and if not, create a new connectionif(!('readyState'insocket)){socket=newWebSocket(getBaseURL()+'/ws');}socket.onopen=function(){try{socket.send(name);// Tell the server who you are, handle validation server side!// Swap the chat and login divs arounddocument.getElementById('name').value='';document.getElementById('login').style.display='none';document.getElementById('chat').style.display='block';document.getElementById('text').focus();}catch(exception){alert(exception);}}socket.onmessage=function(msg){varmsgVal=JSON.parse(msg.data);// We're anticipating messages formatted as "{'name':'Csmith1991', 'text':'example'}"varchatLog=document.getElementById('chatLog');chatLog.innerHTML+='<p>'+msgVal.name+': '+msgVal.text+'</p>';// Add to the chatLogchatLog.scrollTop=chatLog.scrollHeight;// Scroll chatLog to bottom}socket.onclose=function(){socket={};// Remove socket connection// Swap the chat and login divs around. Note we don't do this until the connection has closed.document.getElementById('chat').style.display='none';document.getElementById('login').style.display='block';document.getElementById('name').focus();}}catch(exception){alert(exception);}}functionstartConnection(event){// 13 = enter buttonif(event.which===13||event.keyCode===13){// Connect to WebSocket serverconnect(document.getElementById('name').value);}}functionchat(event){if(event.which===13||event.keyCode===13){varmyObj=document.getElementById('text');socket.send(myObj.value);myObj.value='';myObj.focus();}}functionendConnection(){// We want to close the connection on the server, so we create a message that the server listens for to close onsocket.send('/close');}functiongetBaseURL(){// Get the WebSocket server address e.g. ws://127.0.0.1:8080varhref=window.location.href.substring(7);// strip "http://"varidx=href.indexOf('/');return'ws://'+href.substring(0,idx);}

app.d

modulewebSocketExample;importvibe.d;importvibe.utils.array;privateWebSocket[]sockets;sharedstaticthis(){/// Use /ws to identify websocket requests, serve files out of the public folder otherwiseautorouter=newURLRouter;router.get("/",staticRedirect("/index.html"));router.get("/ws",handleWebSockets(&handleWebSocketConnection));router.get("*",serveStaticFiles("public/"));autosettings=newHTTPServerSettings;settings.port=8080;settings.bindAddresses=["::1","127.0.0.1"];listenHTTP(settings,router);}voidhandleWebSocketConnection(scopeWebSocketsocket){// Add socket to sockets listsockets~=socket;// Get usernamesocket.waitForData(1.seconds);stringname=socket.receiveText;// Server-side validation of resultsif(name!isnull){logInfo("%s connected @ %s.",name,socket.request.peer);sendTextToOtherClients(null,"System",name~" connected to the chat.");}else{// Kick person outsocket.send("{\"name\":\"System\", \"text\":\"Invalid name.\"}");socket.close;sockets.removeFromArray!WebSocket(socket);logInfo("%s disconnected.",name);return;}// message loopwhile(socket.waitForData){if(!socket.connected)break;// we got somethingautotext=socket.receiveText;// Close if recieve "/close"if(text=="/close")break;logInfo("Received: \"%s\" from %s.",text,name);// Relay text to everyone elsesendTextToOtherClients(socket,name,text);}// Remove socket from sockets list and close socketsocket.close;sockets.removeFromArray!WebSocket(socket);logInfo("%s disconnected.",name);sendTextToOtherClients(null,"System",name~" disconnected to the chat.");}voidsendTextToOtherClients(scopeWebSocketsrc_socket,stringname,stringtext){foreach(socket;sockets){// Don't send it to people who won't get it.if(!socket.connected)continue;logInfo("Sending: \"%s\" to %s.",text,socket.request.peer);// JSON encoding for simplicitysocket.send("{\"name\":\""~name~"\", \"text\":\""~text~"\"}");}}