Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Real-time web client (SignalR client) #20

Closed
danroth27 opened this issue Jan 25, 2018 · 37 comments

Comments

Projects
@danroth27
Copy link
Member

commented Jan 25, 2018

  • Create an initial prototype.
  • Provide a simpler way to call C# from JavaScript. (Based on discoveries from building the prototype).
  • Cleanup interop code. (Make naming consistent, etc).
  • Implement asynchronous calls properly both ways (error marshalling, etc.)
  • Move the code into the code base
  • Implement the remaining client API Surface
@grahamehorner

This comment has been minimized.

Copy link

commented Feb 10, 2018

This would be a fantastic addition and could allow a developer to have some great MVVM features that use RPC like communication with server side state/logic, it would IMHO also be great If gRPC could also be added to this list 🤔

@davidfowl

This comment has been minimized.

Copy link
Member

commented Feb 11, 2018

I think we just need to add support for the correct browser APIs:

  • EventSource
  • WebSockets

Then we should be able to use the SignalR client in a blazor application.

@grahamehorner

This comment has been minimized.

Copy link

commented Feb 11, 2018

@davidfowl @danroth27 also having support for WebAssembly Service Workers would be sweet 😎

@grahamehorner

This comment has been minimized.

Copy link

commented Feb 12, 2018

@SteveSandersonMS @danroth27 @davidfowl just thinking aloud 🤔 wondering if a wasm SignalR services worker could be used to allow local communications between multiple wasm components in a IPC style manor for wasm from the same domain without sending over the wire 🤔

@danroth27 danroth27 added this to the Backlog milestone Feb 14, 2018

@danroth27 danroth27 added this to To Do in Blazor Feb 14, 2018

@grahamehorner

This comment has been minimized.

Copy link

commented Feb 16, 2018

FYI, I’m currently looking at using RxJS and RX.NET for hooking into browser DOM events to Blazor .NET functions

@danroth27

This comment has been minimized.

Copy link
Member Author

commented Mar 23, 2018

Follow up on status of detecting WebAssembly as a platform in Mono: mono/mono#7344

@RandyBuchholz

This comment has been minimized.

Copy link

commented Mar 24, 2018

I think SignalR may be too high level (as I think @davidfowl implied), and just sockets may be a little too low. I think a good middle ground would be a (semi?) transparent RPC layer. If I'm running C# on both sides, it would be nice not have to deal with things like AJAX or Json(directly), and just work in real-time.

A basic approach could create a ws:// on the port the page is running on as a default. You could define interface pairs for the client and server. Then you could just decorate a method to indicate it is using an RPC call. You could decorate at the class or method level.

The code on the client could look something like this:

class Bar {
   [RPC: gateway=IFoo]
   public string SomeMethod(string par) => IFoo.ServerMethod(par); 
} 

var fromServerValue = new Bar().SomeMethod("hello");

You can move things between client and server side execution easily. To move the implementation to client-side

