How to send big data via SignalR in .NET client - c#

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?

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.

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.

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..

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;
}
}

Related

C# Pass data from While loop to other functions

I have a Node.Js server running on localhost listening to some API requests.
These requests are transferred to my console application via TCP/IP. Here's my c# code which receives data from Node server (hosted at localhost:9999) via GetData() and pass it to another function SendData().
namespace Datatransfer
{
/* global variable declaration*/
class Global
{
public static string receive_data;
}
class Program
{
static string HOST = "localhost";
static int PORT = 9999;
static TcpClient client;
/*Function to receive data*/
static string GetData()
{
while (true)
{
NetworkStream nwStream = client.GetStream();
byte[] bytesToRead = new byte[client.ReceiveBufferSize];
int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
Global.receive_data= Encoding.ASCII.GetString(bytesToRead, 0, bytesRead);
Console.WriteLine("Received data : " + Global.receive_data);
SendData(Global.receive_data)
}
}
/*Function to send data*/
static void SendData(string val)
{
/*Code to process recevied_data..*/
Console.WriteLine("Data to Send : " + Global.receive_data);
/*some codes....*/
}
static void Main(string[] args)
{
client = new TcpClient();
client.Connect(HOST, PORT);
GetData();
}
}
}
I have declared the receive_data as global so as to use it across the application. The code works and I am getting output. Everytime I make an API request to port 9999 am getting output as :
Connection Successfull...
Received data : somestring
Data to Send : somestring
I was wondering if this is an efficient way or not ?
Is there another way by which the receive_data can be passed to other functions 'without' using the function ( ie;SendData() ) inside the while loop.? Or to put it simply, pass data from an infinite while loop to main or other functions.
Any suggestions?
You basically have two options for further processing the data you receive:
Store it somewhere like you did (from a design perspective it doesn't matter how you implement this). Just one thing to think about would be if you want to store a list of received data-"messages", and what happens if you receive another message.
Call a method an pass the received data. This would be the better approach, because you abstract away the implementation and are free to change it (e.g. from storing global to a message-sink mechanism or whatever) without changing your receiving-code.
Approach 2) has more information and more context, because you trigger the method at the point you receive data. In option 1) you have no information about how old the information is, or even if the same information was sent multiple times. So more information is (always) better, if you don't need it in the method call, you are free to condense it again to say a global variable.
For approach 2) you should keep in mind, that the method is running "inside" your loop, so all long-running operations would block the loop. But still you are free to implement it in a way, that allows the message to be processed in another thread (asynchronous).

Signal R Persistant Connection memory Leak

