Skip to content

Instantly share code, notes, and snippets.

@OliverLeitner
Created July 10, 2018 13:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OliverLeitner/d4f85a75da91e4b30bb2a37cc5213709 to your computer and use it in GitHub Desktop.
Save OliverLeitner/d4f85a75da91e4b30bb2a37cc5213709 to your computer and use it in GitHub Desktop.
dotnet-core-websockets-server
<html>
<head>
<title>test page</title>
<script language="javascript" type="text/javascript">
var socket;
var uri = "ws://" + window.location.host + "/ws";
//var uri = "ws://" + window.location.host;
var output;
var text = "test echo";
function write(s) {
var p = document.createElement("p");
p.innerHTML = s;
output.appendChild(p);
}
function doConnect() {
socket = new WebSocket(uri);
socket.onopen = function (e) {
write("opened " + uri);
doSend();
};
socket.onclose = function (e) {
write("closed");
};
socket.onmessage = function (e) {
write("Received: " + e.data);
socket.close();
};
socket.onerror = function (e) {
write("Error: " + e.data);
};
}
function doSend() {
write("Sending: " + text);
socket.send(text);
}
function onInit() {
output = document.getElementById("output");
doConnect();
}
window.onload = onInit;
</script>
</head>
<body>
<div id="output"></div>
</body>
</html>
using System;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.WebSockets;
namespace wsweb.handlers.sockethandler
{
public class SocketHandler
{
public const int BufferSize = 4096;
WebSocket socket;
SocketHandler(WebSocket socket)
{
this.socket = socket;
}
async Task EchoLoop()
{
var buffer = new byte[BufferSize];
var seg = new ArraySegment<byte>(buffer);
while (this.socket.State == WebSocketState.Open)
{
var incoming = await this.socket.ReceiveAsync(seg, CancellationToken.None);
var outgoing = new ArraySegment<byte>(buffer, 0, incoming.Count);
await this.socket.SendAsync(outgoing, WebSocketMessageType.Text, true, CancellationToken.None);
}
}
static async Task Acceptor(HttpContext hc, Func<Task> n)
{
if (!hc.WebSockets.IsWebSocketRequest)
{
return;
}
var socket = await hc.WebSockets.AcceptWebSocketAsync();
var h = new SocketHandler(socket);
await h.EchoLoop();
}
public static void Map(IApplicationBuilder app)
{
var options = new WebSocketOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(120),
ReceiveBufferSize = BufferSize
};
app.UseWebSockets(options);
app.Use(SocketHandler.Acceptor);
}
}
}
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.WebSockets;
using Microsoft.AspNetCore.WebUtilities;
namespace wsweb.handlers.sockethandlerb {
public class ChatWebSocketMiddleware {
private static ConcurrentDictionary<string, WebSocket> _sockets = new ConcurrentDictionary<string, WebSocket> ();
private readonly RequestDelegate _next;
public ChatWebSocketMiddleware (RequestDelegate next) {
_next = next;
}
public async Task Invoke (HttpContext context) {
if (!context.WebSockets.IsWebSocketRequest) {
await _next.Invoke (context);
return;
}
CancellationToken ct = context.RequestAborted;
WebSocket currentSocket = await context.WebSockets.AcceptWebSocketAsync ();
var socketId = Guid.NewGuid ().ToString ();
_sockets.TryAdd (socketId, currentSocket);
while (true) {
if (ct.IsCancellationRequested) {
break;
}
var response = await ReceiveStringAsync (currentSocket, ct);
if (string.IsNullOrEmpty (response)) {
if (currentSocket.State != WebSocketState.Open) {
break;
}
continue;
}
foreach (var socket in _sockets) {
if (socket.Value.State != WebSocketState.Open) {
continue;
}
await SendStringAsync (socket.Value, response, ct);
}
}
WebSocket dummy;
_sockets.TryRemove (socketId, out dummy);
await currentSocket.CloseAsync (WebSocketCloseStatus.NormalClosure, "Closing", ct);
currentSocket.Dispose ();
}
private static Task SendStringAsync (WebSocket socket, string data, CancellationToken ct = default (CancellationToken)) {
var buffer = Encoding.UTF8.GetBytes (data);
var segment = new ArraySegment<byte> (buffer);
return socket.SendAsync (segment, WebSocketMessageType.Text, true, ct);
}
private static async Task<string> ReceiveStringAsync (WebSocket socket, CancellationToken ct = default (CancellationToken)) {
var buffer = new ArraySegment<byte> (new byte[8192]);
using (var ms = new MemoryStream ()) {
WebSocketReceiveResult result;
do {
ct.ThrowIfCancellationRequested ();
result = await socket.ReceiveAsync (buffer, ct);
ms.Write (buffer.Array, buffer.Offset, result.Count);
}
while (!result.EndOfMessage);
ms.Seek (0, SeekOrigin.Begin);
if (result.MessageType != WebSocketMessageType.Text) {
return null;
}
// Encoding UTF8: https://tools.ietf.org/html/rfc6455#section-5.6
using (var reader = new StreamReader (ms, Encoding.UTF8)) {
return await reader.ReadToEndAsync ();
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
//adding compression
using Microsoft.AspNetCore.ResponseCompression;
//for proxying
using Microsoft.AspNetCore.HttpOverrides;
//adding our custom handler
using wsweb.handlers.sockethandler;
//using wsweb.handlers.sockethandlerb;
namespace wsweb {
public class Startup {
public Startup (IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices (IServiceCollection services) {
services.AddResponseCompression ();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure (IApplicationBuilder app, IHostingEnvironment env) {
//ms says, that this is good before doing any kinda auth, but from
//what i know about admin'ing this is a generally good idea on proxied
//requests
app.UseForwardedHeaders (new ForwardedHeadersOptions {
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseResponseCompression ();
app.UseDefaultFiles ();
app.UseStaticFiles ();
//app.UseMiddleware<ChatWebSocketMiddleware>();
app.Map ("/ws", SocketHandler.Map);
}
}
}
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.StaticFiles" Version="2.1.1" />
</ItemGroup>
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment