WCF Web Socket Service - lock not working correctly - c#

In my web socket wcf service I'm using timer elapsed event to do some logic on my object and after that send information to client (by callback object). I also track callback closed event to clean all object that I'm using in timer elapsed event handler. The problem that i occured is that, when I'm trying to dispose my object i get errors that is still working and to prevent that i try to use lock in both code (timer elapsed event and closed channel event) but it not working correctly (i'm still getting errors that i'm calling method on my object that is no allowed for this moment - this mean that timer elapsed and also call it in the same time).
Is WCF do something special which causes to lock don't work as I expected ?
Here is some of my code:
[ServiceContract(CallbackContract = typeof(IWebSocketsCallback))]
public interface IWebSockets
{
[OperationContract(IsOneWay = true, Action = "*")]
void Start(Message msg);
}
[ServiceContract]
public interface IWebSocketsCallback
{
[OperationContract(IsOneWay = true, Action = "*")]
void SendToClient(Message msg);
}
public class WebSockets : IWebSockets
{
private IWebSocketsCallback callback;
private bool handlePIChanges = false;
private PIDataPipe pipe;
private PIEventsProducer piEventsProducer;
private Timer timer;
private readonly object timerLock = new object();
private readonly object pipeLock = new object();
private bool isPipeClosed = false;
public WebSockets()
{
callback = OperationContext.Current.GetCallbackChannel<IWebSocketsCallback>();
((IChannel)callback).Closed += WebSockets_Closed;
}
public void Start(Message msg)
{
// some custom logic that i ommited ...
timer = CreateTimer();
timer.Elapsed += (sender, e) => PIQueryingCallback(pipe, timer);
}
private void WebSockets_Closed(object sender, EventArgs e)
{
lock (timerLock)
{
handlePIChanges = false;
if (timer != null)
{
timer.Stop();
timer.Dispose();
piEventsProducer.Clear();
}
}
lock (pipeLock)
{
if (pipe != null)
{
pipe.RemoveSignups(pipe.AsReadOnly()); // this cause error, because GetObserverEvents not stopped working
pipe.Close();
isPipeClosed = true;
}
}
}
private void PIQueryingCallback(PIDataPipe pipe, Timer myTimer)
{
bool moreIndicator;
AFErrors<PIPoint> errors;
lock (pipeLock)
{
do
{
if (handlePIChanges && !isPipeClosed)
{
try
{
errors = pipe.GetObserverEvents(2000, out moreIndicator); // this method calls make block for other call on this object untill it return results
}
catch (Exception e)
{
moreIndicator = false;
continue;
}
}
else
{
moreIndicator = false;
}
}
while (moreIndicator);
}
if (handlePIChanges)
{
lock (timerLock)
{
if (handlePIChanges)
{
myTimer.Start();
}
}
}
}
// this method is called after GetObserveEventsCompleted
private void HandlePIDataEventProducerChanges(string msg)
{
if (handlePIChanges && !isPipeClosed)
{
if (((IChannel)callback).State == CommunicationState.Opened)
{
try
{
callback?.SendPIDataChangesToClient(CreateMessage(msg));
}
catch (Exception ex)
{
}
}
}
}
}

Related

error on wcf application when used as resource to move received data to asp.net web API

