I am currently working on a way to create a WebSocket connection between an ASP.NET 5 (DNX), DNX Core Console and a Universal Windows Platform (Windows Store) application.
ASP.NET 5 serves as the server using the package "Microsoft.AspNet.WebSockets.Server": "1.0.0-rc1-final". Both the console app and the UWP app use "System.Net.WebSockets.Client": "4.0.0-beta-23516" as a websocket server.
The server configure method used the following to setup a very simple WebSocket server:
app.UseWebSockets();
app.Use( async ( Context, Next ) =>
{
var HttpContext = ( HttpContext )Context;
if( HttpContext.WebSockets.IsWebSocketRequest )
{
WebSocket Socket = await HttpContext.WebSockets.AcceptWebSocketAsync();
await Task.Delay( 100 );
CancellationTokenSource Cts = new CancellationTokenSource();
CancellationToken Ct = Cts.Token;
System.Diagnostics.Debug.WriteLine( "New client; sending data..." );
byte[] Data = System.Text.Encoding.UTF8.GetBytes( "Hello WebSocket!" );
await Socket.SendAsync( new ArraySegment<byte>( Data ), WebSocketMessageType.Binary, false, Ct );
System.Diagnostics.Debug.WriteLine( "Data sent." );
//System.Diagnostics.Debug.WriteLine( "New client; receiving data..." );
//byte[] Data = new byte[ 16 ];
//var Result = await Socket.ReceiveAsync( new ArraySegment<byte>( Data ), Ct );
//System.Diagnostics.Debug.WriteLine( $"Data received: {Result.Count}" );
}
} );
Both clients (console and UWP app) use the following code to connect to the server:
CancellationTokenSource Cts = new CancellationTokenSource();
CancellationToken Ct = Cts.Token;
string Address = "ws://localhost:5000";
var Socket = new System.Net.WebSockets.ClientWebSocket();
Console.WriteLine( $"Creating connection to: {Address}" );
await Socket.ConnectAsync( new Uri( Address ), Ct );
Task.Delay( 100 ).Wait();
byte[] Data = new byte[ 1024 ];
Console.WriteLine( "Receiving data..." );
var Result = await Socket.ReceiveAsync( new ArraySegment<byte>( Data ), Ct );
Console.WriteLine( $"Data received: {Result.Count}" );
//Console.WriteLine( "Sending data..." );
//byte[] Data = System.Text.Encoding.UTF8.GetBytes( "Hello WebSocket!" );
//Socket.SendAsync( new ArraySegment<byte>( Data ), WebSocketMessageType.Binary, false, Ct ).Wait();
//Console.WriteLine( "Data sent." );
(Of course the UWP app does not use Console.WriteLine but writes to a textblock. When running the server and the console app, the communication does work (Data is sent and received). When running the UWP app however, the connection is established but NO data is received. I have tried it the other way around (thus the commented lines) and in that case the UWP app does send the data, but the server does NOT receive it...
Is this a bug in one of the implementations or am I doing something wrong? I have already tried to deploy the server on another machine and checked my loopback exceptions -- with the same result. When using MVC in the server app, a HttpClient CAN receive data from within the UWP app, just WebSockets seem to be a problem.
Note: I want to re-use System.Net.WebSockets.Client since I'm gonna put everything in a shared library.
I have a working minimal solution to reproduce the issue on my OneDrive: https://onedrive.live.com/redir?resid=1DDFA1793BD83CB5!270551&authkey=!AFtXc_Xae2qnpF8&ithint=file%2czip
Help is greatly appreciated; thanks in advance!
In System.Net.WebSockets.Client
, it uses different WebSocketHandle
for different platform. See the source code in GitHub and in WebSocketHandle.WinRT.cs
, you can find its _webSocket
is defined as WinRTWebSocket
. In WinRTWebSocket
, it uses MessageWebSocket
to establish connection and receive data. MessageWebSocket
is the WebSocket class we usually used in UWP apps. MessageWebSocket
uses MessageReceived
event to handle received message. This event notifies the users of any incoming message. But this event is called when the whole message has been received by the endpoint (not for partial fragments).
So when you send data with endOfMessage
parameter in WebSocket.SendAsync
method to false
, MessageReceived
event won't be called as the message is not complete. You can build a UWP project using MessageWebSocket
to test it (you can refer to WebSocket sample in GitHub). And this makes Socket.ReceiveAsync
method always wait and you feel NO data is received.
To solve the problem, you need set endOfMessage
parameter in WebSocket.SendAsync
method to true
when the message is end. For example in your server use following code:
await Socket.SendAsync(new ArraySegment<byte>(Data), WebSocketMessageType.Binary, false, Ct);
await Task.Delay(1000);
await Socket.SendAsync(new ArraySegment<byte>(Data), WebSocketMessageType.Binary, true, Ct);
Then in your client you will receive Hello WebSocket!Hello WebSocket!
.