Difference between revisions of "User:Csmith1991/Vibe.d Documentation/websocket"
Csmith1991 (talk | contribs) |
m (Websocket's was possessive) |
||
(One intermediate revision by the same user not shown) | |||
Line 1: | Line 1: | ||
− | ==Vibe.d | + | ==Vibe.d Websockets 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. | 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. | ||
− | === | + | === 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. | '''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. | ||
Latest revision as of 02:31, 6 January 2016
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
. ├── 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 ~ "\"}");
}
}