I have a product that uses SignalR (the .NET Framework Version) for its communications. In order for it to work everywhere, we offer that customers can talk to the client via a normal Server>Cloud>Client connection or directly via a SelfHosted SignalR connection.
Everything works fine, except for the fact that it seems that the SelfHosted Connection seems to have a memory leak when sending data directly to the client
The cloud Connection version is this
var _connection = new Connection(ServerUrl);
_connection.Received += OnReceived;
IClientTransport connectionType;
var connectionType = new WebSocketTransport();
_connection.Start(connectionType)
With send from this like this:
private void HandleResult(HeaderPacket headerPacket)
{
_connection.Send(headerPacket); //Send data to Client via Cloud
}
(above code have no memory leaks)
The SelfHosted Version is this
var url = "http://*:8889";
using (WebApp.Start<Startup>(url))
{}
public class Startup
{
public void Configuration(IAppBuilder app)
{
GlobalHost.Configuration.MaxIncomingWebSocketMessageSize = int.MaxValue;
app.MapSignalR();
app.MapSignalR<ServerEndPoint>("/Server");
}
}
public class ServerEndPoint : PersistentConnection
{
private void HandleResult(HeaderPacket response)
{
var data = JsonConvert.SerializeObject(response);
Connection.Send(response.ClientConnectionId, data);
}
protected override Task OnReceived(IRequest request, string connectionId, string data)
{ ... }
}
It is the
Connection.Send(response.ClientConnectionId, data);
Call that makes the memory increase with every call, also with only a single connection.. It should out a few MB and quickly (after 100 calls) it is using 50MB.
If I comment out the send part as the only thing there is no memory leak (indicating it is not the rest of the code that has the memory leak).
I've tried to upgrade latest NuGet packages but the leak persists so I assume it must be some odd setting I'm missing :-(

How to handle incoming TCP messages with a Kestrel ConnectionHandler?

I want to create a TCP listener for my .NET Core project. I'm using Kestrel and configured a new ConnectionHandler for this via
kestrelServerOptions.ListenLocalhost(5000, builder =>
{
builder.UseConnectionHandler<MyTCPConnectionHandler>();
});
So what I have so far is
internal class MyTCPConnectionHandler : ConnectionHandler
{
public override async Task OnConnectedAsync(ConnectionContext connection)
{
IDuplexPipe pipe = connection.Transport;
PipeReader pipeReader = pipe.Input;
while (true)
{
ReadResult readResult = await pipeReader.ReadAsync();
ReadOnlySequence<byte> readResultBuffer = readResult.Buffer;
foreach (ReadOnlyMemory<byte> segment in readResultBuffer)
{
// read the current message
string messageSegment = Encoding.UTF8.GetString(segment.Span);
// send back an echo
await pipe.Output.WriteAsync(segment);
}
if (readResult.IsCompleted)
{
break;
}
pipeReader.AdvanceTo(readResultBuffer.Start, readResultBuffer.End);
}
}
}
When sending messages from a TCP client to the server application the code works fine. The line await pipe.Output.WriteAsync(segment); is acting like an echo for now.
Some questions come up
What response should I send back to the client so that it does not run into a timeout?
When should I send back the response? When readResult.IsCompleted returns true?
How should I change the code to fetch the whole message sent by the client? Should I store each messageSegment in a List<string> and join it to a single string when readResult.IsCompleted returns true?
that is entirely protocol dependent; in many cases, you're fine to do nothing; in others, there will be specific "ping"/"pong" frames to send if you just want to say "I'm still here"
the "when" is entirely protocol dependent; waiting for readResult.IsCompleted means that you're waiting for the inbound socket to be marked as closed, which means you won't send anything until the client closes their outbound socket; for single-shot protocols, that might be fine; but in most cases, you'll want to look for a single inbound frame, and reply to that frame (and repeat)
it sounds like you might indeed be writing a one-shot channel, i.e. the client only sends one thing to the server, and after that: the server only sends one thing to the client; in that case, you do something like:
while (true)
{
var readResult = await pipeReader.ReadAsync();
if (readResult.IsCompleted)
{
// TODO: not shown; process readResult.Buffer
// tell the pipe that we consumed everything, and exit
pipeReader.AdvanceTo(readResultBuffer.End, readResultBuffer.End);
break;
}
else
{
// wait for the client to close their outbound; tell
// the pipe that we couldn't consume anything
pipeReader.AdvanceTo(readResultBuffer.Start, readResultBuffer.End);
}
As for:
Should I store each messageSegment in a List<string> and join it to a single string when
The first thing to consider here is that it is not necessarily the case that each buffer segment contains an exact number of characters. Since you are using UTF-8, which is a multi-byte encoding, a segment might contain fractions of characters at the start and end, so: decoding it is a bit more involved than that.
Because of this, it is common to check IsSingleSegment on the buffer; if this is true, you can just use simple code:
if (buffer.IsSingleSegment)
{
string message = Encoding.UTF8.GetString(s.FirstSpan);
DoSomethingWith(message);
}
else
{
// ... more complex
}
The discontiguous buffer case is much harder; basically, you have two choices here:
linearize the segments into a contiguous buffer, probably leasing an oversized buffer from ArrayPool<byte>.Shared, and use UTF8.GetString on the correct portion of the leased buffer
use the GetDecoder() API on the encoding, and use that to populate a new string, which on older frameworks means overwriting a newly allocated string, or in newer frameworks means using the string.Create API
Frankly, "1" is much simpler. For example (untested):
public static string GetString(in this ReadOnlySequence<byte> payload,
Encoding encoding = null)
{
encoding ??= Encoding.UTF8;
return payload.IsSingleSegment ? encoding.GetString(payload.FirstSpan)
: GetStringSlow(payload, encoding);
static string GetStringSlow(in ReadOnlySequence<byte> payload, Encoding encoding)
{
// linearize
int length = checked((int)payload.Length);
var oversized = ArrayPool<byte>.Shared.Rent(length);
try
{
payload.CopyTo(oversized);
return encoding.GetString(oversized, 0, length);
}
finally
{
ArrayPool<byte>.Shared.Return(oversized);
}
}
}

How to perform database operation independently?

I have 1 exe which is nothing bit a Windows form which will continuously run in background and will watch my serial port and I have 1 event data receive event which fires as my serial port receive data.
As soon as I receive data in this event I will pass this data to another event handler which saves this data in database through web api method.
But data to my serial port will be coming frequently so I want to save this data to my database independently so that my database insert operation doesn't block my incoming serial port data.
This is my code:
void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)//Fires as my serial port receives data
{
int dataLength = _serialPort.BytesToRead;
byte[] data = new byte[dataLength];
int nbrDataRead = _serialPort.Read(data, 0, dataLength);
if (nbrDataRead == 0)
return;
// Send data to whom ever interested
if (NewSerialDataRecieved != null)
{
NewSerialDataRecieved(this, new SerialDataEventArgs(data)); //pass serial port data to new below event handler.
}
}
void _spManager_NewSerialDataRecieved(object sender, SerialDataEventArgs e) //I want this event handler to run independently so that database save operation doenst block incoming serial port data
{
if (this.InvokeRequired)
{
// Using this.Invoke causes deadlock when closing serial port, and BeginInvoke is good practice anyway.
this.BeginInvoke(new EventHandler<SerialDataEventArgs>(_spManager_NewSerialDataRecieved), new object[] { sender, e });
return;
}
//data is converted to text
string str = Encoding.ASCII.GetString(e.Data);
if (!string.IsNullOrEmpty(str))
{
//This is where i will save data to through my web api method.
RunAsync(str).Wait();
}
}
static async Task RunAsync(string data)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:33396/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new StringContent(data);
var response = await client.PostAsJsonAsync<StringContent>("api/Service/Post", content);//nothing happens after this line.
}
}
Web api controller:
public class MyController : ApiController
{
[HttpPost]
public HttpResponseMessage Post(HttpRequestMessage request)
{
var someText = request.Content.ReadAsStringAsync().Result;
return new HttpResponseMessage() { Content = new StringContent(someText) };
}
}
But here problem is:
var response = await client.PostAsJsonAsync<StringContent>("api/Service/Post", content);
Nothing happens after this line that is operation blocks on this line.
So can anybody guide me with this?
By independently we determined in the SO C# chat room that you really mean "Asynchronously".
Your solution is the code above, saving this data to a WebAPI endpoint so any solution to the problem needs to be in 2 parts ...
PART 1: The Client Part
On the client all we need to do is make the call asynchronously in order to free up the current thread to carry on receiving data on the incoming serial port, we can do that like so ...
// build the api client, you may want to keep this in a higher scope to avoid recreating on each message
var api = new HttpClient();
api.BaseAddress = new Uri(someConfigVariable);
// asynchronously make the call and handle the result
api.PostAsJsonAsync("api/My", str)
.ContinueWith(t => HandleResponseAsync(t.Result))
.Unwrap();
...
PART 2: The Server Part
Since you have web api i'm also going to assume you are using EF too, the common and "clean" way to do this, with all the extras stripped out (like model validation / error handling) might look something like this ...
// in your EF code you will have something like this ...
Public async Task<User> SaveUser(User userModel)
{
try
{
var newUser = await context.Users.AddAsync(userModel);
context.SavechangesAsync();
return newUser;
}
catch(Exception ex) {}
}
// and in your WebAPI controller something like this ...
HttpPost]
public async Task<HttpResponseMessage> Post(User newUser)
{
return Ok(await SaveUser(newUser));
}
...
Disclaimer:
The concepts involved here go much deeper and as I hinted above, much has been left out here like validation, error checking, ect but this is the core to getting your serial port data in to a database using the technologies I believe you are using.
Key things to read up on for anyone wanting to achieve this kind of thing might include: Tasks, Event Handling, WebAPI, EF, Async operations, streaming.
From what you describe it seems like you might want to have a setup like this:
1) your windows form listens for serial port
2) when new stuff comes to port your windows forms app saves it to some kind of a queue (msmq, for example)
3) you should have separate windows service that checks queue and as it finds new messages in a queue it sends request to web api
Best solution for this problem is to use ConcurrentQueue.
Just do search on google and you will get planty of samples.
ConcurrentQueue is thread safe and it support writing and reading from multiple threads.
So the component listening to the searal port can write data to the queue. And you can have 2 or more tasks running parallel which listening to this queue and update db as soon as it receives data.
Not sure if it's the problem, but you shouldn't block on async code. You are doing RunAsync(str).Wait(); and I believe that's the problem. Have a look at this blog post by Stephen Cleary:
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