class Bar {
   public string SomeMethod(string par){ // code moved from server class to here }
}
var fromClientValue = new Bar().SomeMethod("hello");

The idea is that not only am minimizing client/server separation by using C# on both the client and server, I'm reducing the separation even more by abstracting away the network and providing "network-methods" I can run on either side.

@MihaMarkic

This comment has been minimized.

Copy link
Contributor

commented Apr 2, 2018

@RandyBuchholz I didn't get that impression. IMO SignalR support would be awesome and should happen.

@YandyZaldivar

This comment has been minimized.

Copy link

commented Apr 2, 2018

Can I assume that the System.Net.WebSockets namespace will be implemented as is, at least everything related to the client, so I can base a high level client implementation of networking on it beforehand?

This implementation is not related to Blazor directly, but probably you have a good perspective on this regard.

@danroth27

This comment has been minimized.

Copy link
Member Author

commented Apr 2, 2018

Can I assume that the System.Net.WebSockets namespace will be implemented as is, at least everything related to the client, so I can base a high level client implementation of networking on it beforehand?

I wouldn't "assume" it as it still needs to be investigated and implemented, but it's certainly how we hope things will work.

@YandyZaldivar

This comment has been minimized.

Copy link

commented Apr 2, 2018

I wouldn't "assume" it as it still needs to be investigated and implemented, but it's certainly how we hope things will work.

Ok, then awaiting for an official response on which is going to be the final API for low level WebSockets.

@l-o-l

This comment has been minimized.

Copy link

commented Apr 8, 2018

Hi,
I'm completely ignorant, so please forgive me for any uninformed utterances, or if this is not the right forum. We have a number of c# services that communicate via sockets using serialisation and reflection to provide a pub-sub model for accessing data. As we build more and more web based client apps, and cloud based services, we end up converting from one data format to another generally losing a bit of fidelity as it goes. Particularly problematic is the fact that REST style services aren't great for a system built around real-time and pub-sub. So websockets look great (in fact we are moving to them for their configuration simplification anyway), but Blazor looks like it might have the potential to take a c# client server style comms/data library and be able to use it in a browser. Is this a potential possibility? Am I just a dolt?

@danroth27

This comment has been minimized.

Copy link
Member Author

commented Apr 9, 2018

For the implementing pub-sub patterns on the server you will soon be able to use SignalR (coming soon for ASP.NET Core). For the client, you can today use the JavaScript SignalR client library to communicate over a websocket to the server. In the future with Blazor you should be able to use the .NET SignalR client library, which is what this issue is tracking.

@javiercn javiercn assigned javiercn and unassigned danroth27 Apr 24, 2018

@javiercn javiercn modified the milestones: Backlog, 0.3.0 Apr 24, 2018

@javiercn javiercn changed the title Real-time web client Real-time web client (SignalR client) Apr 24, 2018

@rynowak rynowak modified the milestones: 0.3.0, 0.4.0 May 14, 2018

@danroth27 danroth27 removed this from the 0.4.0 milestone Jun 4, 2018

@furoraest

This comment has been minimized.

Copy link

commented Jun 21, 2018

I hope there is some progress, one of the cool things I'd like to test out with Blazor

@danroth27

This comment has been minimized.

Copy link
Member Author

commented Jun 21, 2018

Unfortunately this item has been delayed again due to some resource shuffling.

@galvesribeiro Are you still interested in taking this one on?

@galvesribeiro

This comment has been minimized.

Copy link
Contributor

commented Jun 21, 2018

I do. How can we proceed?

@RemiBou

This comment has been minimized.

Copy link
Contributor

commented Jun 25, 2018

How can we help here ?

@galvesribeiro

This comment has been minimized.

Copy link
Contributor

commented Jun 25, 2018

@RemiBou Daniel mentioned on Gitter that he is gathering together the current work they have and he will share it. Once it is out I'll get it and try to finish it before the next release.

@malachib

This comment has been minimized.

Copy link

commented Jul 4, 2018

Eagerly anticipating this feature!

@SteveSandersonMS

This comment has been minimized.

Copy link
Member

commented Jul 9, 2018

Moving out of 0.5.0, because the SignalR client is now being taken on by some folks in the Blazor community.

@SteveSandersonMS SteveSandersonMS modified the milestones: 0.5.0, Backlog Jul 9, 2018

@galvesribeiro

This comment has been minimized.

Copy link
Contributor

commented Jul 9, 2018

@SteveSandersonMS yeah! Still waiting for @danroth27 to start on that! 😄

@lawgorht

This comment has been minimized.

Copy link

commented Jul 14, 2018

I'm looking forward to test this feature! It is the last missing part for us before starting a project with blazor.

@ChristianWeyer

This comment has been minimized.

Copy link

commented Jul 14, 2018

Would it be OK for you start with using the JS SignalR client and JS interop @lawgorht ?

@galvesribeiro

This comment has been minimized.

Copy link
Contributor

commented Jul 14, 2018

I'm almost done with it https://github.com/BlazorExtensions/SignalR

I need to figure out why the Async JS interop send extra quotes on string parameters while the sync doesn't.

That is blocking me now. After that, just complete the test/samples and will pack the nuget. So stay tunned.

@RemiBou

This comment has been minimized.

Copy link
Contributor

commented Jul 14, 2018

@lawgorht

This comment has been minimized.

Copy link

commented Jul 14, 2018

@galvesribeiro

This comment has been minimized.

Copy link
Contributor

commented Jul 14, 2018

@RemiBou yeah, I'll do a container class to test...

Here is what happens when using sync:

image

And now what happens when using async:

image

@galvesribeiro

This comment has been minimized.

Copy link
Contributor

commented Jul 15, 2018

So, progress... Found a workaround for the bug and:

image

Close 😄

@galvesribeiro

This comment has been minimized.

Copy link
Contributor

commented Jul 18, 2018

Ouch! Sorry guys, I forgot to let you all know...

The package is out https://github.com/BlazorExtensions/SignalR and https://www.nuget.org/packages/Blazor.Extensions.SignalR

Please enjoy it and let me know by Gitter or at the package repo if you have any issues with...

Thanks!

@SteveSandersonMS

This comment has been minimized.

Copy link
Member

commented Jul 23, 2018

I'll close this now with the recommendation that you use @galvesribeiro's package. Long term it may become possible to use the native SignalR .NET client library too.

Blazor automation moved this from To Do to Done Jul 23, 2018

@lawgorht

This comment has been minimized.

Copy link

commented Aug 13, 2018

great work @galvesribeiro it is working like a charm

@csnewman

This comment has been minimized.

Copy link

commented Sep 5, 2018

For people who wish to use the native SignalR Core .Net client library, you can take a look at https://github.com/csnewman/BlazorSignalR

@AlexPaskhin

This comment has been minimized.

Copy link

commented Nov 18, 2018

With Blazor version 0.7 I try to have a look on this issue:

Here is a report what/where/why it doesn't work.

My Preparation Steps:

