Created
July 11, 2018 13:07
-
-
Save OliverLeitner/0e70906e46b2315ee943a56ab6296c1e to your computer and use it in GitHub Desktop.
better websocket sample, based upon https://github.com/gpeipman/AspNetCoreChatRoom
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>@ViewData["Title"] - AspNetCoreChatRoom</title> | |
<environment include="Development"> | |
<link rel="stylesheet" href="~/css/site.css" /> | |
</environment> | |
<environment exclude="Development"> | |
<link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> | |
</environment> | |
</head> | |
<body> | |
<div class="container body-content"> | |
@RenderBody() | |
</div> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk.Web"> | |
<PropertyGroup> | |
<TargetFramework>netcoreapp2.1</TargetFramework> | |
</PropertyGroup> | |
<ItemGroup> | |
<PackageReference Include="Microsoft.AspNetCore.App" /> | |
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.1.1" /> | |
</ItemGroup> | |
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.Http; | |
namespace AspNetCoreChatRoom { | |
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; | |
//trying to build a connection | |
try { | |
currentSocket = await context.WebSockets.AcceptWebSocketAsync (); | |
} catch (Exception ex) { | |
Console.WriteLine(ex.Message); | |
return; | |
} | |
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, socketId); | |
} | |
} | |
WebSocket dummy; | |
_sockets.TryRemove (socketId, out dummy); | |
//trying to normally close the connection | |
try { | |
await currentSocket.CloseAsync (WebSocketCloseStatus.NormalClosure, "Closing", ct); | |
currentSocket.Dispose (); | |
} catch (Exception ex) { | |
//if anything goes wrong... | |
//output error | |
Console.WriteLine (ex.Message); | |
currentSocket.Abort (); | |
} | |
} | |
private static Task SendStringAsync (WebSocket socket, string data, CancellationToken ct = default (CancellationToken), string socketId = "deadbeef") { | |
//append the socketid to the message for debugging purposes | |
//couldve taken it globally, this is just a poc | |
var buffer = Encoding.UTF8.GetBytes (data + ' ' + socketId); | |
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 (); | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Mvc; | |
namespace AspNetCoreChatRoom.Controllers { | |
public class HomeController : Controller { | |
[HttpGet] | |
public IActionResult Index () { | |
return View ("InsertUserName"); | |
} | |
[HttpPost] | |
public IActionResult Index (string username) { | |
return View ("Index", username); | |
} | |
public IActionResult Error () { | |
return View (); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@model string | |
@{ | |
ViewData["Title"] = "Home Page"; | |
} | |
<div class="msg"> | |
<div id="msgs"></div> | |
</div> | |
<div> | |
<input type="text" id="MessageField" placeholder="type message and press enter" /> | |
</div> | |
<script> | |
const userName = '@Model'; | |
</script> | |
<environment include="Development"> | |
<script src="~/js/site.js"></script> | |
</environment> | |
<environment exclude="Development"> | |
<script src="~/js/site.min.js"></script> | |
</environment> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<form action="@Url.Action("Index")" method="post"> | |
<input type="text" placeholder="Insert user name" name="userName" /> | |
<input type="submit" value="Eńter" /> | |
</form> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
margin: 0; | |
padding: 0; | |
float:left; | |
width: 100%; | |
} | |
.msg { | |
display:block; | |
white-space:nowrap; | |
padding-left: 30px; | |
padding-top: 10px; | |
} | |
input#MessageField { | |
position: absolute; | |
bottom: 0; | |
min-width: 320px; | |
width: calc(100% - 23px); | |
height: 30px; | |
padding-left: 20px; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const protocol = location.protocol === "https:" ? "wss:" : "ws:"; | |
const wsUri = protocol + "//" + window.location.host; | |
const socket = new WebSocket(wsUri); | |
socket.onopen = e => { | |
console.log("socket opened", e); | |
}; | |
socket.onclose = ((e) => { | |
console.log("socket closed", e); | |
}); | |
socket.onmessage = ((e) => { | |
console.log(e); | |
document.getElementById('msgs').innerHTML += e.data + '<br/>'; | |
}); | |
socket.onerror = ((e) => { | |
console.error(e.data); | |
}); | |
window.addEventListener('keydown', ((e) => { | |
//13 == enter key | |
//and if nothing is in the input field -> no reason to submit it | |
if (e.keyCode != 13 || e.target.value === undefined || e.target.value === '') { | |
return; | |
} | |
e.preventDefault(); | |
let message = userName + ": " + e.target.value; | |
socket.send(message); | |
e.target.value = ''; | |
})); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//needed for timespan function | |
using System; | |
using Microsoft.AspNetCore.Builder; | |
using Microsoft.AspNetCore.Hosting; | |
using Microsoft.Extensions.Configuration; | |
using Microsoft.Extensions.DependencyInjection; | |
using Microsoft.Extensions.Logging; | |
//enable useresponsecompression | |
using Microsoft.AspNetCore.ResponseCompression; | |
//for proxying | |
using Microsoft.AspNetCore.HttpOverrides; | |
namespace AspNetCoreChatRoom { | |
public class Startup { | |
public Startup (IHostingEnvironment env) { | |
var builder = new ConfigurationBuilder () | |
.SetBasePath (env.ContentRootPath) | |
.AddJsonFile ("appsettings.json", optional : false, reloadOnChange : true) | |
.AddJsonFile ($"appsettings.{env.EnvironmentName}.json", optional : true) | |
.AddEnvironmentVariables (); | |
Configuration = builder.Build (); | |
} | |
public IConfigurationRoot Configuration { get; } | |
// This method gets called by the runtime. Use this method to add services to the container. | |
public void ConfigureServices (IServiceCollection services) { | |
// Add framework services. | |
services.AddMvc (); | |
//deflate as a service | |
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, ILoggerFactory loggerFactory) { | |
loggerFactory.AddConsole (Configuration.GetSection ("Logging")); | |
loggerFactory.AddDebug (); | |
if (env.IsDevelopment ()) { | |
app.UseDeveloperExceptionPage (); | |
} else { | |
app.UseExceptionHandler ("/Shared/Error"); | |
} | |
app.UseStaticFiles (); | |
//use deflate | |
app.UseResponseCompression (); | |
var wsOptions = new WebSocketOptions () { | |
KeepAliveInterval = TimeSpan.FromSeconds (120), | |
ReceiveBufferSize = 4 * 1024 | |
}; | |
app.UseWebSockets (wsOptions); | |
app.UseMiddleware<ChatWebSocketMiddleware> (); | |
app.UseForwardedHeaders (new ForwardedHeadersOptions { | |
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | |
}); | |
app.UseMvc (routes => { | |
routes.MapRoute ( | |
name: "default", | |
template: "{controller=Home}/{action=Index}/{id?}"); | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment