Creating a "Hello World" Websocket Example

Creating a Hello World WebSocket example

WebSockets is protocol that relies on TCP streamed connection. Although WebSockets is Message based protocol.

If you want to implement your own protocol then I recommend to use latest and stable specification (for 18/04/12) RFC 6455.
This specification contains all necessary information regarding handshake and framing. As well most of description on scenarios of behaving from browser side as well as from server side.
It is highly recommended to follow what recommendations tells regarding server side during implementing of your code.

In few words, I would describe working with WebSockets like this:

  1. Create server Socket (System.Net.Sockets) bind it to specific port, and keep listening with asynchronous accepting of connections. Something like that:
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
serverSocket.Bind(new IPEndPoint(IPAddress.Any, 8080));
serverSocket.Listen(128);
serverSocket.BeginAccept(null, 0, OnAccept, null);

  1. You should have accepting function "OnAccept" that will implement handshake. In future it has to be in another thread if system is meant to handle huge amount of connections per second.
private void OnAccept(IAsyncResult result) {
try {
Socket client = null;
if (serverSocket != null && serverSocket.IsBound) {
client = serverSocket.EndAccept(result);
}
if (client != null) {
/* Handshaking and managing ClientSocket */
}
} catch(SocketException exception) {

} finally {
if (serverSocket != null && serverSocket.IsBound) {
serverSocket.BeginAccept(null, 0, OnAccept, null);
}
}
}

  1. After connection established, you have to do handshake. Based on specification 1.3 Opening Handshake, after connection established you will receive basic HTTP request with some information. Example:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

This example is based on version of protocol 13. Bear in mind that older versions have some differences but mostly latest versions are cross-compatible. Different browsers may send you some additional data. For example Browser and OS details, cache and others.

Based on provided handshake details, you have to generate answer lines, they are mostly same, but will contain Accept-Key, that is based on provided Sec-WebSocket-Key. In specification 1.3 it is described clearly how to generate response key.
Here is my function I've been using for V13:

static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
private string AcceptKey(ref string key) {
string longKey = key + guid;
SHA1 sha1 = SHA1CryptoServiceProvider.Create();
byte[] hashBytes = sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(longKey));
return Convert.ToBase64String(hashBytes);
}

Handshake answer looks like that:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

But accept key have to be the generated one based on provided key from client and method AcceptKey I provided before. As well, make sure after last character of accept key you put two new lines "\r\n\r\n".


  1. After handshake answer is sent from server, client should trigger "onopen" function, that means you can send messages after.

  2. Messages are not sent in raw format, but they have Data Framing. And from client to server as well implement masking for data based on provided 4 bytes in message header. Although from server to client you don't need to apply masking over data. Read section 5. Data Framing in specification.
    Here is copy-paste from my own implementation. It is not ready-to-use code, and have to be modified, I am posting it just to give an idea and overall logic of read/write with WebSocket framing. Go to this link.

  3. After framing is implemented, make sure that you receive data right way using sockets. For example to prevent that some messages get merged into one, because TCP is still stream based protocol. That means you have to read ONLY specific amount of bytes. Length of message is always based on header and provided data length details in header it self. So when you receiving data from Socket, first receive 2 bytes, get details from header based on Framing specification, then if mask provided another 4 bytes, and then length that might be 1, 4 or 8 bytes based on length of data. And after data it self. After you read it, apply demasking and your message data is ready to use.

  4. You might want to use some Data Protocol, I recommend to use JSON due traffic economy and easy to use on client side in JavaScript. For server side you might want to check some of parsers. There is lots of them, google can be really helpful.

Implementing own WebSockets protocol definitely have some benefits and great experience you get as well as control over protocol it self. But you have to spend some time doing it, and make sure that implementation is highly reliable.

In same time you might have a look in ready to use solutions that google (again) have enough.

What is the simplest way to create a Websocket server in c#?

SignalR is great for cases where:

1) You need a fallback in case websockets aren't available

AND

2) You have control over the implementation of the client (there's a specific protocol that has to be followed by the client)

The easiest way I can think of, from what you tell us about the project (limited control of the implementation of the client, websocket server implementation only, Web API) is Microsoft.WebSockets.

You can install Microsoft.WebSockets from NuGet and be up and running with a websocket server in minutes. There a few tutorials out there (ex.: https://dejanstojanovic.net/aspnet/2014/june/database-change-notifications-in-aspnet-using-websocket/), but essentially:

1) make sure your IIS has websockets enabled

2) create a Web API controller that handles websocket requests. Ex.:

using System;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using Microsoft.Web.WebSockets;

public class SomeController : ApiController
{
// Has to be called something starting with "Get"
public HttpResponseMessage Get()
{
HttpContext.Current.AcceptWebSocketRequest(new SomeWebSocketHandler());
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}

class SomeWebSocketHandler : WebSocketHandler
{
public SomeWebSocketHandler() { SetupNotifier(); }
protected void SetupNotifier()
{
// Call a method to handle whichever change you want to broadcast
var messageToBroadcast = "Hello world";
broadcast(messageToBroadcast);
}

private static WebSocketCollection _someClients = new WebSocketCollection();

public override void OnOpen()
{
_someClients.Add(this);
}

public override void OnMessage(string message)
{

}

private void broadcast(string message)
{
_someClients.Broadcast(msg);
SetupNotifier();
}
}

}

The SetupNotifier() method should contain logic that catches the change you want to react upon. broadcast(string message) (can be renamed of course) contains the logic that "returns" data to the client(s) - this example sends the same message to all clients.

Make sure to test this with a proper websocket client (there are Chrome extenstions for this, if you want the ease of use) - you can't do ws:// requests in a browser as-is.

QWebSocket Hello World Example

You have 2 errors:

  • By not assigning the client object to a variable then it is immediately destroyed.
  • Qt needs an eventloop for the signals, events, etc. to work, in this case a QCoreApplication is enough.
#include "source/server/WebSocketClient.hpp"

#include <QCoreApplication>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ravi::server::WebSocketClient client(QUrl("ws://127.0.0.1:8888"));
return a.exec();
}
#include "source/server/WebSocketServer.hpp"

#include <QCoreApplication>

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ravi::server::WebSocketServer server(8888);
return a.exec();
}

On the other hand I find it strange that auto ws_server = ravi::server::WebSocketServer(8888); does not generate a compilation error since being a WebSocketServer a QObject should not allow the copy constructor.

Creating client to consume websocket from custom server

if I properly understand your needs you could try to check signalr library.
https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-getting-started-with-signalr

Hello world with Tornado and Javascript client

You need a separate WebSocketHandler instance mapped to a separate URL in your application. So your "tornado.html" should be mapped to the URL "/", and your Javascript should connect to a WebSocketHandler listening on a URL like "/websocket". Try this:

import tornado.ioloop
import tornado.web
import tornado.websocket

class Hello(tornado.websocket.WebSocketHandler):
def open(self):
self.write_message("Hello, world")

def on_message(self, message):
pass

def on_close(self):
pass

class Main(tornado.web.RequestHandler):
def get(self):
# This could be a template, too.
self.write('''
<script>
ws = new WebSocket("ws://localhost:8888/websocket");
ws.onmessage = function(e) {
alert('message received: ' + e.data);
};
</script>''')

application = tornado.web.Application([
(r"/", Main),
(r"/websocket", Hello),
])

if __name__ == "__main__":
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

C# as Websocket server for HTML5 websocket connection

I modified a code i downloaded online and here's what I got now:

using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Security.Cryptography;
using System.Threading;

namespace WebSocketServer
{
public enum ServerLogLevel { Nothing, Subtle, Verbose };
public delegate void ClientConnectedEventHandler(WebSocketConnection sender, EventArgs e);

public class WebSocketServer
{
#region private members
private string webSocketOrigin; // location for the protocol handshake
private string webSocketLocation; // location for the protocol handshake
#endregion
static private string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
static IPEndPoint ipLocal;

public event ClientConnectedEventHandler ClientConnected;

/// <summary>
/// TextWriter used for logging
/// </summary>
public TextWriter Logger { get; set; } // stream used for logging

/// <summary>
/// How much information do you want, the server to post to the stream
/// </summary>
public ServerLogLevel LogLevel = ServerLogLevel.Subtle;

/// <summary>
/// Gets the connections of the server
/// </summary>
public List<WebSocketConnection> Connections { get; private set; }

/// <summary>
/// Gets the listener socket. This socket is used to listen for new client connections
/// </summary>
public Socket ListenerSocker { get; private set; }

/// <summary>
/// Get the port of the server
/// </summary>
public int Port { get; private set; }

public WebSocketServer(int port, string origin, string location)
{
Port = port;
Connections = new List<WebSocketConnection>();
webSocketOrigin = origin;
webSocketLocation = location;
}

/// <summary>
/// Starts the server - making it listen for connections
/// </summary>
public void Start()
{
// create the main server socket, bind it to the local ip address and start listening for clients
ListenerSocker = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
ipLocal = new IPEndPoint(IPAddress.Loopback, Port);
ListenerSocker.Bind(ipLocal);
ListenerSocker.Listen(100);

LogLine(DateTime.Now + "> server stated on " + ListenerSocker.LocalEndPoint, ServerLogLevel.Subtle);

ListenForClients();
}

// look for connecting clients
private void ListenForClients()
{
ListenerSocker.BeginAccept(new AsyncCallback(OnClientConnect), null);
}

private void OnClientConnect(IAsyncResult asyn)
{

byte[] buffer = new byte[1024];
string headerResponse = "";

// create a new socket for the connection
var clientSocket = ListenerSocker.EndAccept(asyn);
var i = clientSocket.Receive(buffer);
headerResponse = (System.Text.Encoding.UTF8.GetString(buffer)).Substring(0, i);
//Console.WriteLine(headerResponse);

if (clientSocket != null)
{

// Console.WriteLine("HEADER RESPONSE:"+headerResponse);
var key = headerResponse.Replace("ey:", "`")
.Split('`')[1] // dGhlIHNhbXBsZSBub25jZQ== \r\n .......
.Replace("\r", "").Split('\n')[0] // dGhlIHNhbXBsZSBub25jZQ==
.Trim();
var test1 = AcceptKey(ref key);
var newLine = "\r\n";
var name = "Charmaine";
var response = "HTTP/1.1 101 Switching Protocols" + newLine
+ "Upgrade: websocket" + newLine
+ "Connection: Upgrade" + newLine
+ "Sec-WebSocket-Accept: " + test1 + newLine + newLine
+ "Testing lang naman po:" + name
;

// which one should I use? none of them fires the onopen method
clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(response));
}

// keep track of the new guy
var clientConnection = new WebSocketConnection(clientSocket);
Connections.Add(clientConnection);
// clientConnection.Disconnected += new WebSocketDisconnectedEventHandler(ClientDisconnected);
Console.WriteLine("New user: " + ipLocal);
// invoke the connection event
if (ClientConnected != null)
ClientConnected(clientConnection, EventArgs.Empty);

if (LogLevel != ServerLogLevel.Nothing)
clientConnection.DataReceived += new DataReceivedEventHandler(DataReceivedFromClient);

// listen for more clients
ListenForClients();

}

void ClientDisconnected(WebSocketConnection sender, EventArgs e)
{
Connections.Remove(sender);
LogLine(DateTime.Now + "> " + sender.ConnectionSocket.LocalEndPoint + " disconnected", ServerLogLevel.Subtle);
}

void DataReceivedFromClient(WebSocketConnection sender, DataReceivedEventArgs e)
{
Log(DateTime.Now + "> data from " + sender.ConnectionSocket.LocalEndPoint, ServerLogLevel.Subtle);
Log(": " + e.Data + "\n" + e.Size + " bytes", ServerLogLevel.Verbose);
LogLine("", ServerLogLevel.Subtle);
}

/// <summary>
/// send a string to all the clients (you spammer!)
/// </summary>
/// <param name="data">the string to send</param>
public void SendToAll(string data)
{
Connections.ForEach(a => a.Send(data));
}

/// <summary>
/// send a string to all the clients except one
/// </summary>
/// <param name="data">the string to send</param>
/// <param name="indifferent">the client that doesn't care</param>
public void SendToAllExceptOne(string data, WebSocketConnection indifferent)
{
foreach (var client in Connections)
{
if (client != indifferent)
client.Send(data);
}
}

/// <summary>
/// Takes care of the initial handshaking between the the client and the server
/// </summary>

private void Log(string str, ServerLogLevel level)
{
if (Logger != null && (int)LogLevel >= (int)level)
{
Logger.Write(str);
}
}

private void LogLine(string str, ServerLogLevel level)
{
Log(str + "\r\n", level);
}

private static string AcceptKey(ref string key)
{
string longKey = key + guid;
byte[] hashBytes = ComputeHash(longKey);
return Convert.ToBase64String(hashBytes);
}
static SHA1 sha1 = SHA1CryptoServiceProvider.Create();
private static byte[] ComputeHash(string str)
{
return sha1.ComputeHash(System.Text.Encoding.ASCII.GetBytes(str));
}

private void ShakeHands(Socket conn)
{
using (var stream = new NetworkStream(conn))
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
//read handshake from client (no need to actually read it, we know its there):
LogLine("Reading client handshake:", ServerLogLevel.Verbose);
string r = null;
while (r != "")
{
r = reader.ReadLine();
LogLine(r, ServerLogLevel.Verbose);
}

// send handshake to the client
writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
writer.WriteLine("Upgrade: WebSocket");
writer.WriteLine("Connection: Upgrade");
writer.WriteLine("WebSocket-Origin: " + webSocketOrigin);
writer.WriteLine("WebSocket-Location: " + webSocketLocation);
writer.WriteLine("");
}

// tell the nerds whats going on
LogLine("Sending handshake:", ServerLogLevel.Verbose);
LogLine("HTTP/1.1 101 Web Socket Protocol Handshake", ServerLogLevel.Verbose);
LogLine("Upgrade: WebSocket", ServerLogLevel.Verbose);
LogLine("Connection: Upgrade", ServerLogLevel.Verbose);
LogLine("WebSocket-Origin: " + webSocketOrigin, ServerLogLevel.Verbose);
LogLine("WebSocket-Location: " + webSocketLocation, ServerLogLevel.Verbose);
LogLine("", ServerLogLevel.Verbose);

LogLine("Started listening to client", ServerLogLevel.Verbose);
//conn.Listen();
}

}
}

Connection issue resolved, next would be SENDING DATA to client.

Calling a WebSocket from Silverlight client example code?

It turns out that the WebSocket object was falling out of scope and thus being disposed.

Can't get minimal ASP .NET Core 3.1 WebSocket example to work

That's it! Using wss:// and everything works!

As I mentioned, WebSockets supports well in ASP.NET Core 3.0 and 3.1.

Besides, as we discussed in comments, we should check the server URL and port before we start to establish connection(s).

Using wss://

Sample Image

Using ws://

Sample Image



Related Topics



Leave a reply



Submit