views:

305

answers:

2

I've been experimenting the UDP sending and receiving in C# and have a strange issue. The code works fine in a console app, but the client.Receive method is blocked when I try to use the exact same code in a Service. The Service runs normally and doesn't abort, and I have logging writing to a text file, so I know it gets to the Receive. Anyone have any ideas? Code below...

public partial class Service1 : ServiceBase
{
    private bool serviceStarted = false;
    Thread listenerThread;

    public Service1()
    {
        InitializeComponent();      
    }

    protected override void OnStart(string[] args)
    {
        serviceStarted = false;
        WriteLog("UDPListener Service Starting");

        ThreadStart thread = new ThreadStart(StartListening);
        listenerThread = new Thread(thread);

        serviceStarted = true;
        listenerThread.Start();
    }

    protected override void OnStop()
    {
        WriteLog("UDPListener Service Stopping");

        serviceStarted = false;
        listenerThread.Join(new TimeSpan(0, 0, 5));
    }

    private void StartListening()
    {
        WriteLog("Worker thread spawned.");

        UdpClient client = new UdpClient(40000);

        while (serviceStarted)
        {
            WriteLog("Service is started.  Getting endpoint.");
            IPEndPoint remoteIPEndPoint = new IPEndPoint(IPAddress.Any, 40000);
            WriteLog("Thread is listening...");

            byte[] content = client.Receive(ref remoteIPEndPoint);
            WriteLog("Receive unblocked.");

            if (content.Length > 0)
            {
                string message = Encoding.ASCII.GetString(content);
                WriteLog("UDPListener Message = " + message);
            }
        }
        Thread.CurrentThread.Abort();
    }

    private void WriteLog(string strMessage)
    {
        FileStream filestream = new FileStream(@"c:\temp\UDPClientLog.txt", 
                                       FileMode.OpenOrCreate, 
                                       FileAccess.Write);
        StreamWriter streamwriter = new StreamWriter(filestream);
        streamwriter.BaseStream.Seek(0, SeekOrigin.End);

        streamwriter.WriteLine(DateTime.Now.ToLongDateString() +
            " at " +
            DateTime.Now.ToLongTimeString() +
            ": " +
            strMessage +
            "\n");

        streamwriter.Flush();
        streamwriter.Close();
    }
}
A: 

That's because UdpClient.Receive is blocking (i.e. synchronous) operation, and thread would be blocked until you receive data:

The Receive method will block until a datagram arrives from a remote host. When data is available, the Receive method will read the first enqueued datagram and return the data portion as a byte array. This method populates the remoteEP parameter with the IPAddress and port number of the sender.

If you want nonblocking behaviour you can use asynchronous version: UdpClient.BeginReceive method.

Sergey Teplyakov
Sorry, should clarify. I know Receive should be blocking, but it never receives the message from the sender. This only happens in the Service, the same code in a Console App receives the message correctly.
Nate
Hm.. It strange, because I use iwindows services and UDP sockets and all works fine. Are you sure that you receive data? Some tool like wireshark can prove it...
Sergey Teplyakov
I'm pretty sure it's not receiving data, or I would think the Receive method would return. The thread is spawning and the endpoint is established correctly (both logging entries show up). Everything looks fine - the service is started and no errors in the event viewer - but it just sits there waiting for a message that it never gets. It should work, since the console performs the exact same actions (creates a new thread, etc) and it gets the message just fine. I'm stumped.
Nate
One more advise: you can create separate class that do all stuff and test it in console app. Than you can use the same class (not similar logic) in windows service. It also should work. Maybe there are some security-related problems?
Sergey Teplyakov
Check what account the service is running under. Test it with your admin account.
Steven Sudit
Yep, I've been installing the service with InstallUtil using my admin account. One thing I forgot to mention, though I can't imagine why it would matter... the OS is Windows 7 64 bit. I'm registering the service using the 64 bit .NET framework version of InstallUtil. Thanks for the tip Sergey, I'll try making the code into a class and try that.
Nate
Really strange. Just put all the listener code into a class library and call it from both the console and service. Same result, the console app receives the message, the service doesn't.
Nate
Please, post all your code. I try it on my machine.
Sergey Teplyakov
I posted the answer above, it looks like the Service doesn't allow Windows to prompt to unblock the firewall. If I turn the firewall off, it works fine. I'm a little surprised there is no event logged or any other indication of a problem, but either way, it looks like a firewall problem. Thanks for your suggestions Sergey. By the way, the code is listed in the original post. I had to tweak it a little to put it in a class library, but nothing major.
Nate
+1  A: 

Finally figured this out. Windows Firewall was blocking the incoming connection and apparently the service isn't allowed to interactively ask for permission to unblock it.

Nate