Read server streaming into client c# - c#

I'm doing an application using server streaming.
The problem is the client doesn't read the data from the server streaming.
This is my proto service:
service UserService {
rpc GetData(Id) returns (stream DataResponse) {}
}
message Id {
int32 id = 1;
}
message DataResponse {
bytes data = 1;
}
c# server is like this:
public override async Task GetData(Id request, IServerStreamWriter<DataResponse> response, ServerCallContext context)
{
var user = {} // get user
foreach (var d in user.Data)
{
await response.WriteAsync(new DataResponse { Data = d });
}
}
And it works because I have a NodeJS client where I can call the server and works perfectly.
Client in Node is
let call = client.getData({id:1})
call.on('data', function (response) {
// do things
})
call.on('end', function () {
// do things
})
And c# client is:
AsyncServerStreamingCall<DataResponse> response = client.GetData(new Id{Id_ = 1});
while(await response.ResponseStream.MoveNext())
{
Console.WriteLine("Into while loop"); // <-- This is never executed
DataResponse current = response.ResponseStream.Current;
Console.WriteLine($"{current.Data}");
}
I've also added a try/catch and it doesn't output anything so it seems MoveNext() is always false.
What is the problem here? Why NodeJS client works and c# client can't read the stream? Have I missed something?
Here is full client.cs class:
class Program
{
const int Port = 50051;
static void Main(string[] args)
{
try
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
var client = new UserService.UserServiceClient(channel);
GetDataStreaming(client);
}
catch (RpcException ex)
{
Console.WriteLine($"Error: {{Code: {ex.StatusCode}, Status: {ex.Status.Detail}}}");
}
}
private static async void GetDataStreaming(UserService.UserServiceClient client)
{
AsyncServerStreamingCall<DataResponse> response = client.GetData(new Id { Id_ = 1 });
while (await response.ResponseStream.MoveNext())
{
Console.WriteLine("Into while loop");
DataResponse current = response.ResponseStream.Current;
Console.WriteLine($"{current.Data.ToStringUtf8()}");
}
}
}

The issue is that your client has ended before the client receive the response. When you call GetDataStreaming(client) in Main it doesn't wait and finishes.
To fix the issue change async void GetDataStreaming to async Task GetDataStreaming.
private static async Task GetDataStreaming(UserService.UserServiceClient client)
{
AsyncServerStreamingCall<DataResponse> response = client.GetData(new Id { Id_ = 1 });
while (await response.ResponseStream.MoveNext())
{
Console.WriteLine("Into while loop");
DataResponse current = response.ResponseStream.Current;
Console.WriteLine($"{current.Data.ToStringUtf8()}");
}
}
Change static void Main to static async Task Main and you should also call channel.ShutdownAsync method at the end.
static async Task Main(string[] args)
{
try
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
var client = new UserService.UserServiceClient(channel);
await GetDataStreaming(client);
await channel.ShutdownAsync();
}
catch (RpcException ex)
{
Console.WriteLine($"Error: {{Code: {ex.StatusCode}, Status: {ex.Status.Detail}}}");
}
}
Another option is to change async void GetDataStreaming to async Task GetDataStreaming and in Main method wait until Task complete.
static void Main(string[] args)
{
try
{
Channel channel = new Channel("127.0.0.1:50051", ChannelCredentials.Insecure);
var client = new UserService.UserServiceClient(channel);
var task = GetDataStreaming(client);
task.Wait();
}
catch (RpcException ex)
{
Console.WriteLine($"Error: {{Code: {ex.StatusCode}, Status: {ex.Status.Detail}}}");
}
}

Related

Message is published from API #1, how do I create a listener in another API?

I've two APIs.
When one of the endpoints are called in API #1, message is sent to queue in Azure Service Bus.
API #2 should listen and make some changes in DB after this message appears in queue.
Message is sent to queue successfully.
Listener part doesn't work because the listener method is never called (and I do not understand how to call it).
Listener in API #2 :
public class MessageConsumer : IMessageConsumer
{
const string connectionString = "stringTakenFromAzure";
private static IQueueClient queueClient;
private CartingDbContext context;
public MessageConsumer(CartingDbContext context)
{
this.context = context;
}
public async Task Consume()
{
queueClient = new QueueClient(connectionString, "cartqueue");
var options = new MessageHandlerOptions(ExceptionReceivedHandler)
{
MaxConcurrentCalls = 1,
AutoComplete = false
};
queueClient.RegisterMessageHandler(ProcessMessageAsync, options);
await queueClient.CloseAsync();
}
private async Task ProcessMessageAsync(Microsoft.Azure.ServiceBus.Message message, CancellationToken cancellationToken)
{
var jsonBody = Encoding.UTF8.GetString(message.Body);
var categoryItem = JsonSerializer.Deserialize<CategoryItem>(jsonBody);
//update item in DB.
var categoryItemInDb = context.CategoryItems.Where(x => x.Id == categoryItem.Id).FirstOrDefault();
if (categoryItemInDb == null)
{
context.CategoryItems.Add(categoryItem);
}
else
{
context.CategoryItems.Update(categoryItem);
}
context.SaveChanges();
await queueClient.CompleteAsync(message.SystemProperties.LockToken);
}
private static Task ExceptionReceivedHandler(ExceptionReceivedEventArgs args)
{
return Task.CompletedTask;
}
}
Program.cs
builder.Services.AddTransient<IMessageConsumer, MessageConsumer>();

Receiving two messages from .net core SignalR and react

I am trying to set up a WebSocket connection using the .net SignalR and React app as a client to be able to send private messages.
Here is my code on the client side:
const setUpSignalRConnection = async () => {
const connection = new HubConnectionBuilder()
.withUrl("http://localhost:5000/messaginghub")
.build();
setConnection(connection);
connection.on("ReceiveMessage", (message: string) => {
console.log("Recieved Message", message);
setChatMessages((oldArray) => [...oldArray, message]);
});
try {
await connection.start();
} catch (err) {
console.log("Errors", err);
}
return connection;
};
const SendMessage = async () => {
if (connection) {
try {
console.log("sending message");
await connection.send("SendPrivateMessage", user.user.email, message);
} catch (e) {
console.log("Errors sending message", e);
}
} else {
alert("No connection to server yet.");
}
};
and my server side code
public async Task SendPrivateMessage(string userEmail, string message)
{
var RecivingMessageUser = _unitOfWork.UserRepository.GetByEmail(userEmail);
var currUserEmail = Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
var sender = _unitOfWork.UserRepository.GetByEmail(currUserEmail);
var newMessage = new MessagesDto
{
FromId = sender.UserId,
ToId = RecivingMessageUser.UserId,
MessageBody = message,
SentAt = DateTime.UtcNow,
};
await Clients.Group(userEmail).SendAsync("ReceiveMessage", message);
_unitOfWork.MessagingRepository.Insert(_mapper.Map<MessagesDto, Messages>(newMessage));
_unitOfWork.SaveChanges();
}
public override Task OnConnectedAsync()
{
var groupName = Context.User.FindFirstValue(ClaimTypes.NameIdentifier);
Groups.AddToGroupAsync(Context.ConnectionId, groupName);
return base.OnConnectedAsync();
}
public override Task OnDisconnectedAsync(Exception ex)
{
Groups.RemoveFromGroupAsync(Context.ConnectionId, Context.User.FindFirstValue(ClaimTypes.NameIdentifier));
return base.OnDisconnectedAsync(ex);
}
With console.logs I see that I am sending a message once and the message is stored in DB once but somehow on the other end, I am getting two received messages.
I am testing it on my local machine in two separate browsers.
What am I doing wrong?
Which method on your back-end is calling twice?
You are telling your message saved in to the DB once so it shouldn't be the SendPrivateMessage method which is calling towice.

.NETFramework 4.6.2 client to .NET Core 3.1 server GRPC client calls failure

I've created GRPC service host under .NET core 3.1 (using Grpc.AspNetCore v2.30 from https://github.com/grpc/grpc-dotnet) and GRPC client under .NET framework 4.6.2 (using Grpc v2.30 from https://github.com/grpc/grpc ). These frameworks for the hosts are a constraint.
I'm running many calls to stress test the service from one client - one time calling Update the other calling UpdateStream. For both I'm facing a strange problem in the client side. It sometimes generates an error - can happen immediately when I start the execution or in the middle of it, and it never recovers - I must stop the client host and restart it again to make it work. it only happens when using different machines - on local host calls there are no issues.
any thoughts/ideas about this issue?
This is the exception that I'm getting for the Update/UpdateStream client calls:
Status(StatusCode="Unknown", Detail="Exception was thrown by handler.", DebugException="Grpc.Core.Internal.CoreErrorDetailException: {"created":"#1595
930477.263000000","description":"Error received from peer ipv4:[ip]:23456","file":"T:\src\github\grpc\workspace_csharp_ext_windows_x86\src\core
\lib\surface\call.cc","file_line":1055,"grpc_message":"Exception was thrown by handler.","grpc_status":2}")
This is the client/server code:
Server:
class Program
{
const int _port = 23456;
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
Console.WriteLine("started - press any key to quit...");
Console.ReadKey();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
options.ConfigureEndpointDefaults(o =>
{
o.Protocols = HttpProtocols.Http2;
});
options.ListenAnyIP(_port);
});
webBuilder.UseStartup<Startup>();
});
}
public class ProxyService : StreamingApi.Protos.StreamingApi.StreamingApiBase
{
private long _handledRequests = 0;
private long _timeDiff = 0;
public override Task<UpdateResponse> Update(UpdateRequest request, ServerCallContext context)
{
Interlocked.Add(ref _timeDiff, (DateTime.Now - TimeSpan.FromTicks(Convert.ToInt64(request.Items["time"]))).Millisecond);
Interlocked.Increment(ref _handledRequests);
return Task.FromResult(new UpdateResponse());
}
public override async Task<UpdateResponse> UpdateStream(IAsyncStreamReader<UpdateRequest> requestStream, ServerCallContext serverCallContext)
{
try
{
while (await requestStream.MoveNext(serverCallContext.CancellationToken))
{
var updateReq = requestStream.Current;
Interlocked.Add(ref _timeDiff, (DateTime.Now - TimeSpan.FromTicks(Convert.ToInt64(updateReq.Items["time"]))).Millisecond);
Interlocked.Increment(ref _handledRequests);
}
}
catch(OperationCanceledException ex)
{
// log
}
return new UpdateResponse();
}
}
class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<ProxyService>();
});
}
}
Client:
partial class Program
{
static void Main(string[] args)
{
Console.WriteLine();
Arguments arguments = new Arguments(args);
if (arguments.Initialized)
{
IProxyClient grpcClient = GetGrpcClient(arguments.Host, arguments.Port, ChannelCredentials.Insecure);
string limitationMsg = arguments.UseLimiter ?
$"limitation of max {arguments.MaxRequestsPerTimeUnit} requests per {arguments.TimeUnitSecs} seconds":
"no limitation";
Console.WriteLine($"\nExecuting {arguments.TotalRequests} requests with StreamMode={arguments.IsStreamMode} using {arguments.Threads} threads with {limitationMsg} ...");
var result = Run(grpcClient, arguments.Threads, arguments.TotalRequests, arguments.MaxRequestsPerTimeUnit, TimeSpan.FromSeconds(arguments.TimeUnitSecs), arguments.UseLimiter, arguments.IsStreamMode).Result;
Console.WriteLine($"Time Taken = {result.Item1}, Total Request Calls = {result.Item2}, Total Errors: {result.Item3}\n");
grpcClient.Disconnect().Wait();
Thread.Sleep(1000);
}
}
private static IProxyClient GetGrpcClient(string host, int port, ChannelCredentials channelCredentials)
{
var channel = new Channel(host, port, channelCredentials);
StreamingApi.Protos.StreamingApi.StreamingApiClient channelClient = new StreamingApi.Protos.StreamingApi.StreamingApiClient(channel);
return new ProxyClient(channel, channelClient);
}
private static async Task<(TimeSpan, int, int)> Run(IProxyClient grpcClient,
int threads,
int requests,
int maxRequestsPerTimeUnit,
TimeSpan timeUnit,
bool useLimiter,
bool isStreamMode)
{
int totalRequestCalls = 0;
int totalErrors = 0;
List<Task> tasks = new List<Task>();
int requestsPerThread = requests / threads;
TimeLimiter timeLimiter = useLimiter ? TimeLimiter.GetFromMaxCountByInterval(maxRequestsPerTimeUnit, timeUnit) : null;
UpdateRequest request = GetMeasuredRequest();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < threads; i++)
{
tasks.Add(Task.Run(async () =>
{
for (int requestIndex = 0; requestIndex < requestsPerThread; requestIndex++)
{
request.Items["time"] = DateTime.Now.Ticks.ToString();
if (useLimiter)
{
await timeLimiter;
}
try
{
if (isStreamMode)
{
await grpcClient.SendUpdateStream(request);
}
else
{
_ = await grpcClient.SendUpdate(request);
}
Interlocked.Increment(ref totalRequestCalls);
}
catch (Exception ex)
{
Interlocked.Increment(ref totalErrors);
Console.WriteLine(ex.Message);
Thread.Sleep(500);
}
}
}));
}
await Task.WhenAll(tasks);
sw.Stop();
return (sw.Elapsed, totalRequestCalls, totalErrors);
}
private static UpdateRequest GetMeasuredRequest()
{
UpdateRequest request = new UpdateRequest { ItemName = "pattern", SubcriptionId = "subscriptionId", IsSnapshot = false};
request.Items["key1"] = "value1";
request.Items["key2"] = "value2";
request.Items["key3"] = "value3";
request.Items["key4"] = "value4";
request.Items["key5"] = "value5";
return request;
}
}
public class ProxyClient : IProxyClient
{
private Channel _channel;
private StreamingApi.Protos.StreamingApi.StreamingApiClient _client;
private AsyncClientStreamingCall<UpdateRequest, UpdateResponse> _updateRequestStreamWriter;
public ProxyClient(Channel channel, StreamingApi.Protos.StreamingApi.StreamingApiClient client)
{
_client = client;
_updateRequestStreamWriter = client.UpdateStream();
}
public async Task Disconnect()
{
await _channel.ShutdownAsync();
}
public async Task<UpdateResponse> SendUpdate(UpdateRequest request)
{
return await _client.UpdateAsync(request);
}
public async Task SendUpdateStream(UpdateRequest request)
{
await _updateRequestStreamWriter.RequestStream.WriteAsync(request);
}
}

