All, I am trying to cancel two concurrent HttpWebRequests using a method similar to the code below (shown in pseudo-ish C#).
The Main method creates two threads which create HttpWebRequests. If the user wishes to, they may abort the requests by clicking a button which then calls the Abort method.
private Thread first;
private Thread second;
private string uri = "http://somewhere";
public void Main()
{
first = new Thread(GetFirst);
first.Start();
second = new Thread(GetSecond);
second.Start();
// Some block on threads... like the Countdown class
countdown.Wait();
}
public void Abort()
{
try
{
first.Abort();
}
catch { // do nothing }
try
{
second.Abort();
}
catch { // do nothing }
}
private void GetFirst(object state)
{
MyHandler h = new MyHandler(uri);
h.RunRequest();
}
private void GetSecond(object state)
{
MyHandler h = new MyHandler(uri);
h.RunRequest();
}
The first thread gets interrupted by a SocketException:
A blocking operation was interrupted by a call to WSACancelBlockingCall
The second thread hangs on GetResponse().
How can I abort both of these requests in a way that the web server knows that the connection has been aborted?, and/or, Is there a better way to do this?
UPDATE
As suggested, a good alternative would be to use BeginGetResponse. However, I don't have access to the HttpWebRequest object - it is abstracted in the MyHandler class. I have modified the question to show this.
public class MyHandler
{
public void RunRequest(string uri)
{
HttpWebRequest req = HttpWebRequest.Create(uri);
HttpWebResponse res = req.GetResponse();
}
}
Use BeginGetResponse to initiate the call and then use the Abort method on the class to cancel it.
http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest_methods.aspx
I believe Abort will not work with the synchronous GetResponse:
http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.abort.aspx
If you have to stick with the synchronous version, to kill the situation, all you can do is abort the thread. To give up waiting, you can specify a timeout:
http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.timeout.aspx
If you need to kill the process, I would argue launching it inside a new AppDomain and dropping the AppDomain when you want to kill the request; instead of aborting a thread inside your main process.
A ThreadAbortException is highly non-specific. HttpWebRequest already supports a way to cancel the request in a predictable way with the Abort() method. I recommend you use it instead.
Note that you'll still get a WebException on the thread, designed to tell you that the request got aborted externally. Be prepared to catch it.
This might be because of .NET's connection pooling.
Every WebRequest-instance has a ServicePoint that describes the target you want to communicate with (server address, port, protocol,...). These ServicePoints will be reused, so if you create 2 WebRequests with the same server address, port and protocol they will share the same ServicePoint instance.
When you call WebRequest.GetResponse() it uses the connection pool provided by the ServicePoint to create connections. If you then kill the thread with Thread.Abort() it will NOT return the connection to the ServicePoint's connection pool, so the ServicePoint thinks this connection is still in use.
If the connection limit of the ServicePoint is reached (default: 2) it will not create any new connections, but instead wait for one of the open connections to be returned.
You can increase the connection limit like this:
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(url);
httpRequest.ServicePoint.ConnectionLimit = 10;
or you can use the default connection limit, so every new ServicePoint will use this limit:
System.Net.ServicePointManager.DefaultConnectionLimit = 10;
You can also use ServicePoint.CurrentConnections to get the number of open connections.
You could use the following method to abort your thread:
private Thread thread;
private Uri uri;
void StartThread()
{
thread = new Thread(new ThreadStart(() =>
{
WebRequest request = WebRequest.Create(uri);
request.ConnectionGroupName = "SomeConnectionGroup";
var response = request.GetResponse();
//...
}));
thread.Start();
}
void AbortThread()
{
thread.Abort();
ServicePointManager.FindServicePoint(uri).CloseConnectionGroup("SomeConnectionGroup");
}
Remember that ALL connections to the same server (or ServicePoint) that have the same connection group name will be killed. If you have multiple concurrent threads you might want to assign unique connection group names to them.
Related
This is a follow-up question to this question. On the next level, I now want to use maximal task concurrency to connect to expected hosts on a large set of IP addresses, using TCP/IP on a specific port.
My own research, as well as community reference, has lead me to key articles, for example:
How to check TCP/IP port availability using C# (Socket Communication)
Checking if ip with port is available?
How to set the timeout for a TcpClient?
A very impressive solution for large-scale pinging: Multithreading C# GUI ping example
And of course the precursor to this question: C#, Maximize Thread Concurrency
This allowed me to set up my own code, which works fine, but currently takes a full 30 seconds to finish scanning 255 IPs, using only one specific port. Given the test, machine has 8 logical cores this observation suggests that my construct actually spawns at maximum 8 concurrent tasks (255/8=31.85).
The function I wrote returns a list of responding IPs {IPs} which is a subset of the List of all IPs {IP_Ports} to be checked. This is my current code, working fine but not yet suitable for use on larger networks due to what I suspect is lack of efficient task concurrency:
// Check remote host connectivity
public static class CheckRemoteHost
{
// Private Class members
private static bool AllDone = false;
private static object lockObj = new object();
private static List<string> IPs;
// Wrapper: manage async method <TCP_check>
public static List<string> TCP(Dictionary<string, int> IP_Ports, int TimeoutInMS = 100)
{
// Locals
IPs = new List<string>();
// Perform remote host check
AllDone = false;
TCP_check(IP_Ports, TimeoutInMS);
while (!AllDone) { Thread.Sleep(50); }
// Finish
return IPs;
}
private static async void TCP_check(Dictionary<string, int> IP_Ports, int timeout)
{// async worker method: check remote host via TCP-IP
// Build task-set for parallel IP queries
var tasks = IP_Ports.Select(host => TCP_IPAndUpdateAsync(host.Key, host.Value, timeout));
// Start execution queue
await Task.WhenAll(tasks).ContinueWith(t =>
{
AllDone = true;
});
}
private static async Task TCP_IPAndUpdateAsync(string ip, int port, int timeout)
{// method to call IP-check
// Run method asynchronously
await Task.Run(() =>
{
// Locals
TcpClient client;
IAsyncResult result;
bool success;
try
{
client = new TcpClient();
result = client.BeginConnect(ip, port, null, null);
success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeout));
if (success)
{
lock (lockObj)
{
IPs.Add(ip);
}
}
}
catch (Exception e)
{
// do nothing
}
});
}
}// end public static class CheckRemoteHost
So my question is: how can I maximize the task concurrency of requesting a response using TCP/IP at Port X such that I can obtain very fast IP-Port network scans on large internal networks?
Details
The default task scheduler is usually the ThreadPool scheduler. That means the number of concurrent tasks will be limited by the available threads in the pool.
Remarks
The thread pool provides new worker threads or I/O completion threads on demand until it reaches the minimum for each category. By default, the minimum number of threads is set to the number of processors on a system. When the minimum is reached, the thread pool can create additional threads in that category or wait until some tasks complete. Beginning with the .NET Framework 4, the thread pool creates and destroys threads in order to optimize throughput, which is defined as the number of tasks that complete per unit of time. Too few threads might not make optimal use of available resources, whereas too many threads could increase resource contention.
(Source: https://msdn.microsoft.com/en-us/library/system.threading.threadpool.getminthreads(v=vs.110).aspx)
You are likely just under the threshold where the threadpool would spin up new threads since tasks are being completed. Hence why you only have 8 concurrent tasks running at once.
Solutions
1. Use ConnectAsync with a timeout.
Instead of creating a separate task which blocks waiting for the connect. You can call ConnectAsync and join it with a delay to create a timeout. ConnectAsync doesn't seem to block the threadpool threads.
public static async Task<bool> ConnectAsyncWithTimeout(this Socket socket, string host, int port, int timeout = 0)
{
if (timeout < 0)
throw new ArgumentOutOfRangeException("timeout");
try
{
var connectTask = socket.ConnectAsync(host, port);
var res = await Task.WhenAny(connectTask, Task.Delay(timeout));
await res;
return connectTask == res && connectTask.IsCompleted && !connectTask.IsFaulted;
}
catch(SocketException se)
{
return false;
}
}
Example usage
private static async Task TCP_IPAndUpdateAsync(string ip, int port, int timeout)
{// method to call IP-check
client = new TcpClient();
var success = await client.Client.ConnectAsyncWithTimeout(ip, port, timeout);
if (success)
{
lock (lockObj)
{
IPs.Add(ip);
}
}
}
2. Use long running tasks.
Using Task.Factor.StartNew you can specify that the task is LongRunning. The threadpool task scheduler specifically will create a new thread for the task instead of using the threadpool. This will get around the 8 thread limit you are hitting. However, it should be noted that this is not a good solution if you plan to naively create thousands of tasks. Since at that point, the bottle neck will be thread context switches. You could however split all of the work between, for example, 100 tasks.
3. Use non-blocking connect
This method doesn't require creating multiple tasks. Instead you can call multiple connects on a single thread and check the status of multiple sockets at once. This method is a bit more involved though. If you rather go with this approach and want a more complete example then comment letting me know. Here is a quick snippet on how to use the API.
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Blocking = false;
try
{
socket.Connect("127.0.0.1", 12345);
}
catch(SocketException se)
{
//Ignore the "A non-blocking socket operation could not be completed immediately" error
if (se.ErrorCode != 10035)
throw;
}
//Check the connection status of the socket.
var writeCheck = new List<Socket>() { socket };
var errorCheck = new List<Socket>() { socket };
Socket.Select(null, writeCheck, errorCheck, 0);
if (writeCheck.Contains(socket))
{
//Connection opened successfully.
}
else if (errorCheck.Contains(socket))
{
//Connection refused
}
else
{
//Connect still pending
}
I am working on a solution that uses web socket protocol to notify client (web browser) when some event happened on the server (MVC Core web app). I use Microsoft.AspNetCore.WebSockets nuget.
Here is my client-side code:
$(function () {
var socket = new WebSocket("ws://localhost:61019/data/openSocket");
socket.onopen = function () {
$(".socket-status").css("color", "green");
}
socket.onmessage = function (message) {
$("body").append(document.createTextNode(message.data));
}
socket.onclose = function () {
$(".socket-status").css("color", "red");
}
});
When this view is loaded the socket request is immediately sent to the MVC Core application. Here is the controller action:
[Route("data")]
public class DataController : Controller
{
[Route("openSocket")]
[HttpGet]
public ActionResult OpenSocket()
{
if (HttpContext.WebSockets.IsWebSocketRequest)
{
WebSocket socket = HttpContext.WebSockets.AcceptWebSocketAsync().Result;
if (socket != null && socket.State == WebSocketState.Open)
{
while (!HttpContext.RequestAborted.IsCancellationRequested)
{
var response = string.Format("Hello! Time {0}", System.DateTime.Now.ToString());
var bytes = System.Text.Encoding.UTF8.GetBytes(response);
Task.Run(() => socket.SendAsync(new System.ArraySegment<byte>(bytes),
WebSocketMessageType.Text, true, CancellationToken.None));
Thread.Sleep(3000);
}
}
}
return new StatusCodeResult(101);
}
}
This code works very well. WebSocket here is used exclusively for sending and doesn't receive anything. The problem, however, is that the while loop keeps holding the DataController thread until cancellation request is detected.
Web socket here is bound to the HttpContext object. As soon as HttpContext for the web request is destroyed the socket connection is immediately closed.
Question 1: Is there any way that socket can be preserved outside of the controller thread?
I tried putting it into a singleton that lives in the MVC Core Startup class that is running on the main application thread. Is there any way to keep the socket open or establish connection again from within the main application thread rather than keep holding the controller thread with a while loop?
Even if it is deemed to be OK to hold up controller thread for socket connection to remain open, I cannot think of any good code to put inside the OpenSocket's while loop. What do you think about having a manual reset event in the controller and wait for it to be set inside the while loop within OpenSocket action?
Question 2: If it is not possible to separate HttpContext and WebSocket objects in MVC, what other alternative technologies or development patterns can be utilized to achieve socket connection reuse? If anyone thinks that SignalR or a similar library has some code allowing to have socket independent from HttpContext, please share some example code. If someone thinks there is a better alternative to MVC for this particular scenario, please provide an example, I do not mind switching to pure ASP.NET or Web API, if MVC does not have capabilities to handle independent socket communication.
Question 3: The requirement is to keep socket connection alive or be able to reconnect until explicit timeout or cancel request by the user. The idea is that some independent event happens on the server that triggers established socket to send data.
If you think that some technology other than web sockets would be more useful for this scenario (like HTML/2 or streaming), could you please describe the pattern and frameworks you would use?
P.S. Possible solution would be to send AJAX requests every second to ask if there was new data on the server. This is the last resort.
After lengthy research I ended up going with a custom middleware solution. Here is my middleware class:
public class SocketMiddleware
{
private static ConcurrentDictionary<string, SocketMiddleware> _activeConnections = new ConcurrentDictionary<string, SocketMiddleware>();
private string _packet;
private ManualResetEvent _send = new ManualResetEvent(false);
private ManualResetEvent _exit = new ManualResetEvent(false);
private readonly RequestDelegate _next;
public SocketMiddleware(RequestDelegate next)
{
_next = next;
}
public void Send(string data)
{
_packet = data;
_send.Set();
}
public async Task Invoke(HttpContext context)
{
if (context.WebSockets.IsWebSocketRequest)
{
string connectionName = context.Request.Query["connectionName"]);
if (!_activeConnections.Any(ac => ac.Key == connectionName))
{
WebSocket socket = await context.WebSockets.AcceptWebSocketAsync();
if (socket == null || socket.State != WebSocketState.Open)
{
await _next.Invoke(context);
return;
}
Thread sender = new Thread(() => StartSending(socket));
sender.Start();
if (!_activeConnections.TryAdd(connectionName, this))
{
_exit.Set();
await _next.Invoke(context);
return;
}
while (true)
{
WebSocketReceiveResult result = socket.ReceiveAsync(new ArraySegment<byte>(new byte[1]), CancellationToken.None).Result;
if (result.CloseStatus.HasValue)
{
_exit.Set();
break;
}
}
SocketHandler dummy;
_activeConnections.TryRemove(key, out dummy);
}
}
await _next.Invoke(context);
string data = context.Items["Data"] as string;
if (!string.IsNullOrEmpty(data))
{
string name = context.Items["ConnectionName"] as string;
SocketMiddleware connection = _activeConnections.Where(ac => ac.Key == name)?.Single().Value;
if (connection != null)
{
connection.Send(data);
}
}
}
private void StartSending(WebSocket socket)
{
WaitHandle[] events = new WaitHandle[] { _send, _exit };
while (true)
{
if (WaitHandle.WaitAny(events) == 1)
{
break;
}
if (!string.IsNullOrEmpty(_packet))
{
SendPacket(socket, _packet);
}
_send.Reset();
}
}
private void SendPacket(WebSocket socket, string packet)
{
byte[] buffer = Encoding.UTF8.GetBytes(packet);
ArraySegment<byte> segment = new ArraySegment<byte>(buffer);
Task.Run(() => socket.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None));
}
}
This middleware is going to run on every request. When Invoke is called it checks if it is a web socket request. If it is, the middleware checks if such connection was already opened and if it wasn't, the handshake is accepted and the middleware adds it to the dictionary of connections. It's important that the dictionary is static so that it is created only once during application lifetime.
Now if we stop here and move up the pipeline, HttpContext will eventually get destroyed and, since the socket is not properly encapsulated, it will be closed too. So we must keep the middleware thread running. It is done by asking socket to receive some data.
You may ask why we need to receive anything if the requirement is just to send? The answer is that it is the only way to reliably detect client disconnecting. HttpContext.RequestAborted.IsCancellationRequested works only if you constantly send within the while loop. If you need to wait for some server event on a WaitHandle, cancellation flag is never true. I tried to wait for HttpContext.RequestAborted.WaitHandle as my exit event, but it is never set either. So we ask socket to receive something and if that something sets CloseStatus.HasValue to true, we know that client disconnected. If we receive something else (client side code is unsafe) we will ignore it and start receiving again.
Sending is done in a separate thread. The reason is the same, it's not possible to detect disconnection if we wait on the main middleware thread. To notify the sender thread that client disconnected we use _exit synchronization variable. Remember, it is fine to have private members here since SocketMiddleware instances are saved in a static container.
Now, how do we actually send anything with this set up? Let's say an event occurs on the server and some data becomes available. For simplicity sake, lets assume this data arrives inside normal http request to some controller action. SocketMiddleware will run for every request, but since it is not web socket request, _next.Invoke(context) is called and the request reaches controller action which may look something like this:
[Route("ProvideData")]
[HttpGet]
public ActionResult ProvideData(string data, string connectionName)
{
if (!string.IsNullOrEmpty(data) && !string.IsNullOrEmpty(connectionName))
{
HttpContext.Items.Add("ConnectionName", connectionName);
HttpContext.Items.Add("Data", data);
}
return Ok();
}
Controller populates Items collection which is used to share data between components. Then the pipeline returns to the SocketMiddleware again where we check whether there is anything interesting inside the context.Items. If there is we select respective connection from the dictionary and call its Send() method that sets data string and sets _send event and allows single run of the while loop inside the sender thread.
And voila, we a have socket connection that sends on server side event. This example is very primitive and is there just to illustrate the concept. Of course, to use this middleware you will need to add the following lines in your Startup class before you add MVC:
app.UseWebSockets();
app.UseMiddleware<SocketMiddleware>();
Code is very strange and hopefully we'll be able to write something much nicer when SignalR for dotnetcore is finally out. Hopefully this example will be useful for someone. Comments and suggestions are welcome.
Here's something very weird I had noticed.
I'm writing a CRM 2011 Silverlight extension and, well, all is fine on my local development instance. The application uses OData to communicate, and uses System.Threading.Tasks.Task a lot to perform all the operations in the background (FromAsync is a blessing).
However, I decided to test my application in CRM 2011 Online and found, to my surprise, that it would no longer work; I would receive a Security Exception when ending retrieve tasks.
Using Fiddler, I found that CRM is trying to redirect me to the Live login page, which didn't make much sense, considering I was already logged in.
After some more attempts, I found that the errors were because I was accessing the service from a different thread than the UI thread.
Here's a quick example:
//this will work
private void button1_Click(object sender, RoutedEventArgs e)
{
var query = ctx.AccountSet;
query.BeginExecute((result) =>
{
textBox1.Text = query.EndExecute(result).First().Name;
}, null);
}
//this will fail
private void button2_Click(object sender, RoutedEventArgs e)
{
System.Threading.Tasks.Task.Factory.StartNew(RestAsync);
}
void RestAsync()
{
var query = ctx.AccountSet;
var async = query.BeginExecute(null, null);
var task = System.Threading.Tasks.Task.Factory.FromAsync<Account>(async, (result) =>
{
return query.EndExecute(result).First(); // <- Exception thrown here
});
textBox1.Dispatcher.BeginInvoke(() =>
{
textBox1.Text = task.Result.Name;
});
}
It seems almost obvious that I'm missing some fundamentals on how threads use permissions. Since using a separate thread is preferable in my case, is there any way to "copy" the permissions / authentication? Perhaps some sort of impersonation?
EDIT: In case anyone else is struggling with this, using other threads (or Task, as the case may be) is possible as long as query.BeginExecute(null, null); is executed on the UI thread. You need a way to retrieve the returned IAsyncResult back to the calling thread, but you can do that using a ManualResetEvent.
But I'd still like to know why the darned permissions / authentication isn't shared between the threads...
I am not quite sure, is this will help. But I found a description from by Jeffrey Richter page 770
"Like console applications, ASP.NET Web Form and XML Web Service applications allow
any thread to do whatever it wants. When a thread pool thread starts to process a client’s
request, it can assume the client’s culture (System.Globalization.CultureInfo), allowing
the Web server to return culture-specific formatting for numbers, dates, and times.5 In
addition, the Web server can assume the client’s identity (System.Security.Principal.
IPrincipal) so that the server can access only the resources that the client is allowed to
access. When a thread pool thread spawns an asynchronous operation, it will be completed
by another thread pool thread, which will be processing the result of an asynchronous operation.
While this work is being performed on behalf of the original client request, the culture
and identity information doesn’t flow to the new thread pool thread by default so any
additional work done on behalf of the client is now not using the client’s culture and identity
information. Ideally, we want the culture and identity information to flow to the other thread
pool threads that are still doing work on behalf of the same client."
And here is his example, I hope this will help.
private static AsyncCallback SyncContextCallback(AsyncCallback callback)
{
SynchronizationContext sc = SynchronizationContext.Current;
// If there is no SC, just return what was passed in
if (sc == null) return callback;
// Return a delegate that, when invoked, posts to the captured SC a method that
// calls the original AsyncCallback passing it the IAsyncResult argument
return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
}
protected override void OnMouseClick(MouseEventArgs e) {
// The GUI thread initiates the asynchronous Web request
Text = "Web request initiated";
var webRequest = WebRequest.Create("http://Wintellect.com/");
webRequest.BeginGetResponse(SyncContextCallback(ProcessWebResponse), webRequest);
base.OnMouseClick(e);
}
private void ProcessWebResponse(IAsyncResult result) {
// If we get here, this must be the GUI thread, it's OK to update the UI
var webRequest = (WebRequest)result.AsyncState;
using (var webResponse = webRequest.EndGetResponse(result)) {
Text = "Content length: " + webResponse.ContentLength;
}
}
And here is what I am using in my application
public override void UpdateCanvas(object parameter)
{
Action<GraphPane> startToUpdate = StartToUpdate;
GraphPane selectedPane = Canvas.HostingPane.PaneList.Find(p => p.Title.Text.Equals(defaultPanTitle));
startToUpdate.BeginInvoke(selectedPane, FormSyncContext.SyncContextCallback(RefreshCanvas), selectedPane);
}
public static AsyncCallback SyncContextCallback(AsyncCallback callback)
{
// Capture the calling thread's SynchronizationContext-derived object
SynchronizationContext sc = SynchronizationContext.Current;
// If there is no SC, just return what was passed in
if (sc == null) return callback;
// Return a delegate that, when invoked, posts to the captured SC a method that
// calls the original AsyncCallback passing it the IAsyncResult argument
return asyncResult => sc.Post(result => callback((IAsyncResult)result), asyncResult);
}
I have C# multithreaded application that has to interface with a hardware using SerialPort.
The program is mostly command response sequence but the hardware can send an unsolicited "RESET" message due to an internal error at which time the software has to reinitialize it by sending a sequence of commands setting certain values.
More than one thread (from threadpool) can try to do a TakeSampleNow()
public class ALComm
{
private readonly AutoLoaderManager _manager;
private readonly AutoResetEvent dataArrived = new AutoResetEvent(false);
private SerialPort _alPort;
private string _alResponse;
.... Code to init _alPort and attach datareceived event etc
public void TakeSampleNow()
{
if (Monitor.TryEnter(_alPort, 1000)) //let's wait a second
{
_manager.MessageList.Enqueue("Try sampling");
try
{
Send("Command1");
string response = Receive();
switch(response)
{
case "X": blah blah..
case "Y": blah blah..
}
Send("Command2");
string response = Receive();
while(response != "OK")
{
Send("Command3");
string response = Receive();
Send("Command2");
string response = Receive();
}
}
finally
{
Console.WriteLine("Releasing port");
//Thread.CurrentThread.Priority = ThreadPriority.Normal;
Monitor.Exit(_alPort);
}
else
{
_manager.MessageList.Enqueue("Port is busy!!!");
}
}
public string Receive()
{
string inString = null;
dataArrived.WaitOne(1000);
inString = _alResponse;
return inString;
}
private void AlPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
_alResponse = _alPort.ReadLine();
//if (_alResponse.ToUpper().Contains("RESET"))
//{
// _alState = AlState.Reset;
// TryInitialize();
//}
dataArrived.Set();
}
private void TryInitialize()
{
Monitor.Enter(_alPort); //lock so other threads do not access samplenow during initialization
try
{
string response;
Console.WriteLine("Initializing ... The AutoLoader");
_alPort.DiscardInBuffer();
Send("CommandX");
response = Receive();
--- blah blah
_alResponse = "";
}
finally
{
Monitor.Exit(_alPort);
}
}
I can check the response in the datareceived event and wait on lock in TryInitialize() and for other threads in TakeSampleNow to release the lock, I would have to check on each response if _alResponse contains "RESET" and if it does return from the method. Which makes it more convoluted.
Any suggestions of how I can do this better. I believe it can be a state machine but am not able to conceptualize it.
You don't supply much details of your protocol - you don't say if command/rsponse pairs can overlap and, if so, how the responses are matched up with the commands.
You should be able to do this with a state-engine. Run the state-machine with its own thread that waits on a BlockingCollection for events. You will need a 'SerialRecv' thread as well to run your protocol and parse incoming bytes into messages.
I would use just one 'SerialEvent' class to carry events into the SM queue. The class should have an enum to describe the event and members for rx buffer, txData, parsed data, data to assemble tx string from, an exception/errorMess field - everything needed for any event or and forward purpose, (eg. the SM might forward a completed Request/Response to a display or logger).
Some events I can think of straightaway: EsmNewRequestResponse,EsmRxData,EsmResetRx
The event enum may, as some stages, have other values that are not used by the SM, eg: EsmError,EsmLog,EsmDisplay.
If you need timeouts, you can generate one by timing out the take() on the SM input queue.
Yes, there are things I left out.
If several threads issue SerialEvent instances 'at once', the SM will get new SerialEvents while it is still processing the first one. The SM will need another queue/deque to hold the SerialEvents awaiting handling. Due to the serializing of the SM by the BlockingCollection/thread, this 'pending' queue does not have to be thread-safe. The SM should check this pending queue after any request/response has completed to see if there is another one to process.
To handle request/response synchronously from several threads, the requesting threads must have smething to wait on. An AutoResetEvent in the SerialEvent class would do. Submitting a SerialEvent to the system would queue up the SerialEvent instance and wait on the AutoResetEvent. When the processing of the instance is complete, (ie. response received, error or timeout), the SM would set the event and the originating thread would run on with its SerialEvent instance filled in with data.
Next - the SerialEvent class is getting towards the point where it may be better to pool them rather than continually create/CG. That would need another BlockingCollection to act as the pool.
You don't want to have multiple threads trying to read your serial port. You should have a single thread that does nothing but read the port. When it gets some data, it puts a message in a queue or similar data structure that can be processed by the multiple sample threads. This way your single reader thread can reliably find and react to the RESET messages.
As title implies.
Yes, i know it's horribad to use .abort() but hear me out
I'm using 2 threads, The main thread (of my app) and a socket listen thread (familiar sound anyone?)
Instead of using asynchronous .AcceptAsync() calls (tbh, main reason is that i haven't looked too much into them), I have the thread just hang on socket.Accept();
Ofcourse, when i call thread.Abort(), the thread doesn't close because it's still waiting for a connection, once it passes Accept() it'll abort just fine.
Code:
void listenserver()
{
while (run)
{
fConsole.WriteLine("Waiting for connections..");
connectedsock = mainsock.Accept();
connected = true;
fConsole.WriteLine("Got connection from: " + connectedsock.RemoteEndPoint);
...
and elsewhere:
private void button_start_Click(object sender, EventArgs e)
{
if (!run)
{ //code ommitted.
}
else
{
run = false;
listenthread.Join(3000);
if (listenthread.IsAlive)
{
fConsole.WriteLine("Force-closing rogue listen thread");
listenthread.Abort();
}
button_start.Text = "Start";
groupBox_settings.Enabled = true;
}
is there any way of assuring the thread will end, not being to stuff the whole thing in a seperate app and then ending that?
Note that i DO have thread.IsBackground set to true (as suggested in other forum threads), it doesn't make any difference though.
Since you're already using a thread, you might as well just use BeginAccept. Async code doesn't need to complicate your code, since you can use lambdas like this:
var socket = new Socket(...);
socket.BeginAccept(result =>
{
if (this.abort)
{
// We should probably use a signal, but either way, this is where we abort.
return;
}
socket.EndAccept(result);
// Do your sockety stuff
}, null);
Even with a separate method definition for the AsyncCallback, the code isn't complex.
By doing async IO you're also being a lot more efficient with the CPU time, since between the call to BeginAccept and EndAccept, the thread can be reused for other processing. Since you're not using it for anything purposeful while waiting for the connection, holding up a thread is pretty meaningless and inefficient.