  1. I had cloned with git the source codes:
  1. Added into the BlazorChatSample.Server project references.
    Added SignalR source projects into the solution
  <ItemGroup>
    <ProjectReference Include="..\..\aspnet-signalr\src\Microsoft.AspNetCore.Http.Connections.Common\Microsoft.AspNetCore.Http.Connections.Common.csproj" />
    <ProjectReference Include="..\..\aspnet-signalr\src\Microsoft.AspNetCore.Http.Connections\Microsoft.AspNetCore.Http.Connections.csproj" />
    <ProjectReference Include="..\..\aspnet-signalr\src\Microsoft.AspNetCore.SignalR.Common\Microsoft.AspNetCore.SignalR.Common.csproj" />
    <ProjectReference Include="..\..\aspnet-signalr\src\Microsoft.AspNetCore.SignalR.Core\Microsoft.AspNetCore.SignalR.Core.csproj" />
    <ProjectReference Include="..\..\aspnet-signalr\src\Microsoft.AspNetCore.SignalR.Protocols.Json\Microsoft.AspNetCore.SignalR.Protocols.Json.csproj" />
    <ProjectReference Include="..\..\aspnet-signalr\src\Microsoft.AspNetCore.SignalR\Microsoft.AspNetCore.SignalR.csproj" />
    <ProjectReference Include="..\BlazorChatSample.Client\BlazorChatSample.Client.csproj" />
  </ItemGroup>
  1. Added into the BlazorChatSample.Client project references.
    Added SignalR source projects into the solution:
  <ItemGroup>
    <PackageReference Include="Blazor.Extensions.Logging" Version="0.1.9" />
    <ProjectReference Include="..\..\aspnet-signalr\src\Microsoft.AspNetCore.SignalR.Client\Microsoft.AspNetCore.SignalR.Client.csproj" />
  </ItemGroup>
  1. Added into the client "ChatClient.cs" the new "Start" chat implementation with the SignalR client
 public async Task Start()
        {
            if (!_started)
            {
                Console.WriteLine("R-passed  # ==> ");
                try
                {
                    var hbuilder = new HubConnectionBuilder();
                    hbuilder.WithUrl(@"http://localhost:6840/chathub", (HttpConnectionOptions opt) =>
                     {
                         opt.Transports = HttpTransportType.WebSockets;


                     });
                    hbuilder.Services.AddLogging(builder => builder.AddBrowserConsole());
                    _connection = hbuilder.Build();

                    Console.WriteLine("R-_connection.StartAsync() ==> ");
                    await _connection.StartAsync();
                    Console.WriteLine("R-_connection.StartAsync() <== ");

                    _started = true;

                }
                catch (Exception e)
                {
                    Console.WriteLine("Not passed Exception : {0}", e.Message);
                }
            }
        }

My Investigation Report:

  1. Run the BlazorChatSample.Server.csproj and "F12" debug window "Console" saw:
**"WASM: Not passed Exception : Exception has been thrown by the target of an invocation."**

Find out the culprit "class CookieContainer" ):( and isolate it.

 public HttpConnectionOptions()
        {
            _headers = new Dictionary<string, string>();
            _clientCertificates = new X509CertificateCollection();
            //_cookies = new CookieContainer();

            Transports = HttpTransports.All;
        }
  1. Run again and find that in the HttpConnection.cs , NegotiateAsync() the HttpClient
    that created in the constructor with using method "CreateHttpClient()", doesn’t want to work.

Make small hack in the HttpConnection:

 public HttpConnection(HttpConnectionOptions httpConnectionOptions, ILoggerFactory loggerFactory)
	…

 if (!httpConnectionOptions.SkipNegotiation || httpConnectionOptions.Transports != HttpTransportType.WebSockets)
           {
                // _httpClient = CreateHttpClient();      
                    _httpClient = new HttpClient();
            }
  1. Run again and find out that something wrong with DNS and platform not supported
Failed to start connection. Error starting transport 'WebSockets'.
Exception: System.Net.WebSockets.WebSocketException (0x80004005): Unable to connect to the remote server ---> System.PlatformNotSupportedException: System.Net.Dns:GetHostByName is not supported on this platform.
at System.Net.Dns.EndGetHostAddresses (System.IAsyncResult asyncResult) <0x2422360 + 0x00036> in <00d74f6d2d5d49ba8f898dc4876e5823>:0 at (wrapper delegate-invoke) System.Func`2[System.IAsyncResult,System.Net.IPAddress[]].invoke_TResult_T(System.IAsyncResult) 
at System.Threading.Tasks.TaskFactory`1[TResult].FromAsyncCoreLogic (System.IAsyncResult iar, System.Func`2[T,TResult] endFunction, System.Action`1[T] endAction, System.Threading.Tasks.Task`1[TResult] promise, System.Boolean requiresSynchronization) <0x241c350 + 0x00042> in <cc8fc12fd14a44ad837c221ec0efb917>:0 

Done small hack again in the WebSocketsTransport.cs to use ip address 127.0.0.1 instead of localhost

public async Task StartAsync(Uri url, TransferFormat transferFormat)
        {
            Console.WriteLine("[WebSocketsTransport][StartAsync]==> ");
            if (url == null)
            {
                throw new ArgumentNullException(nameof(url));
            }
            if (transferFormat != TransferFormat.Binary && transferFormat != TransferFormat.Text)
            {
                throw new ArgumentException($"The '{transferFormat}' transfer format is not supported by this transport.", nameof(transferFormat));
            }
	
            _webSocketMessageType = transferFormat == TransferFormat.Binary
                ? WebSocketMessageType.Binary
                : WebSocketMessageType.Text;
	
            var resolvedUrl = ResolveWebSocketsUrl(url);
            var uriBuilder = new UriBuilder(resolvedUrl);
            uriBuilder.Host = "127.0.0.1";
            resolvedUrl = uriBuilder.Uri;

            Log.StartTransport(_logger, transferFormat, resolvedUrl);
	

4.. Run again and find out that something wrong with protocol support in the socket constructor.

Failed to start connection. Error starting transport 'WebSockets'.
Exception: System.Net.WebSockets.WebSocketException (0x80004005): Unable to connect to the remote server ---> System.Net.Sockets.SocketException (0x80004005): Protocol option not supported at 

System.Net.Sockets.Socket..ctor (System.Net.Sockets.AddressFamily addressFamily, System.Net.Sockets.SocketType socketType, System.Net.Sockets.ProtocolType protocolType) <0x2428960 + 0x000a6> in <b7360a45c4ff4a89b0e1545e1d0975a0>:0 

at System.Net.WebSockets.WebSocketHandle.ConnectSocketAsync (System.String host, System.Int32 port, System.Threading.CancellationToken cancellationToken) <0x240b060 + 0x0013a> in <b7360a45c4ff4a89b0e1545e1d0975a0>:0 

at System.Net.WebSockets.WebSocketHandle.ConnectAsyncCore (System.Uri uri, System.Threading.CancellationToken cancellationToken, System.Net.WebSockets.ClientWebSocketOptions options) <0x2404618 + 0x00174> in <b7360a45c4ff4a89b0e1545e1d0975a0>:0 

at System.Net.WebSockets.WebSocketHandle.ConnectAsyncCore (System.Uri uri, System.Threading.CancellationToken cancellationToken, System.Net.WebSockets.ClientWebSocketOptions options) <0x2404618 + 0x005f0> in <b7360a45c4ff4a89b0e1545e1d0975a0>:0 

at System.Net.WebSockets.ClientWebSocket.ConnectAsyncCore (System.Uri uri, System.Threading.CancellationToken cancellationToken) <0x24007a0 + 0x0016a> in <b7360a45c4ff4a89b0e1545e1d0975a0>:0 at Microsoft.AspNetCore.Http.Connections.Client.Internal.WebSocketsTransport.StartAsync (System.Uri url, Microsoft.AspNetCore.Connections.TransferFormat transferFormat) <0x23ebf28 + 0x0035e> in <934961ca980945cb9f82e31ddefe2fda>:0 

at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.StartTransport (System.Uri connectUrl, Microsoft.AspNetCore.Http.Connections.HttpTransportType transportType, Microsoft.AspNetCore.Connections.TransferFormat transferFormat) <0x23d1238 + 0x00138> in <934961ca980945cb9f82e31ddefe2fda>:0 

4.1 I was puzzled for a moment. Forced to create console SignalR client
and run the same "hacked" source code stuff to check does it works.
Took 10 minutes to see that all works - OK.

4.2 Decided to see what is loaded into the browser.
4.3 Find out that "System.Net.WebSockets.WebSocketHandle.ConnectSocketAsync" is the part on the
"tools/mono/bcl/Facades/System.Net.WebHeaderCollection.dll",
"tools/mono/bcl/Facades/System.Net.WebSockets.Client.dll",
"tools/mono/bcl/Facades/System.Net.WebSockets.dll",
And assembly code is located in the blazor bin folder, System.dll

Find the code, via dotPeek, that fails

	 
   private async Task<Socket> ConnectSocketAsync(string host, int port, CancellationToken cancellationToken)
	    {
	      IPAddress[] ipAddressArray = await Dns.GetHostAddressesAsync(host).ConfigureAwait(false);
	      ExceptionDispatchInfo exceptionDispatchInfo;
	      for (int index = 0; index < ipAddressArray.Length; ++index)
	      {
	        IPAddress address = ipAddressArray[index];
	        Socket socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
	        Socket socket1;
…

It clearly tells, there is a problem in mono.wasm runtime which doesn't support sockets.

Done smoke test

	 try
                {
                    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                }
                catch (Exception e)
                {
                    Console.WriteLine("Not passed Exception : {0}", e.Message);
                }

Got confirmation by exception:

**WASM: Not passed Exception : Protocol option not supported**

I think now, it is pretty clear what is the root case the SignalR issues.

Thanks for reading.
It may be useful to fix.

@davidfowl

This comment has been minimized.

Copy link
Member

commented Nov 18, 2018

ClientWebSocket should be mapped as best as it can to the underlying browser websocket APi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.