I have an P2P network app which acts as a server and client.
The app connects to multiple clients in a mesh and sends "data" to each other once the data has been processed.
If a client gets an incoming request then creates their own request before completing the incoming request it seems to deadlock and neither requests complete. Everything works fine unless a create is sent which processing one.
Is this because I'm not using async on the server methods or is this because I shouldn't send a request while processing one?
proto
rpc submit_data(DataSubmission) returns (DataSubmissionAck);
RPC Server
public async void Start()
{
_server = new Server()
{
Services = {
DataService.BindService(new DataService())),
},
Ports = { new ServerPort(_IpAddress, _Port, ServerCredentials.Insecure) }
};
_server.Start();
}
Client method
public void SubmitData(Data data)
{
...
var serverResponse = _serviceClient.submit_data(request);
...
}
Server method
public override Task<DataSubmissionAck> submit_data(DataSubmission request, ServerCallContext context)
{
DataSubmissionAck clientResponse = new DataSubmissionAck();
return Task.FromResult(clientResponse);
}
What seems to have worked is making the server and client-side calls all async.
Related
I have written an Application where I am using SignalR. I am sending connectionId from Client to Server(controller).
Everything is working fine with single browser (request will sent to server with connectionId="conn_1") and signalR is sending response to only conn_1, but when i open new browser and send a request from that client the previous connection gets disposed. Which means only one connection with particular connectionId remains alive.
Is there any way SignalR can not dispose and send response to both with data they want?
I am new to SignalR and would really appropriate any help or guidance.
Angular SignalRService to start connection with server
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(this.paymentDraftHubUrl)
.build();
return this.hubConnection
.start()
.then(() => this.hubConnectionStatus = 'Connection started')
.catch(err => (this.hubConnectionStatus = 'Error while starting connection: ' + err));
}
sending connectionId from client component to Api
this.signalRService.startConnection().then((connection) => {
this.connectionId = connection.toString();
//Calling Api
this.getAllTransactionException(
this.connectionId,
this.pageNumber,
this.pageSize
}
MyHub class in C#
public class PaymentDraftServiceHub : Hub, IPaymentDraftHub
{}
Controller for API
using timer to keep calling repository for new data,
[HttpGet]
[Route("GetCsrTranactions")]
public IActionResult GetCsrTranactions([FromQuery] TransactionExceptionDataRequest queryParams)
{
TimeManager.Dispose();
var timerManager = new TimeManager(async () =>
await _paymentDraftHub.Clients.Clients.Client(queryParams.ConnectionId).SendAsync(SignalRConstants.TransferPaymentDraftServiceData, await _paymentTransactionRepository.GetCsrTranactionsAsync(queryParams)));
var response = new ResponseMessage { Message = "Accepted", Code = "201" };
return Ok(response);
}
Client can have multiple connections with multiple connection IDs if client connect from multiple browser windows or tabs.
According to the code you provided, we can find that you just pass connection ID of SignalR client within current active browser tab/window to your controller, and in your controller action, you use this code snippet .Client(queryParams.ConnectionId).SendAsync() to send message to a specific client, so other browser windows or tabs would not receive the message.
If you'd like to send message(s) to a client with multiple connections, you need to map SignalR users to connection Ids and retain information about users-to-connectionIds mapping, then you can get all connectionIds of a client and send messages to that client with with multiple connectionIds, like below.
//code logic here
//to get all connectinIds of a client/user
//from user-to-connectionIds mapping table
await _paymentDraftHub.Clients.Clients(connectionIds_here).SendAsync("method_here",args_here);
I have an API controller that creates a web socket connection and can connect and interact with fine when using JavaScript, but I'd also like to be able to connect to it using a C# client. I've tried a few different libraries (Alchemy, WebSocket4Net, and WebSocket-Sharp), and they all seem to be timing out during connection open.
Here's the code for the server side:
class WsController : ApiController
{
public HttpResponseMessage Get(string id)
{
HttpContext.Current.AcceptWebSocketRequest(new WsHandler(id));
return Request.CreateResponse(HttpStatusCode.SwitchingProtocols);
}
internal class WsHandler : WebSocketHandler
{
public WsHandler(string id) { /* ... */ }
}
}
If I'm using a JS client, I can use var ws = new WebSocket(url + "?id=someid"); to connect and then send and receive messages. When using a C# client, I get stuck during the handshake and the connect times out. Here's some example code using WebSocket4Net.
var ws = new WebSocket(url);
ws.Opened += new EventHandler(ws_Connect);
ws.Closed += new EventHandler(ws_Disconnect);
ws.Error += new EventHandler<ErrorEventArgs>(ws_Error);
ws.MessageReceived += new EventHandler<MessageReceivedEventArgs>(ws_Receive);
ws.Open(); // Timeout on this line. No errors are thrown to ws_Error.
Is there another way to create a websocket server using MVC's ApiController?
I was using Alchemy websockets for both my client and server but ran into a problem with corrupted/dropped messsages. So I'm trying out another server side implementation. I implemented the server using Fleck, and when I send messages using javascript, the server receives all the messages, solving my previous problem.
However, I need to be able to send messages to the websocket server from a C# client also. Since Fleck does not have a client side implementation in C#, I thought I'd stick with Alchemy. I left the client-side code unchanged so I thought it should just connect to the server as before, however, no messages are being received (though they are being sent according to the debugger).
Here is my server side implementation (Fleck):
private void OnStartWebSocketServer()
{
var server = new WebSocketServer("ws://localhost:11005");
server.Start(socket =>
{
socket.OnOpen = () => Console.WriteLine("Open!");
socket.OnClose = () => Console.WriteLine("Close!");
socket.OnMessage = message => OnReceive(message);
});
}
private static void OnReceive(String message)
{
UpdateUserLocation(message);
}
Here is my client side implementation (Alchemy):
class WSclient
{
WebSocketClient aClient;
public WSclient(String host, String port)
{
aClient = new WebSocketClient("ws://" + host + ":" + 11005 + "/chat")
{
OnReceive = OnReceive,
OnSend = OnSend,
OnConnect = OnConnected,
OnConnected = OnConnect,
OnDisconnect = OnDisconnect
};
aClient.Connect();
}
...
public void Send(String data)
{
aClient.Send(data);
}
I thought it might have something to do with the fact that the Alchemy client requires a channel at the end of the connection string '/chat'. However leaving it blank, or just the '/' gives an error.
I tried creating a simple HTTP server using System.Net.HTTPListener, but it doesn't receive connections from other computers in the network. Example code:
class HTTPServer
{
private HttpListener listener;
public HTTPServer() { }
public bool Start()
{
listener = new HttpListener();
listener.Prefixes.Add("http://+:80/");
listener.Start();
listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
return true;
}
private static void ListenerCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
Console.WriteLine("New request.");
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
byte[] page = Encoding.UTF8.GetBytes("Test");
response.ContentLength64 = page.Length;
Stream output = response.OutputStream;
output.Write(page, 0, page.Length);
output.Close();
}
}
class Program
{
static void Main(string[] args)
{
HTTPServer test = new HTTPServer();
test.Start();
while (true) ;
}
}
Is there something wrong with this code, or is there another problem?
I've tried running the application with administrator privileges, but when I browse to the computer's IP address (i.e. 192.168.1.100) on another computer, I never receive the request. The server works fine if the request is sent from the same computer as where the application is running (using "localhost", "127.0.0.1" and "192.168.1.100"). Pinging works fine. I've also tried nginx, and that works perfectly over the network.
I'm using HTTPListener as a lightweight server to deliver a webpage with a Silverlight XAP file with some dynamic init params, clientaccesspolicy.xml and a simple mobile HTML page.
Firewall
I also thought first of the Firewall. However the problem where my endpoints:
From a tutorial I had a code like the following
String[] endpoints = new String[] {
"http://localhost:8080/do_something/",
// ...
};
This code only works locally and only if you use localhost. To be able to use the IP, I changed it to
String[] endpoints = new String[] {
"http://127.0.0.1:8080/do_something/",
// ...
};
This time the request by ip adress worked, but the server did not respond to remote requests from another ip. What got me working for me was to use a star (*) instead of localhost and 127.0.0.1, so the following code:
String[] endpoints = new String[] {
"http://*:8080/do_something/",
// ...
};
Just leaving this here if somebody stumbles upon this post as I did.
I have a WCF service client and it was generated using the /async argument with svcutil.exe, so it uses the asynchronous programming model. When I make multiple asynchronous requests with a single client though it serializes the execution of those requests.
Here is an example that shows how I am executing the parellel requests.
[TestMethod]
public void AsyncTest()
{
var req1 = Helpers.Request["Symbol"];
var req2 = Helpers.Request["Summary"];
using(var client = new SoapClientAsync(_TcpBinding, _TestRunConfig.ServiceAddress))
{
IAsyncResult[] results = new[]
{
client.BeginExec(req1, new AsyncCallback(asyncComplete), new MyAsyncState { Client = client, Request = req1 }),
client.BeginExec(req2, new AsyncCallback(asyncComplete), new MyAsyncState { Client = client, Request = req2 })
};
WaitHandle[] waits = results.Select(i => (MyAsyncState)i.AsyncState).Select(i => i.WaitEvent).ToArray();
WaitHandle.WaitAll(waits);
var states = results.Select(i => (MyAsyncState)i.AsyncState);
foreach (var state in states)
{
Console.WriteLine(string.Format("client {0}", state.Watch.ElapsedMilliseconds));
Console.WriteLine(string.Format("server {0}", state.Response.ExecutionSummary.Single(i => i.Message == "Total").Duration));
}
}
}
private void asyncRequestComplete(IAsyncResult ar)
{
TestAsyncState state = (MyAsyncState)ar.AsyncState;
state.Response = state.Client.EndExec(ar);
state.Watch.Stop();
state.WaitEvent.Set();
}
Here is the output that I am seeing:
client 26
server 24
client 53
server 26
The server is reporting a consistent execution time of ~25ms. But it is pretty obviously serializing the execution of each request on the client side.
What is the proper way to execute parellel requests against a web service endpoint using WCF?
The answer is quite simply to use more than one instance of the client and issue a single request on each. Soap clients are extremely lightweight, so I wouldn't hesitate to use as many of them as makes sense for your scenario.