How to share a websocket between services in asp net core

I'm setting websocket on .net core using middlewares (it looks something like that https://radu-matei.com/blog/aspnet-core-websockets-middleware/), but I have one issue
for instance my client communicates with websocket via http protocol, in other words, websocket is wrapped in http, when my client sends http request, it goes to the service method where it finally sends message using websocket.
Problem: is it possible to get that websocket reply of messages before returning response?
public async Task SendMessage(WebSocket socket, string response)
{
if (socket.State != WebSocketState.Open)
return;
await socket.SendAsync(buffer: new ArraySegment<byte>(array: Encoding.UTF8.GetBytes(response),
offset: 0,
count: response.Length),
messageType: WebSocketMessageType.Text,
endOfMessage: true,
cancellationToken: CancellationToken.None);
await socket.ReceiveAsync() // When I finally receive some reply message, stop listening and return http response to my client
}
From what you said i understood that you need to share the websocket between multiple services, each using it how it sees fit.Based on this scenario the proposed implementation has a Middleware that contains the different services that require the socket . Special care must be taken when deciding which service does the writing and which does the reading.The websocket is thread-safe in the context of reading-writing at the same time but not in the other scenarios.(writing-writing,reading-reading)
Startup
public void ConfigureServices(IServiceCollection colllection){
collection.AddSingleton<Sender>();
collection.AddSingleton<Receiver>();
}
public void Configure(IApplicationBuilder app)
{
app.UseWebsockets();
app.UseMiddleware<DispatcherMiddleware>(); //receives socket here
}
Middleware
public class DispatcherMiddleware{
private Sender sender;
private Receiver receiver;
private RequestDelegate next;
public Dispatcher(RequestDelegate req,Sender _sender,Receiver _receiver)
{
this.sender=_sender;
this.receiver=_receiver;
this.next=req;
}
public async Task Invoke(HttpContext context){
if(!context.WebSockets.IsWebSocketRequest){
return;
}
await DispatchAsync(context.WebSockets);
}
public async Task DispatchAsync(WebsocketManager manager){
WebSocket socket=await manager.AcceptWebSocketAsync();
Task t1=Task.Run(async()=>await this.sender.SendAsync(socket));
Task t2=Task.Run(async()=>await this.receiver.ReceiveAsync(socket));
await Task.WhenAll(t1,t2);
}
}
Websocket services (example)
public class Sender(){
public async Task SendAsync(WebSocket ws){
try{
while(true){
// ws.SendAsync()
}
}
catch(Exception ex){
}
}
}
public class Receiver{
public async Task ReceiveAsync(WebSocket ws){
try
{
while(true){
//ws.ReceiveAsync(buffer)
}
}
catch (System.Exception)
{
throw;
}
}
}
Edit
You can not perform concurrent reads /writes on the same socket.With this said if you want to use the same socket what you can do is make it thread-safe.Since the operation is async i suggest using the SemaphoreSlim class.
Below is an implementation of a shared socket:
public class SafeSocket {
private const int BUFFER_SIZE = 1024;
private WebSocket socket { get; set; }
private SemaphoreSlim #lock = new SemaphoreSlim(1);
public SafeSocket(WebSocket socket) {
this.socket = socket;
}
public async Task<byte[]> ReadAsync() {
byte[] buffer = ArrayPool<byte>.Shared.Rent(BUFFER_SIZE);
await #lock.WaitAsync();
try {
await this.socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
return buffer;
} catch (Exception) {
throw;
} finally {
#lock.Release();
}
}
}
public class DispatcherMiddleware {
private List<Sender> senders;
private Receiver receiver;
private RequestDelegate next;
public DispatcherMiddleware(RequestDelegate req, List<Sender> _senders, Receiver _receiver) {
this.senders = _senders;
this.receiver = _receiver;
this.next = req;
}
public async Task Invoke(HttpContext context) {
if (!context.WebSockets.IsWebSocketRequest) {
return;
}
await DispatchAsync(context.WebSockets);
}
public async Task DispatchAsync(WebSocketManager manager) {
WebSocket socket = await manager.AcceptWebSocketAsync();
SafeSocket commonSocket = new SafeSocket(socket);
Task[] senderTasks = new Task[this.senders.Count];
for (int senderIndex = 0; senderIndex < senderTasks.Length; senderIndex++) {
int index = senderIndex;// careful at index ! , make copy and use it inside closure !
senderTasks[senderIndex] = Task.Run(async () => {
await commonSocket.ReadAsync();
});
}
}
Keep in mind that the messages order will not be preserved.The same can be applied to the receivers.
So what you will end up with is N senders and K receivers , that at time T :
1 sender will write
1 receiver will read
N-1 senders will wait for the lock
K-1 receivers will wait for the lock
So in the end there will be just 2 operations at any given time.
I do not know if that is what you need though.

How can I get 'fire and forget' behaviour from c# console code to web api

Check the below block of code. I need fire and forget behavior while calling sendMail method (i.e. while calling api). With this block of code I'm getting TaskCancelledException sometimes. Please help
private void ProcessMails()
{
while(true)
{
var mailsToBeProcessed = getAllMailsToBeProcessed(alreadySent, numOfMailsToBeProcessed);
freach(var mail in Mails)
{
sendMail("mailsendingApiUrl", mail)
alreadySent++;
}
Thread.Sleep(60000);
}
}
private async void sendMail(string apiEndPoint, MailContent mailContent)
{
using (var client = new HttpClient())
{
await client.PostAsJsonAsync(apiEndPoint, mailContent.ContentId);
}
}
Try this:
private void ProcessMails()
{
while(true)
{
var mailsToBeProcessed = getAllMailsToBeProcessed(alreadySent, numOfMailsToBeProcessed);
foreach(var mail in mailsToBeProcessed)
{
sendMail("mailsendingApiUrl", mail).Wait();
alreadySent++;
}
}
}
private async Task sendMail(string apiEndPoint, MailContent mailContent)
{
using (var client = new HttpClient())
{
await client.PostAsJsonAsync(apiEndPoint, mailContent.ContentId).ConfigureAwait(false);
}
}

Categories

Resources