Posted By: phreaks | Jul 19th @ 8:53 AM
page 1 of 1
Comments: 15 | Views: 432
I am developing a simple windows service that opens up a socket connection and persistantly listens on a port for some data from a remote server.

I basically have one method that does the work.

I want to be able to automatically reset the connection on a given interval because the remote service is flakey and sometimes stops sending data, at which point I need to reset the connection to resume receiving data.

Lets assume that my main method is called StartRequesting()

The problem is that since the actions performed in StartRequesting are blocking, the scm is not able to determine if the method actually started or not, so the status is listed as "starting" in the service manager.

I resolved that Issue by using ThreadStart to run the method on a new thread asynchronously.
However, that has presented me with a new round of challenges.

When I try to reset the service, by setting a flag (stopRequested = true), I am doing that from another thread, and it seems that the code either ignores it doesn't act on it for some random interval.

I instantiate this code from another class (effectively the service controller onStart)

Does anyting see anything inherintly wrong with what I am doing? Why doesn't the StopRequesting Call from the scm trigger the code path that it should until like 15 minutes after it's called? (and then it throws a thread was being aborted exception)

Any insight / tips are appreciatted.


public void StopRequesting()
{
    _stopRequested = true;
}




public static void StartRequesting()
{
using (Client = new TcpClient(Common.FeedHost, Common.FeedPort))
{
    using (NetworkStream Stream = Client.GetStream())
    {
        // Send authorization credentials
        Stream.Write(_credentials, 0, _credentials.Length);

        using (BinaryReader reader = new BinaryReader(Stream))
        {
            int i;
            try
            {
                while ( (i = reader.Read(_buffer, 0, _buffer.Length)) != 0)
                {
                    //Read data from buffer

                    data += ASCIIEncoding.UTF8.GetString(_buffer, 0, i);
                    _byteCount += _buffer.Length;

                    if ((_byteCount / 1024) >= Common.BufferSize || _stopRequested )
                    {
                        //flush buffer
                        
                        if (_stopRequested)
                        {
                            _data = String.Empty;
                            _byteCount = 0;
                            
                            if (Stream != null)
                                Stream.Close();
                    
                            if (Client != null)
                                Client.Close();

                            Common.ServiceStatus = Common.ServiceStatusEnum.Stopped;
                        }
                    }
                }
            }
        }
    }
}
}


===============================
// SCM 



public class MyServiceController
{

    private static Requestor Instance = new Requestor();

    protected override void OnStart(string[] args)
    {
        Thread thread = new Thread(new ThreadStart(Instance.StartRequesting));
        thread.Start();
    }


    protected override void OnStop()
    {
        Instance.GetType().GetMethod("StopRequesting").Invoke(Instance, null);
    }

}

Just quickly, before I analyse your code, why aren't you using BackgroundWorker? it handles the thread sync for calling back completion and handles doing pending cancellations etc.
Its often used for UI to keep the UI thread ready, but I think the technical design for it is to help with the problem you have above.. it wraps a thread and provides built in functionality for calling back the complete method on the right thread, and being able to push cancellations in from other threads, yadda yadda.
I'd be a bit careful about using BackgroundWorker in a service application. While it works its behavior is different from the one you get in a WinForms application, for example its ReportProgress notification will go on a thread from the thread pool rather than the main thread.

In any case, it still relies on the worker thread checking a value to see if "cancel" was request (CancellationPending property) and we're back to square one. The most likely reason why it takes a while for that thread to stop is NetworkStream.Read. Read is a blocking call, if there's no data available it just sits there waiting so your _stopRequested variable will only be checked when the server sends something.

A simple workaround would be to set a reasonable ReadTimeout, 100 ms for example.
A brutal aproach would be to either abort the worker thread or close the socket from the main thread.
A more complex solution would  be go async by using BeginRead instead of Read which avoid the blocking.

Guys, I have used BGW  in multiple service projects and it rocks!  works super sweet!!

in general what I do is this:

OnStart calls RunWOrkerAsync
OnStop / ReStart set the cancel flag of the BW
the DoWork gets a setup like this:

While(  !CancelFlag && !ExitFlag){
    Dosomething();
    Sleep(timeout);
}

where the sleep time needs to be less than 30 seconds to work with scm and respond in time.

the "dosomething()"  is the part that needs to be custom for each service and may test and exit fast if nothing needs to be done.
as you are doing tcp/ip you may want to also setup a thread pool also to do the actual netowork stuff.
keep the DoWOrk()  as a main controller and dispatcher

so for example it listens and when a new message / connection happens you spin up a new worker thread to handle it. that way the main worker / DoWOrk keeps ready to handle more dispatching of work.

 

 

Yes the read probably is blocking and causing the problem, but I wondered why you were recreating the wheel really..
Not sure If I am following this so let me know if I have it wrong.

you are needing to read some data from a remote server yes?

so then you should setup the request to be serviced on an async worker that when it completes will notfiy your serive that the request has been completed

your DoWOrk should kick off the worker and then go back to a sleep loop.

when it wakes up if it then needs to do something it does.

if your worker can process the data it does that before it dies if it's a one shot call.
then your service can get a stop command from scm and if it has to it can terminate the workers as needed.

you do not start a long runing / blocking task on your main service loop.

if you look at code for unix deamons for example you will see they do a set of fork and spawn commands to branch off into multiple thread / process /task depdneding on which version of UNIx / Linux/  BSD etc... they are.

sounds kind of sick but the parents sit around waiting for the children to die.

Ok you need a "watchdog" that can re-start the tcp connection, that should be ok.

and I would still spin off a worker to read the data, and fire some kind of "got data" event.

your DoWOrk can run the watchdog and if no datarecived in xx time then do a re-set and re-start of the tcp connection.

service control is handled the way I outlined before.

just that you get the read/write/ TCP/IO on a seperate thread .... Hmmm perhaps 2 bgw processes?? one for TCP and the other for SCM??

that way your TCP data can be read and processed asnyc from any SCM state.

but the SCM worker can tell the TCP worker to stop /restart/exit.

I do not have my TCP/Networking book handy at home to check on the details.

 

phreaks said:

From the comments of that blog post, it seems that it's a CLR 1.x problem. Plus there's a work-around by checking the CompletedSynchronously prop to avoid a stack overflow.

What framework are you on?