so bascially i have a console application acting as a client software receiving data from a WCF feed. this works just fine when i want to do a console.writeline and print the data i'm getting in the console. I don't control the service end of the WCF feed so i have to use this console client software to access the data.
I'm trying to build an ASP.net web api application to pass this data to my own client via REST api. There are other complex reasons i have to do it this way but when it comes down to it i have to. I've built the basic web api for doing a GET to get the information. added the console application as a resource to the web api application but get an error message when I make the GET call.
This is the URL i'm using to make the GET call.
http://localhost:60421/api/CallData?skillNumber=92
After i make this call i get the below error message
[ERROR] System.InvalidOperationException occurred
HResult=0x80131509
Message=Could not find default endpoint element that references contract 'FeedService.IFeedService' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.
Source=
StackTrace:
The error occurs in this cs file. I've commented at the line it occurs in.
using ScoreBoardClientTest.FeedService;
namespace ScoreBoardClientTest
{
public class FeedServiceAgent : IFeedServiceAgent, IFeedServiceCallback
{
private static FeedServiceClient _feedServiceClient;
private ConnectionStats.ConnectionStatus _connectionStatus;
private bool _retrying;
private bool _disposed;
public FeedServiceAgent() //(IEventAggregator eventAggregator)
{
//Guard.ArgumentNotNull(eventAggregator, "eventAggregator");
//_eventAggregator = eventAggregator;
InitializeServiceClient();
}
public event MessageReceivedEventHandler MessageReceived;
public void InitializeServiceClient()
{
// The error message occurs at the line right below this
_feedServiceClient = new FeedServiceClient(new InstanceContext(this));
_feedServiceClient.InnerChannel.Opening += OnOpening;
_feedServiceClient.InnerChannel.Opened += OnOpened;
_feedServiceClient.InnerChannel.Closing += OnClosing;
_feedServiceClient.InnerChannel.Closed += OnClosed;
_feedServiceClient.InnerChannel.Faulted += OnFaulted;
}
private void DisposeServiceClient()
{
if (_feedServiceClient == null)
return;
try
{
_feedServiceClient.InnerChannel.Opening -= OnOpening;
_feedServiceClient.InnerChannel.Opened -= OnOpened;
_feedServiceClient.InnerChannel.Closing -= OnClosing;
_feedServiceClient.InnerChannel.Closed -= OnClosed;
_feedServiceClient.InnerChannel.Faulted -= OnFaulted;
_feedServiceClient.Abort();
}
catch
{
//Don't care.
}
finally
{
_feedServiceClient = null;
}
}
public ConnectionStats.ConnectionStatus ConnectionStatus
{
get { return _connectionStatus; }
set
{
_connectionStatus = value;
PublishConnectionStatus();
}
}
private void PublishConnectionStatus()
{
}
private void ProcessCmsData(A cmsData)
{
if (cmsData == null)
return;
var skill = new Skill();
var agents = new List<Agent>();
var cmLookupdata = new List<CmLookupData>();
// LookupData lookupdata = new LookupData();
if (cmsData.C != null && cmsData.C.Length > 0)
LookupDataTranslator.Translate(cmsData.C, cmLookupdata);
if (cmsData.AMember != null)
SkillTranslator.Translate(cmsData.AMember, skill, cmLookupdata);
if (cmsData.B != null && cmsData.B.Length > 0)
AgentTranslator.Translate(cmsData.B, agents, cmsData.AMember.A, cmsData.AMember.CmId); // passing skill params to validate the cmid to discard bad data
var mappedCmsData = new CmsData(skill, agents, cmLookupdata);
if (MessageReceived != null)
MessageReceived(this, new MessageReceivedEventArgs(mappedCmsData, null));
}
#region FeedServiceClient Channel Events
private void OnOpening(object sender, EventArgs eventArgs)
{
_connectionStatus = ConnectionStats.ConnectionStatus.Connecting;
}
private void OnOpened(object sender, EventArgs eventArgs)
{
_connectionStatus = ConnectionStats.ConnectionStatus.Connected;
}
private void OnClosing(object sender, EventArgs eventArgs)
{
_connectionStatus = ConnectionStats.ConnectionStatus.Disconnecting;
}
private void OnClosed(object sender, EventArgs eventArgs)
{
_connectionStatus = ConnectionStats.ConnectionStatus.Disconnected;
}
private void OnFaulted(object sender, EventArgs eventArgs)
{
_connectionStatus = ConnectionStats.ConnectionStatus.Disconnected;
if (!_retrying)
ThreadPool.QueueUserWorkItem(delegate
{
Reconnect();
});
}
#endregion
private void Reconnect()
{
_retrying = true;
_connectionStatus = ConnectionStats.ConnectionStatus.Reconnecting;
// We don't want each client attempting the first reconnect all at the same time.
var random = new Random();
int randomWaitValue = random.Next(1, 10000);
Thread.Sleep(randomWaitValue);
// Try reconnecting 10 times.
for (int i = 1; i <= 10; i++)
{
try
{
DisposeServiceClient();
_feedServiceClient = new FeedServiceClient(new InstanceContext(this));
_feedServiceClient.Open();
_feedServiceClient.InnerChannel.Opening += OnOpening;
_feedServiceClient.InnerChannel.Opened += OnOpened;
_feedServiceClient.InnerChannel.Closing += OnClosing;
_feedServiceClient.InnerChannel.Closed += OnClosed;
_feedServiceClient.InnerChannel.Faulted += OnFaulted;
_connectionStatus = ConnectionStats.ConnectionStatus.Reconnected;
_retrying = false;
//_logger.Info(String.Format("The Scoreboard Client was able to reconnect after {0} retries.", i)) ;
return;
}
catch (Exception exception)
{
//_logger.Error(String.Format("The Scoreboard Client is unable to reconnect: {0}", exception)) ;
// Wait 30 seconds between retries to reduce network congestion.
Thread.Sleep(30000);
}
}
//_logger.Info("The Scoreboard Client was unable to reconnect after 10 retries.") ;
_connectionStatus = ConnectionStats.ConnectionStatus.UnableToConnect;
}
#region Implementation of IDisposable
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
if (disposing)
{
// Dispose managed resources here.
DisposeServiceClient();
}
_disposed = true;
}
#endregion
public void NotifyCmsData(A cmsData)
{
ThreadPool.QueueUserWorkItem(delegate
{
ProcessCmsData(cmsData);
});
}
public void Subscribe(string eventTopic, int cmId)
{
if (String.IsNullOrEmpty(eventTopic))
return;
InvokeAction(x => x.Subscribe(eventTopic, cmId));
}
public void Unsubscribe(string eventTopic, int cmId)
{
if (String.IsNullOrEmpty(eventTopic))
return;
InvokeAction(x => x.Unsubscribe(eventTopic, cmId));
}
public void RefreshCmsData()
{
throw new NotImplementedException();
}
public LookupData GetLookupData()
{
LookupData lookupdata = new LookupData();
try
{
LookupDataTranslator.Translate(_feedServiceClient.GetLookupData(), lookupdata);
}
catch (Exception e)
{
//if (_logger != null)
// _logger.Error("Exception in finding the lookup data. Exception:" + e.Message);
throw e;
}
return lookupdata;
}
private void InvokeAction(Action<IFeedService> action)
{
try
{
action(_feedServiceClient);
}
catch (Exception exception)
{
//_logger.Error(" Exception in FeedService InvokeAction:" + exception.ToString());
_connectionStatus = ConnectionStats.ConnectionStatus.UnableToConnect;
}
}
}
}
The console application has a properly configured config file for the console application which is what the error message suggests does not exist. also, as i said above the console application functions fully by itself.
QUESTION
How do you use a WCF console application as a resource for a web api successfully?
I had to include the FeedService web URL inside of the web api service as long as the console application

Launch Changed event before anything changes

My app uses internet, so I would like to check, if there is internet connection, and if not - show error.
I have implemented class as shown in this site:
public class InternetConnectionChangedEventArgs : EventArgs
{
public InternetConnectionChangedEventArgs(bool isConnected)
{
this.isConnected = isConnected;
}
private bool isConnected;
public bool IsConnected
{
get { return isConnected; }
}
}
public static class Network
{
public static event EventHandler<InternetConnectionChangedEventArgs>
InternetConnectionChanged;
static Network()
{
NetworkInformation.NetworkStatusChanged += (s) =>
{
if (InternetConnectionChanged != null)
{
var arg = new InternetConnectionChangedEventArgs(IsConnected);
InternetConnectionChanged(null, arg);
}
};
}
public static bool IsConnected
{
get
{
var profile = NetworkInformation.GetInternetConnectionProfile();
var isConnected = (profile != null
&& profile.GetNetworkConnectivityLevel() ==
NetworkConnectivityLevel.InternetAccess);
return isConnected;
}
}
}
But using this approach I have to duplicate my code:
if(Network.IsConnected)
{
//do stuff with internet
}
else
//show error message
Network.InternetConnectionChanged += async (s, args) =>
{
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
if (args.IsConnected)
{
//do the same stuff with internet
}
else
{
//show the same error message
}
});
};
Because InternetConnectionChanged event launches only if internet connection changes, but I also need to now, if there is internet connection at the beginning. Is there a way to that without duplicating code and not writing each code as separate method?
Just encapsulate your logic in its own method, something like this:
private void DoStuffDependingOnConnection(bool isConnected)
{
if (isConnected)
{
//...
}
else /* ... */
}
Then when your program launches execute
DoStuffDependingOnConnection(Network.IsConnected);
And your event handler will look like this:
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
async () => DoStuffDependingOnConnection(args.IsConnected));

