I've got a WCF windows service running. It basically registers a number of clients and broadcasts messages to them as and when needed.
Generally it works fine, but recently I've been getting an error trying to register a new client, there are about 24 clients connected already, but when I try to register a 25th, I get "Index was outside the bounds of the array." being returned.
If I restart the service, all the clients reconnect, and the new client is able to register.
The NotifyServer method is called to broadcast a message to all the registered clients. This runs through the clients dictionary creating an async task that sends the message. This is done so that any issue happening with sending to one client does not impact the sending of the message to others.
The service is set up as to use a Reliable Connection.
// List of connected Clients
//
private static Dictionary<string, IBroadcastorCallBack> clients = new Dictionary<string, IBroadcastorCallBack>();
// lock indicator object
//
private static object locker = new object();
public string RegisterClient(string clientName)
{
string returnValue = "";
if (!string.IsNullOrEmpty(clientName))
{
try
{
var callback = OperationContext.Current.GetCallbackChannel<IBroadcastorCallBack>();
lock (locker)
{
// Remove the old client if its already there
//
if (clients.Keys.Contains(clientName))
{
clients.Remove(clientName);
}
// Add the new one
//
clients.Add(clientName, callback);
}
}
catch (Exception ex)
{
// Return any error message
//
returnValue = ex.Message;
}
}
return returnValue;
}
/// <summary>
/// Notify the service of a message that can be broadcast
/// </summary>
/// <param name="eventData">Message details</param>
///
public async void NotifyServer(EventDataType eventData)
{
DateTime Start = DateTime.Now;
// Get a list copy of the dictionary for all clients bar the one sending it. This is so we can't update the list inside the loop.
//
var clientlist = clients.Where(x => x.Key != eventData.ClientName).ToList();
// Logging
//
if (MetricBroadcast.Properties.Settings.Default.LogXML)
{
log.Debug("XML Broadcast from " + eventData.ClientName + " to " + clientlist.Count.ToString() + " clients:\n" + eventData.EventMessage + "\n");
}
// Broadcast to all the valid clients
//
var BroadcastToClientList = clientlist.Select(client => BroadcastMessage(client.Value, client.Key, eventData)).ToList();
// Wait until they are all done
//
await Task.WhenAll(BroadcastToClientList);
// If we are logging and the broadcast time is > 1 second, we make a log entry
//
DateTime End = DateTime.Now;
if (MetricBroadcast.Properties.Settings.Default.LogXML)
{
TimeSpan res = End - Start;
if (res.TotalSeconds > 1)
{
var timetaken = string.Format("XML Broadcast Time : {0,2:00}:{1,2:00}.{2,3:000}", res.Minutes, res.Seconds, res.Milliseconds);
log.Debug(timetaken);
}
}
}
private async Task<bool> BroadcastMessage(IBroadcastorCallBack clientCallback, string ClientKey, EventDataType eventData)
{
bool retval = true;
Exception savedEx = null;
DateTime BroadStart = DateTime.Now;
try
{
// Send the message to the current client
//
clientCallback.BroadcastToClient(eventData);
}
catch (Exception e)
{
// If we can't access the current clients callback method,
// we remove them from the clients list, as they've probably lost their connection.
//
clients.Remove(ClientKey);
savedEx = e;
retval = false;
}
// Log any broadcast that took > .5 seconds
//
DateTime BroadEnd = DateTime.Now;
if (MetricBroadcast.Properties.Settings.Default.LogXML)
{
TimeSpan res = BroadEnd - BroadStart;
if (res.TotalSeconds > .5)
{
var timetaken = string.Format("Single XML Broadcast Time to " + ClientKey + " : {0,2:00}:{1,2:00}.{2,3:000}", res.Minutes, res.Seconds, res.Milliseconds);
log.Debug(timetaken, savedEx);
}
}
return retval;
}
Most likely the problem is incorrect usage of static clients Dictionary in multithreaded environment. New client can register at any time, including in the middle of your BroadcastMessage and NotifyServer functions. Dictionary was not designed for access from multiple threads. Take for example this:
clients.Where(x => x.Key != eventData.ClientName).ToList();
What happens if client is removed in the middle of this enumeration? Who knows, because it was not designed for this, but most likely some exception (like your "index out of bounds") will be thrown. That is why you need to lock your dictionary for both writes and reads, not just for writes:
List<KeyValuePair<string, IBroadcastorCallback>> clientList;
lock (locker)
clientList = clients.Where(x => x.Key != eventData.ClientName).ToList();
Another option is to use ConcurrentDictionary class. It was designed for concurrent access and you can safely read and write to it from multiple threads.
Related
I managed to get connection with Azure IoTHub to send and receive device2Cloud Messages. I send the message with Microsoft.Azure.Devices.Client DeviceClient & consume the messages with Azure.Messaging.EventHubs.Consumer;
This is my code for sending the telemetry data
public async Task<IoTClientStatistics> SendDeviceToCloudMessageAsync(string messageContents)
{
if (_deviceClient == null || IsInitialized == false)
throw new InvalidAsynchronousStateException("You must call Initialize() before sending a message");
// IoT Hub message is a specific object that takes a byte array.
// You can then do many things...
Message message = new Message(Encoding.UTF8.GetBytes(messageContents));
// ...For example: add a custom application property to the message.
// An IoT hub can filter on these properties without access to the message body.
// Here we add the current runtime platform (Android, iOS, ...)
//message.Properties.Add("platform", Device.RuntimePlatform);
message.Properties.Add("name", "DeviceSample");
try
{
// Sending the event. If the event fails to be sent (no network, etc...)
// an exception will be raised.
await _deviceClient.SendEventAsync(message);
Console.WriteLine("Sent!");
// Updating statistics
_statistics.MessagesSent += 1;
_statistics.LastMessageSent = messageContents;
}
catch (Exception e)
{
_statistics.LastMessageSent = "Error: " + e;
_statistics.SendFailures += 1;
}
return _statistics;
}
This is my code for receiving the telemetry data
public async void ReceiveDeviceToCloudMessageAsync()
{
try
{
using CancellationTokenSource cancellationSource = new CancellationTokenSource();
cancellationSource.CancelAfter(TimeSpan.FromSeconds(45));
int eventsRead = 0;
int maximumEvents = 2;
var readEvents = _eventHubConsumerClient.ReadEventsAsync(cancellationSource.Token);
await foreach (PartitionEvent partitionEvent in readEvents)
{
string readFromPartition = partitionEvent.Partition.PartitionId;
byte[] eventBodyBytes = partitionEvent.Data.EventBody.ToArray();
yellowMessage($"\nMessage received on partition {partitionEvent.Partition.PartitionId}:");
string data = Encoding.UTF8.GetString(partitionEvent.Data.Body.ToArray());
greenMessage($"\tMessage body: {data}");
_statistics.MessagesReceived++;
_statistics.LastMessageReceived = data;
yellowMessage("\tApplication properties (set by device):");
foreach (KeyValuePair<string, object> prop in partitionEvent.Data.Properties)
{
PrintProperties(prop);
}
yellowMessage("\tSystem properties (set by IoT Hub):");
foreach (KeyValuePair<string, object> prop in partitionEvent.Data.SystemProperties)
{
PrintProperties(prop);
}
eventsRead++;
if (eventsRead >= maximumEvents)
{
break;
}
}
}
catch (TaskCanceledException e)
{
redMessage(e.ToString());
}
}
Problem is all the application properties I received at the partitionEvent DO NOT contain the application properties I added for the sent messages.
There is 1 possibility which still do not make sense to me, first my IoT device is configured to ignore message Application Properties. But I tested on another client app and the application properties were there normally (Using a Console App to send & a console app to receive with the same Key configuration)
Still struggle to understand why this is happening,
Is there any one expert on this topic ?
Following the MS documentation it was not difficult to receive message(s) from subscription. However, if I'd like my application to receive a message every time new message is posted - a constant polling. Hence the OnMessage() method of the SubscriptionClient class.
MS documentation says: "...When calling OnMessage, the client starts an internal message pump that constantly polls the queue or subscription. This message pump consists of an infinite loop that issues a Receive() call. If the call times out, it issues the next Receive() call. ..."
But when the application is running, the moment OnMessage() method is called only latest message(s) is received. When new messages are posted the constant polling does not seem to be working. After trying many different approaches the only way I could make this work and have the application react the moment new message is received is to place the code into a separate task with infinite loop. This seems totally wrong on so many levels! (see code below).
Can anyone help me to correct my code or post a working sample to accomplish the same functionality without the loop? Thank you!
public void ReceiveMessageFromSubscription(string topicName, string subscriptionFilter)
{
var newMessage = new MessageQueue();
int i = 0;
Task listener = Task.Factory.StartNew(() =>
{
while (true)
{
SubscriptionClient Client = SubscriptionClient.CreateFromConnectionString(connectionString, topicName, subscriptionFilter);
Dictionary<string, string> retrievedMessage = new Dictionary<string, string>();
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromMinutes(1);
Client.OnMessage((message) =>
{
try
{
retrievedMessage.Add("messageGuid", message.Properties["MessageGuid"].ToString());
retrievedMessage.Add("instanceId", message.Properties["InstanceId"].ToString());
retrievedMessage.Add("pId", message.Properties["ProcessId"].ToString());
retrievedMessage.Add("processKey", message.Properties["ProcessKey"].ToString());
retrievedMessage.Add("message", message.Properties["Message"].ToString());
newMessage.AnnounceNewMessage(retrievedMessage); // event ->
message.Complete(); // Remove message from subscription.
}
catch (Exception ex)
{
string exmes = ex.Message;
message.Abandon();
}
}, options);
retrievedMessage.Clear();
i++;
Thread.Sleep(3000);
}
});
}
Your code has a few issues to iron out -
It falls through and I assume your application then exits - or at
least the thread that is listening for the messages terminates.
Your while loop keeps repeating the code to hook up the message handler,
you only need to do this once.
You need a way to keep the call stack alive and prevent your app from garbage collecting your object.
The below should see you on your way to success. Good luck.
ManualResetEvent CompletedResetEvent = new ManualResetEvent(false);
SubscriptionClient Client;
public void ReceiveMessagesFromSubscription(string topicName, string subscriptionFilter, string connectionString)
{
Task listener = Task.Factory.StartNew(() =>
{
// You only need to set up the below once.
Client = SubscriptionClient.CreateFromConnectionString(connectionString, topicName, subscriptionFilter);
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromMinutes(1);
options.ExceptionReceived += LogErrors;
Client.OnMessage((message) =>
{
try
{
Trace.WriteLine("Got the message with ID {0}", message.MessageId);
message.Complete(); // Remove message from subscription.
}
catch (Exception ex)
{
Trace.WriteLine("Exception occurred receiving a message: {0}" + ex.ToString());
message.Abandon(); // Failed. Leave the message for retry or max deliveries is exceeded and it dead letters.
}
}, options);
CompletedResetEvent.WaitOne();
});
}
/// <summary>
/// Added in rudimentary exception handling .
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="ex">The <see cref="ExceptionReceivedEventArgs"/> instance containing the event data.</param>
private void LogErrors(object sender, ExceptionReceivedEventArgs ex)
{
Trace.WriteLine("Exception occurred in OnMessage: {0}" + ex.ToString());
}
/// <summary>
/// Call this to stop the messages arriving from subscription.
/// </summary>
public void StopMessagesFromSubscription()
{
Client.Close(); // Close the message pump down gracefully
CompletedResetEvent.Set(); // Let the execution of the listener complete and terminate gracefully
}
Alternatively you can chunk the message off in a more traditional fashion yourself using ReceiveBatch:
var messages = await queueClient.ReceiveBatchAsync(10, TimeSpan.FromSeconds(30),
cancellationToken);
Recently I had to build a small TCP client app, that connects to an external app's TCP listener and is intended to deal with large amount of data volumes and on high frequencies.
I made a wrapper class around the TCPClient class, just to catch exceptions, and keep reference to some properties of interest (network stream etc.). Here is the wrapper:
public class MyTCPClient
{
private string serverIP;
private int serverPort;
public TcpClient tcpClient = new TcpClient();
private IPEndPoint serverEndPoint;
private NetworkStream stream = null;
public string name;
public MyTCPClient(string serverIp, int serverPort, string parentName)
{
this.serverIP = serverIp;
this.serverPort = serverPort;
this.name = parentName + "_TCPClient";
serverEndPoint = new IPEndPoint(IPAddress.Parse(serverIP), serverPort);
tcpClient.ReceiveBufferSize = 1048576;
this.TryConnect();
}
private bool TryConnect()
{
try
{
tcpClient.Connect(serverEndPoint);
}
catch (SocketException e1)
{
throw new ErrorOnConnectingException(e1, "SocketException while connecting. (see msdn Remarks section for more details. ) Error code: " + e1.ErrorCode);
}
catch (ArgumentNullException e2)
{
throw new ErrorOnConnectingException(e2, "ArgumentNullException while connecting. (The hostname parameter is null.) Message: " + e2.Message);
}
catch (ArgumentOutOfRangeException e3)
{
throw new ErrorOnConnectingException(e3, "ArgumentOutOfRangeException while connecting (The port parameter is not between MinPort and MaxPort. ). Message: " + e3.Message);
}
catch (ObjectDisposedException e4)
{
throw new ErrorOnConnectingException(e4, "ObjectDisposedException while connecting. (TcpClient is closed. ) Message: " + e4.Message);
}
try
{
stream = this.tcpClient.GetStream();
}
catch (ObjectDisposedException e1)
{
throw new ErrorOnGettingStreamException(e1, "ObjectDisposedException while acquiring Network stream. (The TcpClient has been closed. ) Message: " + e1.Message);
}
catch (InvalidOperationException e2)
{
throw new ErrorOnGettingStreamException(e2, "ArgumentOutOfRangeException while acquiring Network stream (The TcpClient is not connected to a remote host. ). Message: " + e2.Message);
}
return true;
}
public string ReadData()
{
try
{
ASCIIEncoding encoder = new ASCIIEncoding();
byte[] dataHeader = new byte[12];
if (this.tcpClient.Connected)
{
stream.Read(dataHeader, 0, 12);
}
else
{
throw new ErrorOnReadingException(null, "The underlying TCP tcpClient is not connected any more");
}
var strHeaderMessage = System.Text.Encoding.Default.GetString(dataHeader);
Utils.logToTimeStampedFile(strHeaderMessage, name);
int bodyAndTailCount = Convert.ToInt32(strHeaderMessage.Replace("#", ""));
byte[] dataBodyAndTail = new byte[bodyAndTailCount];
if (this.tcpClient.Connected)
{
stream.Read(dataBodyAndTail, 0, bodyAndTailCount);
}
else
{
throw new ErrorOnReadingException(null, "The underlying TCP tcpClient is not connected any more");
}
var strBodyAndTailMessage = System.Text.Encoding.Default.GetString(dataBodyAndTail);
Utils.logToTimeStampedFile(strBodyAndTailMessage, name);
return strBodyAndTailMessage;
}
catch (FormatException e0)
{
CloseAllLeft();
throw new ErrorOnReadingException(e0, "FormatException while reading data. (Bytes red are null or does not correspond to specification, happens on closing Server) Message: " + e0.Message);
}
catch (ArgumentNullException e1)
{
CloseAllLeft();
throw new ErrorOnReadingException(e1, "ArgumentNullException while reading data. (The buffer parameter is null.) Message: " + e1.Message);
}
catch (ArgumentOutOfRangeException e2)
{
CloseAllLeft();
throw new ErrorOnReadingException(e2, "ArgumentOutOfRangeException while reading data. (see msdn description) Message: " + e2.Message);
}
catch (IOException e3)
{
CloseAllLeft();
throw new ErrorOnReadingException(e3, "IOException while reading data. (The underlying Socket is closed.) Message: " + e3.Message);
}
catch (ObjectDisposedException e4)
{
CloseAllLeft();
throw new ErrorOnReadingException(e4, "ArgumentOutOfRangeException while reading data. (see msdn description) Message: " + e4.Message);
}
}
public void CloseAllLeft()
{
try
{
stream.Close();
}
catch (Exception e)
{
Console.WriteLine("Exception closing tcp network stream: " + e.Message);
}
try
{
tcpClient.Close();
}
catch (Exception e)
{
Console.WriteLine("Exception closing tcpClient: " + e.Message);
}
}
}
Still, nothing mentioned about the threads using this MyTCPClient. The app should have two such TCP clients, connecting on different ports, and doing different jobs. I was new to TCP programming and after some wandering around the properties I decided to use the blocking read approach - i.e by default the TCPClient.Read() method will block the thread until there is new data. I needed such approach bucause I do not have control on the external app's listener, and the only way to recognize a server's closing was the "zero bytes" sent as per TCP Sockets specs.
So, i build an abstract class that will maintain and control threads that will later make use of the above MyTCPClient class (which by design, eventually might block the parent theads). Here is the code for my abstract TCPManager:
/// <summary>
/// Serves as a dispatcher for the high frequency readings from the TCP pipe.
/// Each time the thread is started it initializes new TCPClients which will attempt to connect to server.
/// Once established a TCP socket connection is alive until the thread is not requested to stop.
///
/// Error hanling level here:
///
/// Resources lke NetworkStream and TCPClients are ensured to be closed already within the myTCPClient class, and the error handling here
/// is steps on top of that - sending proper emails, notifications and logging.
///
/// </summary>
public abstract class AbstractmyTCPClientManager
{
public string name;
public string serverIP;
public int serverPort;
public Boolean requestStop = false;
public Boolean MyTCPClientThreadRunning = false;
public Boolean requestStart = false;
public myTCPClient myTCPClient;
public int sleepInterval;
public Thread MyTCPClientThread;
public AbstractmyTCPClientManager(string name, string serverIP, int serverPort)
{
this.name = name;
this.serverIP = serverIP;
this.serverPort = serverPort;
}
public void ThreadRun()
{
MyTCPClientThreadRunning = false;
bool TCPSocketConnected = false;
bool AdditionalInitializationOK = false;
// keep trying to init requested tcp clients
while (!MyTCPClientThreadRunning && !requestStop) // and we are not suggested to stop
{
while (!TCPSocketConnected && !requestStop) // and we are not suggested to stop)
{
try
{
myTCPClient = new myTCPClient(serverIP, serverPort, name);
TCPSocketConnected = true;
}
catch (ErrorOnConnectingException e0)
{
// nah, too long message
string detail = e0.originalException != null ? e0.originalException.Message : "No inner exception";
//Utils.logToTimeStampedFile("Creating connection attempt failed.(1." + e0.customMessage + " 2." + detail + "). Will retry in 10 seconds...", name);
//Utils.logToTimeStampedFile(e0.customMessage + " (" + detail + "). Will retry in 10 seconds...", name);
Utils.logToTimeStampedFile(detail + ". Will retry in 10 seconds...", name);
Thread.Sleep(10000);
}
catch (ErrorOnGettingStreamException e1)
{
// nah, too long message
string detail = e1.originalException != null ? e1.originalException.Message : "No inner exception";
//Utils.logToTimeStampedFile("Getting network stream attempt failed. (1." + e1.customMessage + " 2." + detail + "). Will retry in 10 seconds...", name);
//Utils.logToTimeStampedFile(e1.customMessage + " (" + detail + "). Will retry in 10 seconds...", name);
Utils.logToTimeStampedFile(detail + ". Will retry in 10 seconds...", name);
Thread.Sleep(10000);
}
}
Utils.logToTimeStampedFile("TCP Communication established", name);
while (!AdditionalInitializationOK && !requestStop) // or we are not suggested to stop
{
try
{
AdditionalInitialization();
AdditionalInitializationOK = true;
}
catch (AdditionalInitializationException e1)
{
string detail = e1.originalException != null ? e1.originalException.Message : "No inner exception";
//Utils.logToTimeStampedFile("Additional initialization failed (1." + e1.customMessage + " 2." + detail + "). Will retry in 10 seconds", name);
Utils.logToTimeStampedFile(e1.customMessage + ". Will retry in 10 seconds", name);
Thread.Sleep(10000);
}
}
MyTCPClientThreadRunning = TCPSocketConnected && AdditionalInitializationOK;
ViewModelLocator.ControlTabStatic.updateUIButtons();
}
Utils.logToTimeStampedFile("Additional Initialization successfully completed, thread started", name);
// while all normal (i.e nobody request a stop) continiously sync with server (read data)
while (!requestStop)
{
try
{
syncWithInterface();
}
catch (ErrorOnReadingException e1)
{
string detail = e1.originalException != null ? e1.originalException.Message : "No inner exception";
//Utils.logToTimeStampedFile("Error ocured while reading data. (1." + e1.customMessage + " 2." + detail + ")", name);
Utils.logToTimeStampedFile(e1.customMessage, name);
if (!requestStop) // i.e if this indeed is an exception, during a normal flow, and nobody requested a thread stop (which migh cause read exceptions as a consequence)
{
Utils.logToTimeStampedFile("There was no external stop request, when the error occured, doing tcp client restart.", name);
requestStop = true;
requestStart = true;
}
}
Thread.Sleep(sleepInterval);
}
// we need to close all after execution, but the execution may be closed before/while resources were still initializing
if (TCPSocketConnected)
{
myTCPClient.CloseAllLeft();
}
if (AdditionalInitializationOK)
{
ReleaseAdditionalResources();
}
// remember that thread is stoped
MyTCPClientThreadRunning = false;
Utils.logToTimeStampedFile("Thread stoped", name);
ViewModelLocator.ControlTabStatic.updateUIButtons();
// this serves as a restart
if (requestStart)
{
Utils.logToTimeStampedFile("Restarting thread...", name);
this.requestStop = false;
this.requestStart = false; // we are already processing a request start event, so reset this flag
this.MyTCPClientThread = new Thread(new ThreadStart(this.ThreadRun));
this.MyTCPClientThread.Name = this.name;
this.MyTCPClientThread.IsBackground = true;
this.MyTCPClientThread.Start();
}
}
/// <summary>
/// this method empties the entire TCP buffer, cycling through it
/// </summary>
private void syncWithInterface()
{
int counter = 0;
// read at most 100 messages at once (we assume that for 3 sec interval there might not be more,
//even if they are, it is still OK, they just will be processed next time)
while (counter < 100)
{
counter++;
string data = myTCPClient.ReadData();
ForwardData(data);
}
// below is left for testing:
/*
* "Sleep(0) or Yield is occasionally useful in production code for
* advanced performance tweaks. It’s also an excellent diagnostic tool
* for helping to uncover thread safety issues: if inserting Thread.Yield()
* anywhere in your code makes or breaks the program, you almost certainly have a bug."*/
Thread.Yield();
}
/// <summary>
/// Left for implementing in the caller that initialized the object. Meaning: one and the same way for receiving market/order data. Different ways of processing this data
/// </summary>
/// <param name="data"></param>
public abstract void ForwardData(string data);
/// <summary>
/// left for implementing in child classes. Its purpose is to initialize any additional resources needed for the thread to operate.
/// If something goes wrong while getting this additional resources,
/// an AdditionalInitialization exception should be thrown, which is than handled from the initialization phase in the caller.
/// </summary>
public abstract void AdditionalInitialization();
// countrapart of AdditionalInitialization method - what is initialized should be then closed
public abstract void ReleaseAdditionalResources();
}
Later, each needed TCP communication channel would have a dedicated implementation for the above abstract class, providing implementation of the methods ForwardData (i.e what to do with this data) and AdditionalInitialization (i.e what else is needed to be initialized before a particular TCP communication processing is run. For example on of my threads required additional storage Thread to be initialized prior receiving data).
Everything was fine, except for closing the TCP processing. I had this requestStop variables to control whther a thread should exit or continue, but the thing is that Read() method may fall in continious blocking, preventing even the requestStop variable from being read (I should say that the two tcp channels I need to process are very different in that one of them is very frequently receiving data and the other one - sporadically). I would like still to have them implementing the same design. So from what I am reading so far I have to implement another, "parent", or "controlling", or "wrapper" thread that will actually take the job on observing requestStop parameter.
I am looking towards solutions like this post, or timers like this post
Any suggestions would be greatly appreciated. Thanks!
I would personally use asynchronous sockets for this:
http://msdn.microsoft.com/en-us/library/bbx2eya8.aspx
If you, however, still want to use blocking reads, you one could simply be to Close() the socket from another thread.
I hope this help.
I would recommend calling the ReadAsync method of NetworkStream and passing a CancellationToken to it. This way, the read operation can be easily cancelled (from another thread) when a request stop event is observed:
public class MyTCPClient : IDisposable
{
...
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource ();
...
public string ReadData()
{
...
byte[] dataHeader = new byte[12];
if (this.tcpClient.Connected)
{
stream.ReadAsync(dataHeader, 0, 12, cancellationTokenSource.Token).Wait();
} ...
Set your 'requestStop' bool and Close the client socket from another thread. This causes the read() call to return 'early' with an error/exception. The client thread can check 'requestStop' after every read() return and clean up/exit if requested.
TBH, I rarely bother with explicitly shutting down such clients anyway. I just leave them until the app exits.
I am using Named Pipes to communicate with a process. I have been able to make it work with the following code. (Original code found here : via archive.org )
class ProgramPipeTest
{
public void ThreadSenderStartClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".","ToSrvPipe",PipeDirection.Out,PipeOptions.None))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
Console.WriteLine("[Client] Pipe connection established");
using (StreamWriter sw = new StreamWriter(pipeStream))
{
sw.AutoFlush = true;
string temp;
Console.WriteLine("Please type a message and press [Enter], or type 'quit' to exit the program");
while ((temp = Console.ReadLine()) != null)
{
if (temp == "quit") break;
sw.WriteLine(temp);
}
}
}
}
public void ThreadStartReceiverClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "FromSrvPipe", PipeDirection.In, PipeOptions.None))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
Console.WriteLine("[ClientReceiver] Pipe connection established");
using (StreamReader sr = new StreamReader(pipeStream))
{
// Display the read text to the console
string temp;
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("Received from server: {0}", temp);
}
}
}
}
static void Main(string[] args)
{
// To simplify debugging we are going to create just one process, and have two tasks
// talk to each other. (Which is a bit like me sending an e-mail to my co-workers)
ProgramPipeTest Client = new ProgramPipeTest();
Thread ClientThread = new Thread(Client.ThreadSenderStartClient);
Thread ReceivedThread = new Thread(Client.ThreadStartReceiverClient);
ClientThread.Start();
ReceivedThread.Start();
}
}
Everything works as intended. I am able to issue commands to my target process (audacity).
My issue is, I basically want to wrap a C# GUI around this code, but am not sure how to modify it so that the communication is done without having to use the console, as commands would be issued via the GUI or from the code.
I have tried turning the streamWriter sw into a class variable, exposing it via property and calling sw.WriteLine() with a method, but that doesn't seem to work.
So I am unsure how to encapsulate the stream back and forth nicely within an object.
I found this article which seemed like it was spot on, Using Named Pipes to Connect a GUI to a Console App in Windows, but unfortunately it does not seem to come with any code and is kind of over my head without any to refer to.
So how can I use named pipes without having to use the console to issue the commands ?
What you want to do is take the main pieces of logic which are the sender, the receiver out of that code and rewrite it into a re-usable class that can be used like a purpose-specific wrapper class.
Perhaps the code below could serve as a guideline (I have NOT checked to see if this works, it might require minor changes)
public sealed class ResponseReceivedEventArgs : EventArgs
{
public ResponseReceivedEventArgs(string id, string response)
{
Id = id;
Response = response;
}
public string Id
{
private set;
get;
}
public string Response
{
private set;
get;
}
}
public delegate void ResponseReceived(object sender, ResponseReceivedEventArgs e);
public sealed class NamedPipeCommands
{
private readonly Queue<Tuple<string, string>> _queuedCommands = new Queue<Tuple<string,string>>();
private string _currentId;
private readonly Thread _sender;
private readonly Thread _receiver;
// Equivalent to receiving a "quit" on the console
private bool _cancelRequested;
// To wait till a response is received for a request and THEN proceed
private readonly AutoResetEvent _waitForResponse = new AutoResetEvent(false);
// Lock to modify the command queue safely
private readonly object _commandQueueLock = new object();
// Raise an event when a response is received
private void RaiseResponseReceived(string id, string message)
{
if (ResponseReceived != null)
ResponseReceived(this, new ResponseReceivedEventArgs(id, message));
}
// Add a command to queue of outgoing commands
// Returns the id of the enqueued command
// So the user can relate it with the corresponding response
public string EnqueueCommand(string command)
{
var resultId = Guid.NewGuid().ToString();
lock (_commandQueueLock)
{
_queuedCommands.Enqueue(Tuple.Create(resultId, command));
}
return resultId;
}
// Constructor. Please pass in whatever parameters the two pipes need
// The list below may be incomplete
public NamedPipeCommands(string servername, string pipeName)
{
_sender = new Thread(syncClientServer =>
{
// Body of thread
var waitForResponse = (AutoResetEvent)syncClientServer;
using (var pipeStream = new NamedPipeClientStream(servername, pipeName, PipeDirection.Out, PipeOptions.None))
{
pipeStream.Connect();
using (var sw = new StreamWriter(pipeStream) { AutoFlush = true })
// Do this till Cancel() is called
while (!_cancelRequested)
{
// No commands? Keep waiting
// This is a tight loop, perhaps a Thread.Yield or something?
if (_queuedCommands.Count == 0)
continue;
Tuple<string, string> _currentCommand = null;
// We're going to modify the command queue, lock it
lock (_commandQueueLock)
// Check to see if someone else stole our command
// before we got here
if (_queuedCommands.Count > 0)
_currentCommand = _queuedCommands.Dequeue();
// Was a command dequeued above?
if (_currentCommand != null)
{
_currentId = _currentCommand.Item1;
sw.WriteLine(_currentCommand.Item2);
// Wait for the response to this command
waitForResponse.WaitOne();
}
}
}
});
_receiver = new Thread(syncClientServer =>
{
var waitForResponse = (AutoResetEvent)syncClientServer;
using (var pipeStream = new NamedPipeClientStream(servername, pipeName, PipeDirection.In, PipeOptions.None))
{
pipeStream.Connect();
using (var sr = new StreamReader(pipeStream))
// Do this till Cancel() is called
// Again, this is a tight loop, perhaps a Thread.Yield or something?
while (!_cancelRequested)
// If there's anything in the stream
if (!sr.EndOfStream)
{
// Read it
var response = sr.ReadLine();
// Raise the event for processing
// Note that this event is being raised from the
// receiver thread and you can't access UI here
// You will need to Control.BeginInvoke or some such
RaiseResponseReceived(_currentId, response);
// Proceed with sending subsequent commands
waitForResponse.Set();
}
}
});
}
public void Start()
{
_sender.Start(_waitForResponse);
_receiver.Start(_waitForResponse);
}
public void Cancel()
{
_cancelRequested = true;
}
public event ResponseReceived ResponseReceived;
}
You can see that I have created abstractions for the Console.ReadLine (the command queue) and Console.WriteLine (the event). The "quit" is also a boolean variable that is set by the "Cancel()" method now. Obviously this isn't the most optimal/correct way of doing it - I am just showing you one way to relate the imperative code from above into a wrapper class that can be re-used.
i have a simple windows service which runs and starts a thread which listen/receive heartbeat via tcp/ip. i'm having a hard time finding ways to sync between getting information from the tcp thread and using that value to update something in the main thread.
i try to use a thread.sleep method and keep on looping it for a few times while awaiting the answer back from the thread and then getting the value, but that method seems to be a bit volatile with the method sometimes working and sometimes not.
so what's a good way to sync between these two?
basically what i want to do is to start the listening tcp thread, get specific value and the update the main program.
attached are the receive function and the function which i used to start the thread.
p.s: i'm a totally noobie when it comes to tcp/ip and c# so any comments on any part of the code or the design is more than welcome :)
public virtual void Receive()
{
string eventMessage = string.Empty;
int bytesRcvd = 0;
int totalBytesRcvd = 0;
byte[] byteBuffer = new byte[maxBufferSize];
NetworkStream listenStream;
try
{
if (client.Connected)
{
listenStream = client.GetStream();
}
else
{
return;
}
while (true)
{
//message that is slot in from the object will get sent here.
if (!string.IsNullOrEmpty(MessageToSend))
{
Send(MessageToSend);
MessageToSend = string.Empty;
}
// must convert it back and look for the delimiter, cannot wait for the three heartbeat to pass
string leftoverMsg = string.Empty;
bytesRcvd = listenStream.Read(byteBuffer, totalBytesRcvd, maxBufferSize - totalBytesRcvd);
totalBytesRcvd += bytesRcvd;
//if more than heart beat size, can process to see if it's a heartbeat and proceed to send
if (totalBytesRcvd > msgHeartbeatSize)
{
eventMessage = Encoding.ASCII.GetString(byteBuffer, 0, totalBytesRcvd);
ProcessMessage(eventMessage, ref leftoverMsg, ref totalBytesRcvd, ref byteBuffer);
}
}
}
catch (ThreadAbortException thEx)
{
//do nothing as main thread has aborted and waiting to close
logger.Info(Thread.CurrentThread.Name + " is stopped. ");
}
catch (Exception exce)
{
bIsActive = false;
logger.Error(exce);
CleanUp();
}
finally
{
logger.Info(String.Format("Thread {0} Exiting. ", Thread.CurrentThread.Name));
}
}
public virtual void StartReceivingThread()
{
Thread thrReceive = new Thread(Receive);
try
{
if (!bIsActive && Connect())
{
//NOTE: exception thrown by a thread can only be captured by that thread itself
//start a listen thread
//wait until heartbeat message is accepted
thrReceive.Name = "thr" + serviceType.Name;
thrReceive.Start();
bIsActive = true;
//wait to get the heartbeat message
for (int i = 0; i < maxRetry; i++)
{
Thread.Sleep(maxTimeOutValue);
if (bIsReceivingHeartbeat)
break;
}
//if nothing happens close the connection and try again
if (!bIsReceivingHeartbeat)
{
bIsActive = false;
CleanUp();
logger.Info("Closing receiver thread - " + thrReceive.Name);
}
else
{
logger.Info("Starting receiver thread - " + thrReceive.Name);
}
}
}
catch(Exception ex)
{
logger.Error(ex);
}
//finally
//{
// logger.Info("Exiting receiver thread - " + thrReceive.Name);
//}
}
I assume bIsReceivingHeartbeat is a bool member variable of the class. If the value changed in one thread (receiver) is not visible in the other thread this is most likely due to memory barrier. I am saying this from my Java background but this is most likely true in .net as well.
Try declaring the variables volatile or use a property and make the getter and setter synchronized:
private bool bIsReceivingHeartbeat;
public bool IsReceivingHeartbeat
{
[MethodImpl(MethodImplOptions.Synchronized)]
get { return bIsReceivingHeartbeat; }
[MethodImpl(MethodImplOptions.Synchronized)]
set { bIsReceivingHeartbeat = value; }
}
And in the calling code:
if (!IsReceivingHeartbeat) ....
I am writing from Java background but the situation most likely similar
(Looks like you also posted this code in refactormycode.com.)
Anyway, instead of the loop with a sleep delay, I recommend using an Event object that pulsed by the code that sets IsReceivingHeartbeat. See the ManualResetEvent and AutoResetEvent classes in MSDN.