System.ObjectDisposedException throwed on WebSocket communication

I'm trying to communicate between a web browser client and an ASP.NET server using WebSockets.
I make a set of requests, of different sizes and with some seconds of elapsed time between each of them. The three first ones pass correctly, but a precise one, with nothing in particular from the other, close the WebSocket connection, throw an exception on server side.
The error message and stack trace of this exception look like this :
FATAL ERROR: Cannot access a disposed object.
Object name: 'System.Web.WebSockets.AspNetWebSocket'.
at System.Web.WebSockets.AspNetWebSocket.ThrowIfDisposed()
at System.Web.WebSockets.AspNetWebSocket.SendAsyncImpl(ArraySegment 1 buffer, WebSocketMessageType messageType, Boolean endOfMessage, CancellationToken cancellationToken, Boolean performValidation)
at System.Web.WebSockets.AspNetWebSocket.SendAsync(ArraySegment 1 buffer, WebSocketMessageType messageType, Boolean endOfMessage, CancellationToken cancellationToken)
at [my code path here...]
It may be a threading problem, because I'm using async methods everywhere from functions that communicate with websockets.
I know the exception is throwed from this code (at socket.SendAsync):
public class SocketTranslater
{
private WebSocket socket;
private JavaScriptSerializer serializer;
// [...]
private ArraySegment<byte> Encode(Object data)
{
string json = serializer.Serialize(data);
return (new ArraySegment<byte>(Encoding.UTF8.GetBytes(json)));
}
public async Task Send(Object packet)
{
ArraySegment<byte> encoded = this.Encode(packet);
await socket.SendAsync(encoded, WebSocketMessageType.Text, true, CancellationToken.None);
}
}
The socket is created from another class :
public class EventSender : IHttpHandler
{
private static List<SocketTranslater> socketTranslaters = new List<SocketTranslater>();
public void ProcessRequest(HttpContext context)
{
this.id = context.Request.UserHostName + ":" + context.Request.UserAgent;
if (context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(ProcessSocket);
}
}
private async Task ManageSocket(AspNetWebSocketContext context)
{
WebSocket socket = context.WebSocket;
SocketTranslater translater = new SocketTranslater(socket);
translaters.add(translater);
while (true)
{
// Manage messages, using the other class to send responses.
translater.Send(/* Any struct here */);
}
}
Unhopefully, my project is too big to put all the code here.
Any idea of error source, or additional information that you would require ?
UPDATE:
After some more tests, I don't have this exception from time to time. I always do the same thing, but the server seems to have a random comportment.
That makes my problem even weirder...
Finally, after some more tests and interesting questions and answer from here (like this one), I understood:
My problem was that I was stocking WebSockets in a Dictionary linked with hostnames. So on the first connection of a client on my server, everything worked correctly. But if I refresh the page, the websocket was closed by the server (because there was no chance to use it again) et another one was created.
But as I used the same key for both sockets, the deprecated and the new one, I was trying to answer to the new client with the previous socket, that was closed. (disposed = closed for ASP.NET).
So the only thing that I had to do is to remove a socket from the list on the client disconnection (at the end of ManageSocket). And forbid a client to connect twice with the same hostname.
I didn't mention the part where I was linking sockets with hostnames, so I admit you couldn't really help me... I apologize.

Categories