Basically I'm making a program to simulate a petrol station system.
My problem is that I'm trying to send a request through a WCF service such as this:
User Requests Pump to be activated ----> WCF SERVICE ----> Point of Sale
User starts pumping petrol<---- WCF SERVICE <---- Point of Sale Accepts
At the moment it works, but only sometimes.
This is how I try to get a response:
while(PumpserviceClient.getRequestedAcceptedStatusFromPos().Accepted == false)
{
PumpserviceClient.RequestPump(int.Parse(PumpID));
// needs to wait for pump to be activated
if (PumpserviceClient.getRequestedAcceptedStatusFromPos().Accepted == true /*&& PumpserviceClient.getRequestedAcceptedStatusFromPos().PumpNo == int.Parse(PumpID)*/)
{
MessageBox.Show(" The Pos has accepted your pump request");
// if its accepted you call
Customer.ActivatePump();
}
And these are the methods in the service:
bool Accepted= false;
bool Requested=false;
public void AcceptPump(int PumpNumber)
{
Accepted = true;
Requested = false;
int pumpnumber = PumpNumber;
PumpRequest.Accepted = Accepted;
PumpRequest.Requested = Requested;
}
public void RequestPump(int PumpNumber)
{
int pumpnumber = PumpNumber;
Requested = true;
Accepted = false;
PumpRequest.Accepted = Accepted;
PumpRequest.PumpNo = PumpNumber;
PumpRequest.Requested = Requested;
}
public void ResetRequest(int PumpNumber)
{
int pumpnumber = PumpNumber;
Requested = false;
Accepted = false;
PumpRequest.Accepted = Accepted;
PumpRequest.PumpNo = 0;
PumpRequest.Requested = Requested;
}
public Message getRequestedStatusFromPump()
{
return PumpRequest;
}
public Message getRequestedAcceptedStatusFromPos()
{
return PumpRequest;
}
}
and the point of sale system accepts the requests by:
if (Client.getRequestedStatusFromPump().Requested == true)
{
MessageBox.Show("Pump Number: "+Client.getRequestedStatusFromPump().PumpNo + " Is waiting to be accepted");
// need to press a button or something
Client.AcceptPump(Client.getRequestedStatusFromPump().PumpNo);
}
Code here http://www.pastebucket.com/8642
I read the code posted. You use the following attribute:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
This means your code will not multi-thread. But there is no guarantee multiple sessions won't make requests and "interrupt" each other's workflow.
For example:
Client A calls request pump
Client B calls reset pump
Client A reads... client A wonders why pump was reset.
Your code is written expecting the object to be by session. I'd suggest using this context mode and seeing if you have better luck.
The other option is to add session information to your model. I can't imagine why this would be useful. It certainly won't be easy.
The only way i found around this problem, without changing service behaviors was to create a new list
public void CreatePumpList()
{
WaitingPumps = new List<WaitingPumps>();
for (int i = 0; i < PumpLimit+1 ; i++)
{
WaitingPumps.Add(new WaitingPumps());
}
}
Then just use the pump Number as the index in this list so they don't get confused with each other.
Related
Per the comments, and my own testing, I am certain that my initial TCP port is not clearing out, and I don't really know how to remedy it.
I tried to run through the prefixes like so within my refresh method:
foreach (string item in myWebListener.Prefixes) {
if (item.Contains("http://127.0.0.1:5000")) {
myWebListener.Prefixes.Remove(item);
}
}
This did not make any noticeable change in the behavior.
The only other thing that was popping up in the web logs was something about the favicon.ico file, and just maybe another browser thread is trapping my connection trying to retrieve this and was going to provide it as an async callback to finish things out, though I didn't provide it so this line of thinking makes sense to me (How to provide a favicon.ico file in my byte stream as an embedded resource is My next research project).
If this shows no changes, my next research project is trying to leverage TCPListener:
I ran across this Q/A, but it really didn't help me as I am not familiar with the TCP way of doing what I have done here, and the work-around looks similar to what I was already attempting in my original code.
There was a mention of a using statement in the linked Q/A, and my guess is that it would look something like:
using(TCPListener myTCPListener = new TCPListener()) {
//Set the necessary TCP statements here.
//Do what I already did with my existing code here.
}
That's as far as my brain can take me so far, does this sound far off based on the factors at play here?
BELOW WAS THE ORIGINAL QUESTION CONTENT:
I am successfully reaching my running C# HttpListener localhost server, and successfully sending information back out to the local requesting browser page.
But after the first time, I completely lock up the browser web page waiting for a new response.
I debug with no errors on VS, so I don't even know where to look next; Hence that's why I'm here.
My hope is, is that someone can shed some light on how to refresh my server internally, so when the new query is needed, I can respond to it just like it was the first time.
I'm using an http get method to call this, so the call would look similar to the following:
"http://127.0.0.1:5000/?param1=searchForThings¶m2=itemToSearchFor";
I have to rip out a lot of proprietary code, but here is what is happening:
public class myWebServer {
public myWebServer() {
refresh();
//The Global is being set When the API
// Loads & is referenced here
ev1 = _MyGlobalEvents.ev1
}
public static HttpListener myWebListener { get; set; }
public static HttpListenerContext context { get; set; }
public static AsyncCallback myGetCallback { get; set; }
public static HttpResponseMessage myResp { get; set; }
public static Stream output { get; set; }
public static MyAPIDefinedEventType ev1 { get; set; }
public static void refresh() {
if (myWebListener != null) {
myWebListener.Stop();
myWebListener.Close();
}
if (output != null) { output.Dispose(); }
if (myGetCallback != null) { myGetCallback = null; }
myWebListener = new HttpListener();
myGetCallback = new AsyncCallback(processRequest);
myWebListener.Prefixes.Add("http://127.0.0.1:5000/");
myWebListener.Start();
myResp = new HttpResponseMessage(HttpStatusCode.Created);
myWebListener.BeginGetContext(myGetCallback, myResp);
}
public static void processRequest(IAsyncResult result) {
context = myWebListener.EndGetContext(result);
context.Response.KeepAlive = true;
myResp = new HttpResponseMessage(HttpStatusCode.Accepted);
context.Response.StatusCode = (int)HttpStatusCode.Accepted;
output = context.Response.OutputStream;
string label = context.Request.QueryString["param1"];
string fiStr = context.Request.QueryString["param2"];
if (label != null || label != "" || label.Contains("\n") == false) {
int pass1 = 0;
int pass2 = 0;
try {
int myInt = label.ToCharArray().Length;
pass1 = 1;
} catch { }
if (pass1 > 0) {
try {
int myInt2 = fiStr.ToCharArray().Length;
pass2 = 1;
} catch { }
}
if ((pass1 == 1 && pass2 == 0) || (pass1 == 1 && pass2 == 1)) {
pass1 = 0;
pass2 = 0;
if (label == "searchForThings" && (fiStr != null && fiStr != "")) {
string respStr = "<html><body><div id=\"status_msg\" style=\"display:inline;\">" + fiStr + "</div></body></html>";
byte[] respBuffer = Encoding.ASCII.GetBytes(respStr);
context.Response.StatusCode = (int)HttpStatusCode.Accepted;
output.Write(respBuffer, 0, respBuffer.Length);
_MyGlobalEvents.searchStr = fiStr;
ev1.Raise();
}
}
}
}
//When the Custom Event is done processing it runs something like the
//following to clean up and finalize
public void _processSearch(string resultStr) { processSearch(resultStr); }
public static void processSearch(string resultStr) {
string respStr = "<html><head>" +
"<style type=\"text/css\">" +
"tr.dispRow{ display:table-row; }\n" +
"tr.noRow{ display:none; }" +
"</style>" +
"</head>" +
"<body>" +
resultStr +
"</body></html>";
byte[] respBuffer = Encoding.ASCII.GetBytes(respStr);
output.Flush();
context.Response.StatusCode = (int)HttpStatusCode.OK;
output.Write(respBuffer, 0, respBuffer.Length);
output.Close();
context.Response.KeepAlive = false;
context.Response.Close();
refresh();
}
}
public static class _MyGlobalEvents {
public static string searchStr { get; set; }
public static MyAPIDefinedEventType ev1 { get; set; }
}
"But after the first time, I completely lock up the browser web page waiting for a new response" - are you saying the error is seen on the browser sending an Http request but getting no response ?
I'd advise downloading and using Telerik Fiddler (free) to see whats actually being sent and received between the client and server.
As other respondees have said - this is not a normal pattern for HttpListener and I'd guess you are having problems with TCP port caching on the server, and your .Stop() and .Close() methods are not freeing the TCP port (Windows caches it)
My particular problem was actually the favicon.ico file request from the browser locking up the connection.
Early on I was noticing that my server was being hit twice, and that is why I had my flags in place to make certain that I was only operating on what was the actual Get request based on the URL I was expecting...
Otherwise the URL was erroring as a blank string (turns out this was the favicon.ico request being made - irritating, because when the string is completely empty you have no idea how to troubleshoot it). So I was ignoring it and not touching what I didn't understand.
Well it turns I could NOT ignore this request, because the browser is expecting a response for it. All I had to do was put an else section in my code that executed if my flag options weren't met, and close, dispose and refresh the connection when this happened.
This Q/A really described the solidified solution to the problem fairly well, but my Exception.Message was blank which didn't really get addressed there either, so I'm glad I get to document it here in the hopes that anyone else who may ever run into the same problem finds the answer a little more strait forward.
As for the answer that PhillipH provided, this proved to be the most helpful to me, as it revealed to me that I needed to be checking what was happening from the network traffic eventing.
However I could not really use the tools that this user suggested, but found an alternative with Google Chrome's built in chrome://net-internals/. Just type that into your address bar in chrome and execute it.
Anyway way this was the total answer for me, but PhillipH you deserve it for moving me in the right direction.
I am working on an ASP.NET Webform project (legacy code).On my button_click event i am sending sms message to all the datas populated in this.
var customerSMS = BusinessLayer.SMS.SmsSetup.GetAllCustomerSMS(OfficeId);
This takes around 15seconds to do all the computing and get the data(1000rows)
from the Db.And for each data it runs through the loop and does validation and
sends the sms and it does take time.I want to do this task in background and
redirect the user to the index page and the background process continues till it
gets out of the loop.I am new to this and still learning this beautiful
language C#.I did go through this amazing Asynchronous Programming async/await
and Multithreading approach and got hold of it only in simple WindowsForm
applications.Any reference/code snippet/best approach with a simple explanation for my case would be helpful.
My button click event code :
protected void ReturntoDashboard_Click(object sender, EventArgs e)
{
sms = Everest.Net.BusinessLayer.SMS.SmsSetup.GetSmsSetUp(OfficeId);
if (sms.EnableSmsData && sms.SmsCount > 0)
{
#region Loan Section
var smsLoan = Everest.Net.BusinessLayer.SMS.SmsSetup.GetLoanId(s.Sms_AccountNumber);
var loanId =
BusinessLayer.SMS.SmsSetup.GetLoanIdValue(s.Sms_AccountNumber);
var dateexceeded =
BusinessLayer.SMS.SmsSetup.IsDateExceeded(loanId);
if (smsLoan != null && dateexceeded == true)
{
foreach (Common.SMS.SMSSetup sm in smsLoan)
{
var smsClosingBalanceLoan = BusinessLayer.SMS.SmsSetup.GetAmountForLoanAlert( sm.LoanId,
BusinessLayer.Core.DateConversion
.GetCurrentServerDate()
.AddDays(sms.DaysbeforeLoanalerts).ToString());
if (smsClosingBalanceLoan != null)
{
if (smsClosingBalanceLoan.LoanAmountToPay > 0)
{
int smsSentAlertCount = sms.LoanAlertCount;
var logCount = BusinessLayer.SMS.SmsSetup.GetLoanSmsAlertSentCount(DateTime.Now.AddDays(-smsSentAlertCount).ToString("yyyy-MM-dd"), DateTime.Now.ToString("yyyy-MM-dd"), sm.LoanAccountNumber);
if (logCount < smsSentAlertCount)
{
smsLog = new Everest.Net.Common.SMS.SMSSetup();
finalMessage = "Dear Member, Your Loan accnt " + sm.LoanAccountNumber + " with Principal"+ "+" + "Int Amnt: Rs." + smsClosingBalanceLoan.LoanAmountToPay + " need to be payed.Thank You," + officeName.OfficeName;
smsLog.LogServiceType = "Loan";
smsLog.LogSmsType = s.Sms_SmsType;
smsLog.LogSmsMessage = finalMessage;
smsLog.LogCustomerId = s.CustomerId.ToString();
smsLog.LogAccountNumber = s.Sms_AccountNumber;
smsLog.LogAccountType = s.Sms_AccountType;
smsLog.LogSmsSentDate = BusinessLayer.Core.DateConversion.GetCurrentServerDate();
smsLog.LogSmsFailedDate = "";
smsLog.LogSentStatus = true;
smsLog.LogUserId = UserId;
smsLog.LogSmsFailedMessage = "";
try
{
var result = Everest.Net.BusinessLayer.SMS.smsParameters.SendSMS(sms.FromNum, sms.Token, sms.Url, cellNum, finalMessage);
}
catch (Exception ex)
{
smsLog.LogSmsFailedDate = System.DateTime.Now.ToString("MM/dd/yyyy HHmmss");
smsLog.LogSentStatus = false;
smsLog.LogSmsFailedMessage = ex.Message;
Everest.Net.BusinessLayer.SMS.SmsSetup.InsertSMSLog(smsLog);
}
sms = Everest.Net.BusinessLayer.SMS.SmsSetup.GetSmsSetUp(OfficeId);
sms.SmsCount = sms.SmsCount - 1;
Everest.Net.BusinessLayer.SMS.SmsSetup.UpdateSmsSetup(sms);
Everest.Net.BusinessLayer.SMS.SmsSetup.InsertSMSLog(smsLog);
}
}
}
}
}
}
}
}
catch (Exception ex)
The ideal solution would remove the responsibility of sending the SMS from the web application itself. Instead, the web application should create a database record containing the message and recipient addresses, and a separate background job (e.g. a Windows Service) should poll the database and send SMS messages when neeeded. This is the best solution in terms of fault tolerance and auditability, because there is a permanent record of the messaging job which can be resumed if the system fails.
That being said, maybe you don't want to go to all that trouble. If you feel strongly that you wish to send the SMS directly from the ASP.NET application, you will need to create a Task and queue it to run using QueueBackgroundWorkitem. You will need to refactor your code a bit.
Move all the logic for sending the SMS into a separate function that accepts all the information needed as parameters. For example,
static void SendSMS(string[] addresses, string messagetext)
{
//Put your SMS code here
}
When you need to call the function, queue it as a background item
HostingEnvironment.QueueBackgroundWorkItem(a => SendSMS(addresses, messageText));
If your worker task needs to access its own cancellation token (e.g. if it is supposed to loop until cancelled), it is passed as an argument to the lambda expression. So you could modify the prototype
static void SendSMS(string[] addresses, string messagetext, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
//Put your code here
}
}
and pass it thus:
HostingEnvironment.QueueBackgroundWorkItem(token => SendSMS(addresses, messageText, token));
Placing the task in the background queue ensures that ASP.NET keeps track of the thread, doesn't try to garbage collect it, and shuts it down properly when the application pool needs to shut down.
After queuing the background operation, your page can render is content per usual and conclude the HTTP response while the task continues to execute.
I am a newbie when it comes to MVVM and Web related technologies. We are currently developing an MVVM client app using the latest MVVMLight framework in Visual Studios 2013. The application uses Microsoft's Webapi (latest version) to get data into our model via HTTP requests. Our code executes up to the point where the HTTP request is made, and we have confirmed that the server is getting the request, gathering up the requested data and returning it as JSON. However the client never sees the response, it just continues to wait for the response. Its almost as though we are seeing an issue that seems to do with threads. Where the request was made on one thread, but the response is being received on another. Here is our code (where the HTTP request is made):
public class DataService : IDataService
{
#region Fields
private HttpClient _client;
private LfActivityDataReturnObject _activityReturnObj;
private AdroServices _adroServices;
private bool _selectedProcssingOptionPosted = false;
private bool _activityDataSuccessfullyRetrieved = false;
private decimal _incomingLFEntryId;
#endregion
#region Constructor
public DataService()
{
try
{
//Get command line arguments
string[] arguments = Environment.GetCommandLineArgs();
for (int i = 1; i < arguments.Length; i++)
{
switch (i)
{
case 1:
{
if (!Decimal.TryParse(arguments[i], out _incomingLFEntryId))
{
_incomingLFEntryId = -1;
}
break;
}
}
}
if (_incomingLFEntryId <= 0)
{
throw new ArgumentException(String.Format("Invalid Activity Shortcut Entry ID: {0}", _incomingLFEntryId));
}
}
catch (Exception e)
{
throw e;
}
}
#endregion
#region Methods
public void GetGeneralInformationModel(Action<GeneralInformationModel, Exception> callback)
{
Exception locException = null;
GeneralInformationModel locGeneralInformationModel = null;
if (_adroServices == null)
{
try
{
//Start the HTTP request
GetActivityDataAsync().Wait();
}
catch (Exception e)
{
locException = e;
}
// change? should be for http success but adro failure
if (_activityDataSuccessfullyRetrieved)
{
_adroServices = new AdroServices(_activityReturnObj);
locGeneralInformationModel = new GeneralInformationModel(_adroServices);
}
else
{
Exception e2 = new Exception("Error retrieving activity data in DataService");
locException = e2;
}
}
else
{
locGeneralInformationModel = new GeneralInformationModel(_adroServices);
}
var item = locGeneralInformationModel;
callback(item, locException);
}
//Get data from the repository via the service layer.
private async Task GetActivityDataAsync()
{
try
{
using (this._client = new HttpClient())
{
_client.BaseAddress = new Uri("http://localhost:52512//");
_client.DefaultRequestHeaders.Accept.Clear();
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//Line below is where the app just runs getting no response
HttpResponseMessage caseDataResponse = await _client.GetAsync(string.Format("api/LfUrs/{0}", _incomingLFEntryId));
if (caseDataResponse.IsSuccessStatusCode)
{
_activityReturnObj = await caseDataResponse.Content.ReadAsAsync<LfActivityDataReturnObject>();
if (_activityReturnObj.ReturnCode == 0)
{
_activityDataSuccessfullyRetrieved = true;
}
else
{
_activityDataSuccessfullyRetrieved = false;
}
}
else
{
_activityDataSuccessfullyRetrieved = false;
}
}
}
catch (Exception ex)
{
_activityDataSuccessfullyRetrieved = false;
throw ex;
}
}
We have tried using Fiddler to get more information but Fiddler doesn't seem to be able to reveal any of the details. Also I should mention that we are simulating the server on the local host and as I stated above, we have confirmed the server gets the request and returns the data. My associates and I are starting to think this has something to do with the MVVM Light Framework and the IOC or possibly threading. When we use this same code in a MVVM solution that doesn't use the framework it works. Any help would be sincerely appreciated. Thanks...Mike
I have figured it out. Somehow, in my app.xaml, the Dispatcher.Helper had been moved to the event On_Startup rather than being contained in the class constructor. Additionally I had to move the HTTP stuff into the ADRO services class and make sure it was constructed in the App constructor in app.xaml right after Dispatcher.Helper. But thanks to everyone who participates here on StackOverFlow. This resource is an absolute life-saver. Thanks
When I print out received messages on the Console the displayed messages are all messed up, each message containing 5 string sub-messages that are printed on the Console before control reverts back to the incoming message callback. I strongly assume this is because the incoming message event is raised async in Booksleeve?
I refer to the following post, How does PubSub work in BookSleeve/ Redis?, where the author, Marc Gravell, pointed to the ability to force sync reception by setting Completion Mode to "PreserveOrder". I have done that, tried before and after connecting the client. Neither seems to work.
Any ideas how I can receive messages and print them on the console in the exact order they were sent? I only have one single publisher in this case.
Thanks
Edit:
Below some code snippets to show how I send messages and the Booksleeve wrapper I quickly wrote.
Here the client (I have a similar Client2 that receives the messages and checks order, but I omitted it as it seems trivial).
class Client1
{
const string ClientId = "Client1";
private static Messaging Client { get; set; }
private static void Main(string[] args)
{
var settings = new MessagingSettings("127.0.0.1", 6379, -1, 60, 5000, 1000);
Client = new Messaging(ClientId, settings, ReceiveMessage);
Client.Connect();
Console.WriteLine("Press key to start sending messages...");
Console.ReadLine();
for (int index = 1; index <= 100; index++)
{
//I turned this off because I want to preserve
//the order even if messages are sent in rapit succession
//Thread.Sleep(5);
var msg = new MessageEnvelope("Client1", "Client2", index.ToString());
Client.SendOneWayMessage(msg);
}
Console.WriteLine("Press key to exit....");
Console.ReadLine();
Client.Disconnect();
}
private static void ReceiveMessage(MessageEnvelope msg)
{
Console.WriteLine("Message Received");
}
}
Here the relevant code snippets of the library:
public void Connect()
{
RequestForReplyMessageIds = new ConcurrentBag<string>();
Connection = new RedisConnection(Settings.HostName, Settings.Port, Settings.IoTimeOut);
Connection.Closed += OnConnectionClosed;
Connection.CompletionMode = ResultCompletionMode.PreserveOrder;
Connection.SetKeepAlive(Settings.PingAliveSeconds);
try
{
if (Connection.Open().Wait(Settings.RequestTimeOutMilliseconds))
{
//Subscribe to own ClientId Channel ID
SubscribeToChannel(ClientId);
}
else
{
throw new Exception("Could not connect Redis client to server");
}
}
catch
{
throw new Exception("Could not connect Redis Client to Server");
}
}
public void SendOneWayMessage(MessageEnvelope message)
{
SendMessage(message);
}
private void SendMessage(MessageEnvelope msg)
{
//Connection.Publish(msg.To, msg.GetByteArray());
Connection.Publish(msg.To, msg.GetByteArray()).Wait();
}
private void IncomingChannelSubscriptionMessage(string channel, byte[] body)
{
var msg = MessageEnvelope.GetMessageEnvelope(body);
//forward received message
ReceivedMessageCallback(msg);
//release requestMessage if returned msgId matches
string msgId = msg.MessageId;
if (RequestForReplyMessageIds.Contains(msgId))
{
RequestForReplyMessageIds.TryTake(out msgId);
}
}
public void SubscribeToChannel(string channelName)
{
if (!ChannelSubscriptions.Contains(channelName))
{
var subscriberChannel = Connection.GetOpenSubscriberChannel();
subscriberChannel.Subscribe(channelName, IncomingChannelSubscriptionMessage).Wait();
ChannelSubscriptions.Add(channelName);
}
}
Without seeing exactly how you are checking for this, it is hard to comment, but what I can say is that any threading oddity is going to be hard to track down and fix, and is therefore very unlikely to be addressed in BookSleeve, given that it has been succeeded. However! It will absolutely be checked in StackExchange.Redis. Here's the a rig I've put together in SE.Redis (and, embarrassingly, it did highlight a slight bug, fixed in next release, so .222 or later); output first:
Subscribing...
Sending (preserved order)...
Allowing time for delivery etc...
Checking...
Received: 500 in 2993ms
Out of order: 0
Sending (any order)...
Allowing time for delivery etc...
Checking...
Received: 500 in 341ms
Out of order: 306
(keep in mind that 500 x 5ms is 2500, so we should not be amazed by the 2993ms number, or the 341ms - this is mainly the cost of the Thread.Sleep we have added to nudge the thread-pool into overlapping them; if we remove that, both loops take 0ms, which is awesome - but we can't see the overlapping issue so convincingly)
As you can see, the first run has the correct order output; the second run has mixed order, but it ten times faster. And that is when doing trivial work; for real work it would be even more noticeable. As always, it is a trade-off.
Here's the test rig:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using StackExchange.Redis;
static class Program
{
static void Main()
{
using (var conn = ConnectionMultiplexer.Connect("localhost"))
{
var sub = conn.GetSubscriber();
var received = new List<int>();
Console.WriteLine("Subscribing...");
const int COUNT = 500;
sub.Subscribe("foo", (channel, message) =>
{
lock (received)
{
received.Add((int)message);
if (received.Count == COUNT)
Monitor.PulseAll(received); // wake the test rig
}
Thread.Sleep(5); // you kinda need to be slow, otherwise
// the pool will end up doing everything on one thread
});
SendAndCheck(conn, received, COUNT, true);
SendAndCheck(conn, received, COUNT, false);
}
Console.WriteLine("Press any key");
Console.ReadLine();
}
static void SendAndCheck(ConnectionMultiplexer conn, List<int> received, int quantity, bool preserveAsyncOrder)
{
conn.PreserveAsyncOrder = preserveAsyncOrder;
var sub = conn.GetSubscriber();
Console.WriteLine();
Console.WriteLine("Sending ({0})...", (preserveAsyncOrder ? "preserved order" : "any order"));
lock (received)
{
received.Clear();
// we'll also use received as a wait-detection mechanism; sneaky
// note: this does not do any cheating;
// it all goes to the server and back
for (int i = 0; i < quantity; i++)
{
sub.Publish("foo", i);
}
Console.WriteLine("Allowing time for delivery etc...");
var watch = Stopwatch.StartNew();
if (!Monitor.Wait(received, 10000))
{
Console.WriteLine("Timed out; expect less data");
}
watch.Stop();
Console.WriteLine("Checking...");
lock (received)
{
Console.WriteLine("Received: {0} in {1}ms", received.Count, watch.ElapsedMilliseconds);
int wrongOrder = 0;
for (int i = 0; i < Math.Min(quantity, received.Count); i++)
{
if (received[i] != i) wrongOrder++;
}
Console.WriteLine("Out of order: " + wrongOrder);
}
}
}
}
This is my first time playing around with SignalR. I am trying to build a notification system where the server checks at regular intervals to see if there is something (query database) to broadcast and if there is then it broadcasts it to all the clients.
I came across this post on Stackoverflow and was wondering if modifying the code to make a DB call at a particular interval was indeed the right way to do it. If not is there a better way to do it?
I did see a lot of Notification related questions posted here but none with any code in it. Hence this post.
This is the exact code that I am using:
public class NotificationHub : Hub
{
public void Start()
{
Thread thread = new Thread(Notify);
thread.Start();
}
public void Notify()
{
List<CDCNotification> notifications = new List<CDCNotification>();
while (true)
{
notifications.Clear();
notifications.Add(new CDCNotification()
{
Server = "Server A", Application = "Some App",
Message = "This is a long ass message and amesaadfasd asdf message",
ImgURL = "../Content/Images/accept-icon.png"
});
Clients.shownotification(notifications);
Thread.Sleep(20000);
}
}
}
I am already seeing some weird behaviour where the notifications come more often than they are supposed to. Even though I am supposed to get it every 20s I get it around 4-5 secs and I get multiple messages.
Here is my client:
var notifier = $.connection.notificationHub;
notifier.shownotification = function (data) {
$.each(data, function (i, sample) {
var output = Mustache.render("<img class='pull-left' src='{{ImgURL}}'/> <div><strong>{{Application}}</strong></div><em>{{Server}}</em> <p>{{Message}}</p>", sample);
$.sticky(output);
});
};
$.connection.hub.start(function () { notifier.start(); });
Couple of notes:
As soon as a second client connects to your server there will be 2 threads sending the notifications, therefore if you ave more than one client you will have intervals smaller than 20s
Handling thread manually within ASP.NET is considered bad practice, you should avoid this if possible
In general this smells a lot like polling which is kind of the thing SignalR lets you get rid of since you don't need to signal the server/client
In order to solve this you need todo something like this (again, threads in a web application are generally not a good idea):
public class NotificationHub : Hub
{
public static bool initialized = false;
public static object initLock = new object();
public void Start()
{
if(initialized)
return;
lock(initLock)
{
if(initialized)
return;
Thread thread = new Thread(Notify);
thread.Start();
initialized = true;
}
}
public void Notify()
{
List<CDCNotification> notifications = new List<CDCNotification>();
while (true)
{
notifications.Clear();
notifications.Add(new CDCNotification() { Server = "Server A", Application = "Some App", Message = "This is a long ass message and amesaadfasd asdf message", ImgURL = "../Content/Images/accept-icon.png" });
Clients.shownotification(notifications);
Thread.Sleep(20000);
}
}
}
The static initialized flag prevents multiple threads from being created. The locking around it is to ensure that the flag is only set once.
I am working on the same task over here. Instead of continuously checking the database, I created my own events and listener, where an event is RAISED when a NOTIFICATION IS ADDED :) What do you think about that?