C# Lock Threading

I am currently trying to do some reading for locking for threads in C#
.
If I have a class similar to below
public class Connection
{
bool Connected;
internal bool startConnection(Device dev)
{
// start connection
Connected = true;
}
internal bool endConnection(Device dev)
{
// End connection
Connected = false;
}
private void readFromConnected(Device dev)
{
if(Connected)
{
// read values from connected device
}
}
}
The problem I have right now is that i have multiple threads using this class to read values from different devices
A problem arises when a thread tries to read values when it is actually disconnected, but attempts to read the values anyways because another thread has kept the Connected bool to true;
The thread that is calling this class looks like this.
Device deviceReceived;
public PollingInstance(Device deviceSent)
{
deviceReceived = deviceSent;
aTimer = new System.Timers.Timer(2500); //1000 = 1 sec
aTimer.Elapsed += OnTimedEvent;
aTimer.Enabled = true;
}
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
for (int i = 0; i < 10; i++)
{
Connection.startConnection(deviceReceived);
Connection.readFromConnected(deviceReceived);
Connection.endConnection(deviceReceived);
}
}
The part of the main class calling Polling Instance looks similar to this
foreach(Device dev in listOfDev)
{
Task.Factory.StartNew(() => pollThread(dev));
}
private void pollThread(Device dev)
{
PollingInstance testingPoll = new PollingInstance(dev);
}
Simple. Why is connected a bool?
Try this.
public class Connection
{
private int _connections = 0;
internal bool startConnection(Device dev)
{
// start connection
if(Interlocked.Increment(ref _connections) == 1)
{
//do work to connect.
}
}
internal bool endConnection(Device dev)
{
// End connection
if(Interlocked.Decrement(ref _connections) == 0)
{
//do work to disconnect.
}
}
private void readFromConnected(Device dev)
{
if(_connections > 0)
{
// read values from connected device
}
}
}
This will "works" for some values of work. But is prone to connections being left open due to exceptions and sloppy/forgetful programming. Therefore I would advise the following.
Device device = ...
using(var connection = device.CreateConnection())
{
var results = connection.Read();
}
public abstract class Connection : IDisposable
{
public object Read();
}
public class Device
{
private class DeviceConnection : Connection
{
private Device Parent { get; set; }
void Dispose()
{
Parent.StopConnection();
}
public object Read()
{
return Device.readFromConnected();
}
}
private int _connections = 0;
public Connection CreateConnection()
{
// start connection
if(Interlocked.Increment(ref _connections) == 1)
{
//do work to connect.
}
return new DeviceConnection { Parent = this };
}
private bool StopConnection()
{
// End connection
if(Interlocked.Decrement(ref _connections) == 0)
{
//do work to disconnect.
}
}
private object readFromConnected()
{
//Device is guaranteed to be connected now!
}
}
It's hard to say exactly what will happen, because the code you posted won't even compile.
you wan't something like this:
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
Connection connection = whereverThisComesFrom();
if(!Monitor.TryEnter(connection)) return; // another timer is in progress
try
{
for (int i = 0; i < 10; i++)
{
connection.startConnection(deviceReceived);
connection.readFromConnected(deviceReceived);
connection.endConnection(deviceReceived);
}
}
finally
{
Monitor.Exit(connection);
}
}
You said:
A problem arises when a thread tries to read values when it is
actually disconnected, but attempts to read the values anyways because
another thread has kept the Connected bool to true;
Can you use a try/finally to ensure you set the boolean properly?
The lock keyword is equivalent to a Monitor try/finally.
object syncObject = new object();
Monitor.Enter(syncObject);
try {
// Code updating shared data
}
finally {
Monitor.Exit(syncObject);
}
object syncObject = new object();
lock (syncObject) {
// Code updating shared data
}

Event handler not always called

