Difference between revisions of "User:Csmith1991/Vibe.d Documentation/websocket"
Csmith1991 (talk | contribs) m (→Vibe.d Websocket's Tutorial) |
Csmith1991 (talk | contribs) (→Vibe.d Websocket's Tutorial -- added code) |
||
Line 1: | Line 1: | ||
==Vibe.d Websocket's Tutorial== | ==Vibe.d Websocket's Tutorial== | ||
+ | |||
+ | This tutorial is extended from [https://github.com/rejectedsoftware/vibe.d/tree/master/examples/websocket here]. Instead of a simple counter, this provides a functioning web chat application. | ||
+ | |||
+ | ===Folder Structure=== | ||
+ | . | ||
+ | ├── public/ | ||
+ | | ├── index.html | ||
+ | | └── scripts/ | ||
+ | | └── websocket.js | ||
+ | ├── source/ | ||
+ | | └── app.d | ||
+ | |||
+ | ==== index.html ==== | ||
+ | <syntaxhighlight lang="html4strict"> | ||
+ | <!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> | ||
+ | <div id="chatLog"></div><!-- this is where we'll insert chat into --> | ||
+ | |||
+ | <div id="login"> | ||
+ | <!-- {which: 13} is a hack to make the button function the same as onkeypress --> | ||
+ | <input id="name" type="text" placeholder="Your Name" onkeypress="startConnection(event);" /> <button id="connect" onclick="startConnection({which: 13});">Connect</button> | ||
+ | </div> | ||
+ | <div id="chat" style="display:none;"> | ||
+ | <input id="text" type="text" placeholder="press enter to submit" onkeypress="chat(event);" /> <button id="send" onclick="chat({which: 13});">Send</button><br /> | ||
+ | <button id="disconnect" onclick="endConnection();">Disconnect</button> | ||
+ | </div> | ||
+ | <script src="/scripts/websocket.js"></script> | ||
+ | </body> | ||
+ | </html> | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== websocket.js ==== | ||
+ | |||
+ | <syntaxhighlight lang="JavaScript"> | ||
+ | // Keep socket variable at global level | ||
+ | var socket = {}; | ||
+ | |||
+ | function connect(name) { | ||
+ | try { | ||
+ | |||
+ | // Test if websocket doesn't exist, and if not, create a new connection | ||
+ | if (! ('readyState' in socket)) { | ||
+ | socket = new WebSocket(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 around | ||
+ | document.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) { | ||
+ | var msgVal = JSON.parse(msg.data); // We're anticipating messages formatted as "{'name':'Csmith1991', 'text':'example'}" | ||
+ | var chatLog = document.getElementById('chatLog'); | ||
+ | chatLog.innerHTML += '<p>' + msgVal.name + ': ' + msgVal.text + '</p>'; // Add to the chatLog | ||
+ | chatLog.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); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function startConnection(event) { | ||
+ | // 13 = enter button | ||
+ | if (event.which === 13 || event.keyCode === 13 ) { | ||
+ | // Connect to WebSocket server | ||
+ | connect(document.getElementById('name').value); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function chat(event) { | ||
+ | if (event.which === 13 || event.keyCode === 13 ) { | ||
+ | var myObj = document.getElementById('text'); | ||
+ | socket.send(myObj.value); | ||
+ | myObj.value = ''; | ||
+ | myObj.focus(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function endConnection() { | ||
+ | // We want to close the connection on the server, so we create a message that the server listens for to close on | ||
+ | socket.send('/close'); | ||
+ | } | ||
+ | |||
+ | function getBaseURL() { | ||
+ | // Get the WebSocket server address e.g. ws://127.0.0.1:8080 | ||
+ | |||
+ | var href = window.location.href.substring(7); // strip "http://" | ||
+ | var idx = href.indexOf('/'); | ||
+ | return 'ws://' + href.substring(0, idx); | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ==== app.d ==== | ||
+ | |||
<syntaxhighlight lang="D"> | <syntaxhighlight lang="D"> | ||
− | + | module webSocketExample; | |
+ | |||
import vibe.d; | import vibe.d; | ||
+ | import vibe.utils.array; | ||
+ | |||
+ | private WebSocket[] sockets; | ||
+ | |||
+ | shared static this() | ||
+ | { | ||
+ | /// Use /ws to identify websocket requests, serve files out of the public folder otherwise | ||
+ | auto router = new URLRouter; | ||
+ | router.get("/", staticRedirect("/index.html")); | ||
+ | router.get("/ws", handleWebSockets(&handleWebSocketConnection)); | ||
+ | router.get("*", serveStaticFiles("public/")); | ||
+ | |||
+ | auto settings = new HTTPServerSettings; | ||
+ | settings.port = 8080; | ||
+ | settings.bindAddresses = ["::1", "127.0.0.1"]; | ||
+ | listenHTTP(settings, router); | ||
+ | } | ||
+ | |||
+ | void handleWebSocketConnection(scope WebSocket socket) | ||
+ | { | ||
+ | // Add socket to sockets list | ||
+ | sockets ~= socket; | ||
+ | |||
+ | // Get username | ||
+ | socket.waitForData(1.seconds); | ||
+ | string name = socket.receiveText; | ||
+ | |||
+ | // Server-side validation of results | ||
+ | if (name !is null) | ||
+ | { | ||
+ | logInfo("%s connected @ %s.", name, socket.request.peer); | ||
+ | sendTextToOtherClients(null, "System", name ~ " connected to the chat."); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | // Kick person out | ||
+ | socket.send("{\"name\":\"System\", \"text\":\"Invalid name.\"}"); | ||
+ | |||
+ | socket.close; | ||
+ | sockets.removeFromArray!WebSocket(socket); | ||
+ | logInfo("%s disconnected.", name); | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | // message loop | ||
+ | while (socket.waitForData) | ||
+ | { | ||
+ | if (!socket.connected) break; | ||
+ | // we got something | ||
+ | auto text = socket.receiveText; | ||
+ | |||
+ | // Close if recieve "/close" | ||
+ | if (text == "/close") break; | ||
+ | |||
+ | logInfo("Received: \"%s\" from %s.", text, name); | ||
+ | // Relay text to everyone else | ||
+ | sendTextToOtherClients(socket, name, text); | ||
+ | } | ||
+ | |||
+ | // Remove socket from sockets list and close socket | ||
+ | socket.close; | ||
+ | sockets.removeFromArray!WebSocket(socket); | ||
+ | logInfo("%s disconnected.", name); | ||
+ | |||
+ | sendTextToOtherClients(null, "System", name ~ " disconnected to the chat."); | ||
+ | } | ||
+ | |||
+ | void sendTextToOtherClients(scope WebSocket src_socket, string name, string text) | ||
+ | { | ||
+ | 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 simplicity | ||
+ | socket.send("{\"name\":\"" ~ name ~ "\", \"text\":\"" ~ text ~ "\"}"); | ||
+ | } | ||
+ | } | ||
+ | |||
</syntaxhighlight> | </syntaxhighlight> |
Revision as of 02:21, 12 May 2015
Contents
Vibe.d Websocket's Tutorial
This tutorial is extended from here. Instead of a simple counter, this provides a functioning web chat application.
Folder Structure
. ├── public/ | ├── index.html | └── scripts/ | └── websocket.js ├── source/ | └── app.d
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>
<div id="chatLog"></div><!-- this is where we'll insert chat into -->
<div id="login">
<!-- {which: 13} is a hack to make the button function the same as onkeypress -->
<input id="name" type="text" placeholder="Your Name" onkeypress="startConnection(event);" /> <button id="connect" onclick="startConnection({which: 13});">Connect</button>
</div>
<div id="chat" style="display:none;">
<input id="text" type="text" placeholder="press enter to submit" onkeypress="chat(event);" /> <button id="send" onclick="chat({which: 13});">Send</button><br />
<button id="disconnect" onclick="endConnection();">Disconnect</button>
</div>
<script src="/scripts/websocket.js"></script>
</body>
</html>
websocket.js
// Keep socket variable at global level
var socket = {};
function connect(name) {
try {
// Test if websocket doesn't exist, and if not, create a new connection
if (! ('readyState' in socket)) {
socket = new WebSocket(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 around
document.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) {
var msgVal = JSON.parse(msg.data); // We're anticipating messages formatted as "{'name':'Csmith1991', 'text':'example'}"
var chatLog = document.getElementById('chatLog');
chatLog.innerHTML += '<p>' + msgVal.name + ': ' + msgVal.text + '</p>'; // Add to the chatLog
chatLog.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);
}
}
function startConnection(event) {
// 13 = enter button
if (event.which === 13 || event.keyCode === 13 ) {
// Connect to WebSocket server
connect(document.getElementById('name').value);
}
}
function chat(event) {
if (event.which === 13 || event.keyCode === 13 ) {
var myObj = document.getElementById('text');
socket.send(myObj.value);
myObj.value = '';
myObj.focus();
}
}
function endConnection() {
// We want to close the connection on the server, so we create a message that the server listens for to close on
socket.send('/close');
}
function getBaseURL() {
// Get the WebSocket server address e.g. ws://127.0.0.1:8080
var href = window.location.href.substring(7); // strip "http://"
var idx = href.indexOf('/');
return 'ws://' + href.substring(0, idx);
}
app.d
module webSocketExample;
import vibe.d;
import vibe.utils.array;
private WebSocket[] sockets;
shared static this()
{
/// Use /ws to identify websocket requests, serve files out of the public folder otherwise
auto router = new URLRouter;
router.get("/", staticRedirect("/index.html"));
router.get("/ws", handleWebSockets(&handleWebSocketConnection));
router.get("*", serveStaticFiles("public/"));
auto settings = new HTTPServerSettings;
settings.port = 8080;
settings.bindAddresses = ["::1", "127.0.0.1"];
listenHTTP(settings, router);
}
void handleWebSocketConnection(scope WebSocket socket)
{
// Add socket to sockets list
sockets ~= socket;
// Get username
socket.waitForData(1.seconds);
string name = socket.receiveText;
// Server-side validation of results
if (name !is null)
{
logInfo("%s connected @ %s.", name, socket.request.peer);
sendTextToOtherClients(null, "System", name ~ " connected to the chat.");
}
else
{
// Kick person out
socket.send("{\"name\":\"System\", \"text\":\"Invalid name.\"}");
socket.close;
sockets.removeFromArray!WebSocket(socket);
logInfo("%s disconnected.", name);
return;
}
// message loop
while (socket.waitForData)
{
if (!socket.connected) break;
// we got something
auto text = socket.receiveText;
// Close if recieve "/close"
if (text == "/close") break;
logInfo("Received: \"%s\" from %s.", text, name);
// Relay text to everyone else
sendTextToOtherClients(socket, name, text);
}
// Remove socket from sockets list and close socket
socket.close;
sockets.removeFromArray!WebSocket(socket);
logInfo("%s disconnected.", name);
sendTextToOtherClients(null, "System", name ~ " disconnected to the chat.");
}
void sendTextToOtherClients(scope WebSocket src_socket, string name, string text)
{
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 simplicity
socket.send("{\"name\":\"" ~ name ~ "\", \"text\":\"" ~ text ~ "\"}");
}
}