I developed a websocket on server & client machines using Microsoft websocket.
Clients upload and download bookings with the server if there is any change from both sides. It is a real time communication between clients and server using Microsoft websocket.
It works fine with a few clients only. However, when I set up for around 100 clients. it makes download speeds of the server slow down. Normally, the internet speed of the server is around 900 Mbps and it drops to 50Mbps.
I configured websocket on IIS. I checked on IIS, worker processes use too much CPU and bandwidth, CPU sometimes up to 100%.
How can I avoid this performance degradation?
Here is the sample of my code
public class booking : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
try
{
if (context.IsWebSocketRequest)
{
context.AcceptWebSocketRequest(new MicrosoftWebsockets());
}
}
catch (Exception e)
{
MicrosoftWebsockets.WriteErrorWebSocket(e.Message, tillNumber);
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
public class MicrosoftWebsockets : WebSocketHandler
{
private static WebSocketCollection clients = new WebSocketCollection();
public string businessid;
public override void OnOpen()
{
try
{
..............
}
catch (Exception e)
{
WriteLogError(e.Message);
}
}
public override void OnMessage(string message)
{
try
{
if (!string.IsNullOrEmpty(message))
{
ReceiveMessage(message);
}
}
catch (Exception e)
{
WriteLogError(e.Message);
}
}
public void ReceiveMessage(string message)
{
try
{
dynamic json = JsonConvert.DeserializeObject<dynamic>(message);
var s = json.method;
if (s == "updatechanged")
{
updatechanged(this.businessid, json);
}
}
catch (Exception e)
{
WriteLogError(e.Message);
}
}
public async Task updatechanged(string businessid, dynamic json, string message)
{
List<Appointments> appts = DataHelper.ConvertJonToAppointmentList(businessid, message);
if (appts != null && appts.Count > 0)
{
var resltList = await UpdateAppointment(businessid, appts);
JArray props = new JArray();
if (resltList != null && resltList.Count > 0)
{
foreach (var p in resltList)
{
string output = JsonConvert.SerializeObject(p);
props.Add(output);
}
JObject joe = new JObject();
joe["jsonrpc"] = "2.0";
joe["id"] = json.id;
joe["method"] = "serverreturnappointment";
joe.Add(new JProperty("params", props));
string result = JsonConvert.SerializeObject(joe);
clients.SingleOrDefault(r => ((MicrosoftWebsockets)r).businessid == this.businessid).Send(result);
}
}
}
}
Related
In my Prism module in ViewModel class in OnNavigatedTo method
I would like to fill an ObservableCollection with results of multiple async calls without waiting for all calls to complete.
I am using answer from this question:
How to hydrate a Dictionary with the results of async calls?
The following code is a cleaned-up version of my real code:
My status class:
public class Status
{
public string ipAddress;
public string status;
}
My view model:
using Prism.Mvvm;
using Prism.Regions;
public class StatusViewModel : BindableBase, INavigationAware
{
ObservableCollection<Status> statusCollection = new ObservableCollection<Status>();
HttpClient httpClient = new HttpClient();
Ping ping = new Ping();
List<string> ipAddressList = new List<string>();
public void OnNavigatedTo(NavigationContext navigationContext)
{
GetEveryStatus();
}
public void GetEveryIp() // this is not important, works ok
{
var addressBytes = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).GetAddressBytes();
for (byte i = 1; i < 255; ++i)
{
addressBytes[3] = i;
string ipAddress = new IPAddress(addressBytes).ToString();
if (ping.Send(ipAddress, 10).Status == IPStatus.Success)
{
ipAddressList.Add(ipAddress);
}
}
}
public void GetEveryStatus() // this is important, here is the problem
{
GetEveryIp();
var task = GetStatusArray(ipAddressList);
statusCollection.AddRange(task.Result);
}
// solution from stackoverflow, but it throws exception
public async Task<Status[]> GetStatusArray(List<string> ipAddressList)
{
Status[] statusArray = await Task.WhenAll(
ipAddressList.Select(
async ipAddress => new Status(
ipAddress,
await httpClient.GetStringAsync("http://" + ipAddress + ":8080" + "/status")
)
)
);
return statusArray;
}
}
but that didn't work because GetStringAsync can throw an exception, so I changed it to this:
public void GetEveryStatus() // this is important, here is the problem
{
GetEveryIp();
foreach (string ipAddress in ipAddressList)
{
try
{
var task = httpClient.GetStringAsync("http://" + ipAddress + ":8080" + "/status");
statusCollection.Add(new Status(ipAddress, task.Result));
}
catch (Exception)
{
}
}
}
but it still doesn't work.
What is the right way to do this? Thank you!
Thanks to #AccessDenied for explaining the role of async in interface implementation.
Thanks to #Selvin for explaining Task.Result and Task.Wait.
If anyone is interesed in the final solution, here it is:
PositioningModule is a hardware device, this class has nothing to do with Prism.Modularity.IModule
public class PositioningModule
{
public string IpAddress { get; set; }
public PositioningModuleStatus PositioningModuleStatus { get; set; }
public PositioningModule(string ipAddress, PositioningModuleStatus positioningModuleStatus)
{
IpAddress = ipAddress;
PositioningModuleStatus = positioningModuleStatus;
}
}
The ViewModel:
I had to use BindingOperations.EnableCollectionSynchronization and lock on the ObservableCollection. This is the main reason why it didn't work async before!
Changing OnNavigatedTo to async blocked the UI, so I used Task.Run().
using Prism.Mvvm;
using Prism.Regions;
public class DomePositioningViewModel : BindableBase, INavigationAware
{
ObservableCollection<PositioningModule> _positioningModuleCollection = new ObservableCollection<PositioningModule>();
readonly object _lock = new object();
DomePositioningModel _domePositioningModel = new DomePositioningModel();
public DomePositioningViewModel()
{
BindingOperations.EnableCollectionSynchronization(_positioningModuleCollection, _lock);
}
public /* async */ void OnNavigatedTo(NavigationContext navigationContext)
{
//await _domePositioningModel.ScanForModulesAsync(AddModule); - this blocks the UI
Task.Run(() => _domePositioningModel.ScanForModulesAsync(AddModule));
}
private void AddModule(PositioningModule module)
{
lock (_lock)
{
_positioningModuleCollection.Add(module);
}
}
}
The Model:
I changed Send to SendPingAsync and I had to use new Ping() instead of ping.
Using Select instead of foreach to make the calls parallel made everything much faster!
#define PARALLEL
public class DomePositioningModel
{
private readonly HttpClient _httpClient = new HttpClient();
public DomePositioningModel()
{
_httpClient.Timeout = TimeSpan.FromMilliseconds(50);
}
public async Task ScanForModulesAsync(Action<PositioningModule> AddModule)
{
List<string> ipAddressList = new List<string>();
var addressBytes = Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).GetAddressBytes();
for (addressBytes[3] = 1; addressBytes[3] < 255; ++addressBytes[3])
{
ipAddressList.Add(new IPAddress(addressBytes).ToString());
}
//Ping ping = new Ping(); - this behaves strangely, use "new Ping()" instead of "ping"
#if PARALLEL
var tasks = ipAddressList.Select(async ipAddress => // much faster
#else
foreach (string ipAddress in ipAddressList) // much slower
#endif
{
PingReply pingReply = await new Ping().SendPingAsync(ipAddress, 10); // use "new Ping()" instead of "ping"
if (pingReply.Status == IPStatus.Success)
{
try
{
string status = await _httpClient.GetStringAsync("http://" + ipAddress + ":8080" + "/status");
if (Enum.TryParse(status, true, out PositioningModuleStatus positioningModuleStatus))
{
AddModule?.Invoke(new PositioningModule(ipAddress, positioningModuleStatus));
}
}
catch (TaskCanceledException) // timeout
{
}
catch (HttpRequestException) // could not reach IP
{
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex.Message);
}
}
}
#if PARALLEL
);
await Task.WhenAll(tasks);
#endif
}
}
It didn't benchmark it because the difference is so obvious - about 0.5 sec instead of 14 sec!
I'm trying to create a UWP service app on the Raspberry Pi3 which provides the access to the on board UART. I'm facing an issue about the AppConnection Request/response.
this is the service method that handles the incoming requests from client apps
internal class Inbound
{
public static async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
{
var messageDeferral = args.GetDeferral();
var response = new ValueSet();
bool success = false;
var msg = args.Request.Message.Keys;
if (args.Request.Message.TryGetValue(ServiceApiRequests.Keys.Command, out object command))
{
try
{
switch (command)
{
case ServiceApiRequests.CommandValues.UartWrite:
if (args.Request.Message.TryGetValue(ServiceApiRequests.Keys.UartTxBuffer, out object txBuffer))
{
string rxBuff = "";
success = await Pi3.Peripherals.Uart.GerInstance(57600).Write((string)txBuffer);
if (success)
{
Debug.WriteLine("Tx: " + (string)txBuffer);
if (args.Request.Message.TryGetValue(ServiceApiRequests.Keys.ReadUartResponse, out object getResponse))
{
if ((string)getResponse == ServiceApiRequests.ReadUartResponse.Yes)
{
rxBuff = await Pi3.Peripherals.Uart.GerInstance(57600).Read();
Debug.WriteLine("Rx: " + rxBuff);
}
}
}
response.Add(ServiceApiRequests.Keys.UartRxBuffer, rxBuff);
}
break;
}
}
catch (Exception ex)
{
success = false;
}
}
response.Add(new KeyValuePair<string, object>(ServiceApiRequests.Keys.Result, success ? ServiceApiRequests.ResultValues.Ok : ServiceApiRequests.ResultValues.Ko));
var result = await args.Request.SendResponseAsync(response);
if (result == AppServiceResponseStatus.Failure)
{
Debug.WriteLine("Failed to send the response");
}
messageDeferral.Complete();
}
}
As you can figure out, the Uart class is get using the Singleton pattern using the method Pi3.Peripherals.Uart.GerInstance(57600).
Following the code i using for send the request from the client app.
public static class Uart
{
public static IAsyncOperation<string> SendCommand(this AppServiceConnection DriverControllerConnection, string txBuffer, string awaitResponse = ServiceApiRequests.ReadUartResponse.Yes)
{
return _SendCommand(DriverControllerConnection, txBuffer, awaitResponse).AsAsyncOperation();
}
private static async Task<string> _SendCommand(AppServiceConnection DriverControllerConnection, string txBuffer, string awaitResponse)
{
AppServiceResponse response = null;
string response_str = "";
try
{
if (DriverControllerConnection != null)
{
response = await DriverControllerConnection.SendMessageAsync(new ServiceApiRequests.UartWrite().GetCommand(txBuffer, awaitResponse));
if (response.Status == AppServiceResponseStatus.Success)
{
if (response.Message.TryGetValue(ServiceApiRequests.Keys.Result, out object result))
{
if ((string)result == ServiceApiRequests.ResultValues.Ok && awaitResponse == ServiceApiRequests.ReadUartResponse.Yes)
{
response_str = response.Message[ServiceApiRequests.Keys.UartRxBuffer] as string;
}
}
}
}
}
catch (Exception ex)
{
// TODO: log
}
return response_str;
}
}
The system works well just for a while, until i have response.Status == AppServiceResponseStatus.Success , then the result of the request changes and it becomes AppServiceResponseStatus.Failure. This way the program counter never steps into the condition if (response.Status == AppServiceResponseStatus.Success).
Any idea about the cause?
Thank you so much for the help.
EDIT
Follow the suggestions, i added an handler for the ServiceClosed event. This is the main class.
public sealed class DriverListener : IBackgroundTask
{
private BackgroundTaskDeferral backgroundTaskDeferral;
private AppServiceConnection appServiceConnection;
public void Run(IBackgroundTaskInstance taskInstance)
{
backgroundTaskDeferral = taskInstance.GetDeferral();
// taskInstance.Canceled += OnTaskCanceled;
var triggerDetails = taskInstance.TriggerDetails as AppServiceTriggerDetails;
appServiceConnection = triggerDetails.AppServiceConnection;
appServiceConnection.RequestReceived += Inbound.OnRequestReceived;
appServiceConnection.ServiceClosed += OnTaskCanceled;
}
private void OnTaskCanceled(AppServiceConnection sender, AppServiceClosedEventArgs reason)
{
if (this.backgroundTaskDeferral != null)
{
Debug.WriteLine("ServiceClosed");
// Complete the service deferral.
this.backgroundTaskDeferral.Complete();
}
}
}
Placing a breakpoint in this function, i see that it was never triggered.
The app connection is opened using the singleton pattern, and putted in a dll that i use in the client app
public static AppServiceConnection GetDriverConnectionInstance()
{
if (_DriverConnectionInstance == null)
{
try
{
_DriverConnectionInstance = OpenDriverConnection().AsTask().GetAwaiter().GetResult();
}
catch
{
}
}
return _DriverConnectionInstance;
}
I also add a Request to the service that toggles a led, and i noticed that the led status changes but the response from the app service is still "Failure" and the message is null.
The AppService has a default lifetime of 25sec, unless it is being requested by the foreground experience. When the service shuts down the connection, your client process will receive the ServiceClosed event, so you know you will need to reopen the connection the next time you want to send a request.
I have a WCF service as an OPC client that interfaces with an OPC DA server, also I have a WCF client that interacts with WCF and launches it (uses its methods)
The problem is that:
When I use WCF method connectToOPC() at first time, the connection can be break after few seconds, or not. DataChange stops coming.
But when I restart connectToOPC, the connection remains active and does not fall. What happens and why?
WCF Service:
public void connectToOPC(string OPCUrl, int GroupUpdateTime)
{
try
{
Main(OPCUrl, GroupUpdateTime);
}
catch (Exception ex) { Log(ex.Message, ""); }
}
public bool Run(string OPCUrl, int GroupUpdateTime)
{
try
{
url = new Opc.URL(OPCUrl);
server.Connect(url, new Opc.ConnectData(new System.Net.NetworkCredential()));
groupState = new Opc.Da.SubscriptionState();
groupState.Name = "MyGroup";
groupState.UpdateRate = GroupUpdateTime;
groupState.Active = true;
groupRead = (Opc.Da.Subscription)server.CreateSubscription(groupState);
groupRead.DataChanged += new Opc.Da.DataChangedEventHandler(group_DataChanged);
items = new Opc.Da.Item[ListOfTags.Count];
for (int i = 0; i < ListOfTags.Count; i++)
{
items[i] = new Opc.Da.Item();
items[i].ItemName = ListOfTags[i];
}
items = groupRead.AddItems(items);
}
catch (Exception ex)
{
Log(ex.Message, "");
return false;
}
return true;
}
void group_DataChanged(object subscriptionHandle, object requestHandle,Opc.Da.ItemValueResult[] values)
{
//its stops here at first time
}
And code of WCF client(WPF):
public partial class MainWindow : Window
{
OPCClient client;
public MainWindow()
{
InitializeComponent();
client = new OPCClient("BasicHttpBinding_IOPC");
}
private void buttonOpcRun_Click(object sender, RoutedEventArgs e)
{
try
{
GroupUpdateTime = Convert.ToInt32(textBoxUpdateGroupTime.Text);
client.connectToOPC(OPCUrl, GroupUpdateTime);
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(ex + "");
}
}
}
I want to connect to signalr with a client thats on a different pc. This means i wont be using localhost. I already made a simple networkdiscovery to get the correct ip address but it seems signalr does not allow remote clients to connect even though I already use CorsOptions.AllowAll.
class Startup
{
public void Configuration(IAppBuilder app)
{
var hubConfiguration = new HubConfiguration
{
#if DEBUG
EnableDetailedErrors = true
#else
EnableDetailedErrors = false
#endif
};
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR(hubConfiguration);
}
}
Iam using duality which is a 2d game engine. Here is the server:
public class SignalRServer : Component, ICmpInitializable
{
private IDisposable _signalRServer;
public int _port { get; set; } = 8080;
public void StopServer()
{
if (_signalRServer != null)
_signalRServer.Dispose();
}
public void OnInit(InitContext context)
{
if (context == InitContext.Activate && DualityApp.ExecContext == DualityApp.ExecutionContext.Game)
{
var networkDiscovery = new NetworkDiscovery(_port, "TestGame"); //Network discovery to get the ip adres of the server if one is found
IPEndPoint ipEndPoint;
if (networkDiscovery.LookForServer(out ipEndPoint))
{
try
{
ConnectToServer(ipEndPoint).Wait();
Debug.WriteLine($"Connection established to {ipEndPoint}");
}
catch (Exception ex)
{
Debug.WriteLine("Could not find server");
}
}
else //No server was found so we create one
{
Debug.WriteLine("Starting signalR server");
string url = $"http://*:{_port}"; //To test go to http://localhost:8080/signalr/hubs
networkDiscovery.Start();
_signalRServer = WebApp.Start<Startup>(url);
}
}
}
private async Task ConnectToServer(IPEndPoint ipEndPoint)
{
var hubConnection = new HubConnection($"http://{ipEndPoint}/");
IHubProxy hubProxy = hubConnection.CreateHubProxy(nameof(MyHub));
hubProxy.On<string, string>(nameof(MyHub.Send), (name, message) =>
{
Debug.WriteLine("Incoming data: {0} {1}", name, message);
});
ServicePointManager.DefaultConnectionLimit = 10;
await hubConnection.Start();
}
public void OnShutdown(ShutdownContext context)
{
StopServer();
}
}
And the hub:
public class MyHub : Hub
{
public void Send(string name, string message)
{
Clients.All.addMessage(name, message);
}
public override Task OnConnected()
{
Debug.WriteLine("Client connected: " + Context.ConnectionId);
Send("Server", $"Client with id {Context.ConnectionId} has connected");
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
Debug.WriteLine("Client disconnected: " + Context.ConnectionId);
return base.OnDisconnected(stopCalled);
}
}
I have created a application to send messages between client-server and I am having a
problem on server side.
I want to write the incoming NewOrderSingle message( .body extension file) files in store folder on server side.
The newordersingle message along with executionreport message get written into store file on client side. but on server side I don’t get application messages(file with .body extension) in store file.
How to write the incoming application messages to the file and not the admin messages.
My sample code is as follows:
public class clsFIXServer : QuickFix.MessageCracker, QuickFix.IApplication
{
public void FromApp(QuickFix.Message message, QuickFix.SessionID sessionID)
{
Console.WriteLine("IN: " + message);
Crack(message, sessionID);
}
public void OnCreate(QuickFix.SessionID sessionID)
{
}
public void OnLogon(QuickFix.SessionID sessionID)
{
}
public void OnLogout(QuickFix.SessionID sessionID)
{
}
public void ToAdmin(QuickFix.Message message, QuickFix.SessionID sessionID)
{
}
public void ToApp(QuickFix.Message message, QuickFix.SessionID sessionId)
{
Console.WriteLine("OUT: " + message);
}
public void OnMessage(QuickFix.FIX44.NewOrderSingle n, SessionID s)
{
Symbol symbol = n.Symbol;
Side side = n.Side;
OrdType ordType = n.OrdType;
OrderQty orderQty = n.OrderQty;
Price price = new Price(DEFAULT_MARKET_PRICE);
ClOrdID clOrdID = n.ClOrdID;
switch (ordType.getValue())
{
case OrdType.LIMIT:
price = n.Price;
if (price.Obj == 0)
throw new IncorrectTagValue(price.Tag);
break;
case OrdType.MARKET: break;
default: throw new IncorrectTagValue(ordType.Tag);
}
QuickFix.FIX44.ExecutionReport exReport = new QuickFix.FIX44.ExecutionReport(
new OrderID(GenOrderID()),
new ExecID(GenExecID()),
new ExecType(ExecType.FILL),
new OrdStatus(OrdStatus.FILLED),
symbol, //shouldn't be here?
side,
new LeavesQty(0),
new CumQty(orderQty.getValue()),
new AvgPx(price.getValue()));
exReport.Set(clOrdID);
exReport.Set(symbol);
exReport.Set(orderQty);
exReport.Set(new LastQty(orderQty.getValue()));
exReport.Set(new LastPx(price.getValue()));
if (n.IsSetAccount())
exReport.SetField(n.Account);
try
{
Session.SendToTarget(exReport, s);
}
catch (SessionNotFound ex)
{
Console.WriteLine("==session not found exception!==");
Console.WriteLine(ex.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
My client side function that creates the newordersingle message :-
public void Run()
{
objEMSOrder = ((FIXFormatter.EMSOrder)messageQueue.Receive().Body);
if (this._session.SessionID.BeginString == "FIX.4.4")
{
QuickFix.FIX44.NewOrderSingle m = objMessageCreator.NewOrderSingle44MessageCreator(objEMSOrder);
**//DLL FUNCTION THAT CREATES MESSAGE**
if (m != null)
{
m.Header.GetField(Tags.BeginString);
SendMessage(m);
}
}
}
The message store is for internal use by the FIX session protocol. It only stores outgoing messages so that if there is a sequence gap is can resend previously sent messages. You want to look at the FileLogFactory and FileLog classes. Those will log both incoming and outgoing messages.