I have custom thread which parses WiFi networks and updates the UI (DataGridView and graphs). Here is the thread method:
private void RefreshThread()
{
var watch = Stopwatch.StartNew();
while (true)
{
UpdateAllNetworks();
UpdateAllInterferences();
UpdateAllColors();
switch (ActivePage)
{
case Page.Start:
break;
case Page.Networks:
this.Invoke((MethodInvoker)delegate
{
UpdateDataGridWithNetworks();
ClearGraphs();
Draw24GHzGraph();
DrawSignalsOverTimeGraph();
});
break;
case Page.Channels:
break;
case Page.Analyze:
break;
default:
break;
}
watch.Stop();
int elapsedMs = (int) watch.ElapsedMilliseconds;
if (elapsedMs < Constants.NetworksRefreshThreadInterval)
Thread.Sleep(Constants.NetworksRefreshThreadInterval - elapsedMs);
}
}
Custom DataGridView:
public class CustomDataGridView : DataGridView
{
...
protected override void OnCellClick(DataGridViewCellEventArgs e)
{
base.OnCellClick(e);
int Index = e.RowIndex;
if (Index != -1)
{
DataGridViewRow row = Rows[Index];
PrimaryKeyForSelectedRow = row.Cells[KeyName].Value.ToString();
}
}
}
The DataGridView is my custom DataGrid where I have a click event handler. I have observed that sometimes the event handler isn't called but in most cases it is.
What could be the problem? Is it related to multithreading or the event isn't queued?
Your code blocks main thread, use separate thread for your network details update. Here is quick sample how it done.
class Program
{
static void Main(string[] args)
{
var helper = new Looper(5000, YourMethod_RefreshThread);
helper.Start();
}
private static void YourMethod_RefreshThread()
{
Console.WriteLine(DateTime.Now);
}
}
public class Looper
{
private readonly Action _callback;
private readonly int _interval;
public Looper(int interval, Action callback)
{
if(interval <=0)
{
throw new ArgumentOutOfRangeException("interval");
}
if(callback == null)
{
throw new ArgumentNullException("callback");
}
_interval = interval;
_callback = callback;
}
private void Work()
{
var next = Environment.TickCount;
do
{
if (Environment.TickCount >= next)
{
_callback();
next = Environment.TickCount + _interval;
}
Thread.Sleep(_interval);
} while (IsRunning);
}
public void Start()
{
if (IsRunning)
{
return;
}
var thread = new Thread(Work);
thread.Start();
IsRunning = true;
}
public void Stop()
{
this.IsRunning = false;
}
public bool IsRunning { get; private set; }

Should i pass a Backgroundworker to method

I have an app that has several methods that take a long time to complete. I am using a backgroundworker to run these methods and keep my UI responsive. My methods look something like
public void DoSomething()
{
while( HaveMoreWork )
{
// do work
}
}
Now i want the UI to be able to cancel this at any time so I have changed my methods to take a Backgroundworker like so
public void DoSomething(Backgroundworker worker)
{
while( HaveMoreWork && !worker.CancelationPending )
{
// do work
}
}
My question is, is there a better way to do this. Seems like passing a Backgroundwoker as an argument to all these methods is a bit messy. What is best practice for this?
I am using global variable
private BackgroundWorker _bwSearch = new BackgroundWorker();
private void InitializeBackgroundWorker()
{
_bwSearch = new BackgroundWorker();
_bwSearch.WorkerSupportsCancellation = true;
_bwSearch.DoWork += bwSearch_DoWork;
_bwSearch.RunWorkerCompleted += bwSearch_RunWorkerCompleted;
}
when clicked on stop button
private void btnCancel_Click(object sender, EventArgs e)
{
_bwSearch.Abort();
}
Updated:
Also I am using this simple helper class that is inherited from BackgroundWorker
public class AbortableBackgroundWorker : BackgroundWorker
{
private Thread _workerThread;
protected override void OnDoWork(DoWorkEventArgs e)
{
_workerThread = Thread.CurrentThread;
try
{
base.OnDoWork(e);
}
catch (ThreadAbortException)
{
e.Cancel = true;
Thread.ResetAbort();
}
}
public void Abort()
{
if (_workerThread != null)
{
_workerThread.Abort();
_workerThread = null;
}
}
}
public class DoSomethingService
{
private volatile bool _stopped = false;
public void Start(object socketQueueObject)
{
while (!_stopped)
{
...
}
}
public void Stop()
{
_stopped = true;
}
}
...
var doSomethingService = DoSomethingService();
doSomethingService.Start();
...
doSomethingService.Stop();

Categories