15

We have a .NET client, which use SignalR to call Server method, but the parameter seems very big, for such scenario how to fix it?

Client code:

public async Task FooAsync()
{
    var hubConnection = new HubConnection(...);
    await hubConnection.Start();

    var hubProxy = hubConnection.CreateHubProcx("ValueHub");
    //the content is very long, about 11776065 bytes (11MB)
    var content = File.ReadAllText(...);
    hubProxy.Invoke("Send", content);
    ...
}

Server code:

[HubName("ValueHub")]
public class ValueHub : Hub
{
    public void Send(string json)
    {

    }
}

From the exception stack and source code, I found the SignalR internally use HttpClient with the FormUrlEncodedContent type HttpContent, and maybe the limitation came from here.

System.UriFormatException was unhandled
  HResult=-2146233033
  Message=Invalid URI: The Uri string is too long.
  Source=System
  StackTrace:
       at System.UriHelper.EscapeString(String input, Int32 start, Int32 end, Char[] dest, Int32& destPos, Boolean isUriString, Char force1, Char force2, Char rsvd)
       at System.Uri.EscapeDataString(String stringToEscape)
       at System.Net.Http.FormUrlEncodedContent.Encode(String data)
       at System.Net.Http.FormUrlEncodedContent.GetContentByteArray(IEnumerable`1 nameValueCollection)
       at System.Net.Http.FormUrlEncodedContent..ctor(IEnumerable`1 nameValueCollection)
       at Microsoft.AspNet.SignalR.Client.Http.DefaultHttpClient.Post(String url, Action`1 prepareRequest, IDictionary`2 postData, Boolean isLongRunning)
       at Microsoft.AspNet.SignalR.Client.Transports.HttpBasedTransport.Send(IConnection connection, String data, String connectionData)
       at Microsoft.AspNet.SignalR.Client.Transports.AutoTransport.Send(IConnection connection, String data, String connectionData)
       at Microsoft.AspNet.SignalR.Client.Connection.Send(String data)
       at Microsoft.AspNet.SignalR.Client.Hubs.HubProxy.Invoke[T](String method, Object[] args)
       at Microsoft.AspNet.SignalR.Client.Hubs.HubProxy.Invoke(String method, Object[] args)

Any good suggestions over this problem?

Jerry Bian
  • 3,398
  • 5
  • 25
  • 48
  • 4
    Honestly I would not do it in a hub. Use the hub as a messaging framework to inform the client that new data has arrived. The client then can fetch the data via web api / webmethod or any other transportation technique. – Schadensbegrenzer Jun 17 '14 at 11:00
  • This might help you http://stackoverflow.com/questions/7043566/invalid-uri-the-uri-string-is-too-long – K D Jun 17 '14 at 11:05
  • What is the use case? – Anders Jun 17 '14 at 11:09
  • http://stackoverflow.com/questions/13426014/signalr-and-large-data-transfer – st4hoo Jun 17 '14 at 11:10

5 Answers5

19

You can add a line that makes the message size 'infinite 'in your Startup.cs by setting the MaxIncomingWebSocketMessageSize to null:

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
        app.MapSignalR();
        GlobalHost.Configuration.MaxIncomingWebSocketMessageSize = null;
        }
    }
} 

Mine works with ~200kb of data, 10 messages send consistently. I don't know how well it works if there is more data send per second though.

Alexander
  • 1,286
  • 1
  • 15
  • 21
  • 1
    Working great. nice solution. – No1Lives4Ever Feb 07 '17 at 16:03
  • Didn't wonk for me. May because it's an owin app with Web Api and Signalr and the configuration has to be set up differently – Christoph Adamakis May 31 '17 at 10:46
  • 1
    In case SignalR is inside an Owin App, the above answer has to be combined with this http://jerodkrone.com/signalr-2-0-dependency-injection-using-globalhost/ in order to work. So if the hub is using a custom configuration like var hubconfig = new HubConfiguration(); and a custom resolver like hubconfig.Resolver = new AutofacDependencyResolver(lifetimeScope); then the following 2 lines need to be added in StartUp: GlobalHost.DependencyResolver = hubconfig.Resolver; GlobalHost.Configuration.MaxIncomingWebSocketMessageSize = null; – Christoph Adamakis Jun 29 '17 at 10:08
  • 1
    For dot net core use - - - - - services.AddSignalR(configure => { configure.MaximumReceiveMessageSize = null; }); – Muhammad Waqas Aziz Feb 25 '20 at 13:09
  • 1
    This should really be the accepted answer. It solved the problem for me. Thanks so much @Alexander, I was pulling my hair out over this one! – David R. Oct 12 '20 at 10:03
8

As you have already gathered - this data is too much for SIGNALR by it's own design.

Would it not be a better idea to rather have another process that does this with a normal REST API (GET/POST). Perhaps a message indicating to the user that this needs to be done, as this feels very 'BATCH' like.

Secondly, if it a requirement (possible wrong tool for the job), have you considered compression.

Dane Balia
  • 4,259
  • 5
  • 26
  • 48
  • What about smaller files? I want to send 60KB thumbnails. I can tell my server to accept such a long query string(and it will be quite fast anyway), but SignalR's uri encoding will still block it :S. – tec-goblin Jul 02 '14 at 08:57
4

its easy to do it..

  • split files into byte Array chunks (my tests shows max 10kb per chunk is enough)
  • send chunks to client with an invoke like (calculate totalBytes and the chunk data you have) :

    hubProxy.Invoke("Send", chunk,currentBytes,totalBytes);
    
  • get the chunks from client and create a byte array and append each chunk, signalr sends files syncronously, this means data will be received as your send order
  • you have totalbytes and currentBytes data, now you know all data received, save this byte array to a file with stream or whatever you like..
Machavity
  • 28,730
  • 25
  • 78
  • 91
2

For dot net core in startup.cs in ConfigureServices(IServiceCollection services) method add the below line

services.AddSignalR(conf =>
{
      conf.MaximumReceiveMessageSize = null;
}
0

add this to web config
<httpRuntime maxRequestLength="1048576" executionTimeout="600" />

then add this code to Startup.cs

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        app.MapSignalR();
        GlobalHost.Configuration.MaxIncomingWebSocketMessageSize = null;
    }
}