Background thread still listening on TCP port after service stopped

时间:2018-06-05 05:05:34

标签: c# windows service task-parallel-library tcplistener

I'm writing a C# service (.NET 4.0) which listens on a TCP port. I start the TcpListener on a background thread (using Task Parallel Library) so the service is not unresponsive to Windows. I also use TPL whenever a client connects, as each client will do some database work and I don't want to block other clients.

I install and uninstall the service using InstallUtil.exe on Windows Server 2012 R2 Standard. Whenever I stop the service and uninstall it, using netstat -abo I can see the port is still being listened to by the [System] process. It has a PID, however I can't see a process with this PID in Task Manager or tasklist, nor can I kill it with taskkill. It just says process not found, but it's always there when I run netstat -abo. If I try to start the service again using the same port, I get a socket exception:

Only one usage of each socket address (protocol/network address/port) is normally permitted
Stacktrace:
at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
at System.Net.Sockets.Socket.Bind(EndPoint localEP)
at System.Net.Sockets.TcpListener.Start(Int32 backlog)

I'm guessing my background threads are still hanging around after I stop the service, but I have no idea how to kill them now, and how to prevent this from happening. Here is what I hope is the relevant code (deleted exception handling and logging for easier reading):

public partial class MyService : ServiceBase
        {
            private static TCPServer server = null;

            protected override void OnStart(string[] args)
            {
                server = new TCPServer();
            }

            protected override void OnStop()
            {
                if (server != null)
                {
                    server.StopServer();
                }
            }
        }

    class TCPServer
        {
            public static TcpListener listener = null;
            private static Task listenerTask = null;
            private static List<Task> clientTasks = new List<Task>();

            public TCPServer()
            {
                    listenerTask = new Task(() => StartServer());
                    listenerTask.Start();
            }

            public void StopServer()
            {
                foreach(Task task in clientTasks)
                {
                    task.Dispose();
                }

                    listenerTask.Dispose();

                    if (listener != null)
                    {
                        listener.Stop();
                        listener = null;
                    }
            }

            private void StartServer()
            {
                    Int32 port = 51987;
                    IPAddress localAddr = GetLocalIP();

                    listener = new TcpListener(localAddr, port);
                    listener.Start();

                    while (listener != null)
                    {
                        if (listener.Pending())
                        {
                            TcpClient client = listener.AcceptTcpClient();

                            Task task = new Task((obj) => ProcessClient(obj), client);
                            task.Start();
                            clientTasks.Add(task);
                        }
                    }
            }

            private void ProcessClient(object obj)
            {
                    using (TcpClient client = obj as TcpClient)
                    {

                    Byte[] bytes = new Byte[2048];
                    String data = null;

                    NetworkStream stream = client.GetStream();
                    int i;

                    while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                    {
                        data = Encoding.ASCII.GetString(bytes, 0, i);
                    }

                    // do some stuff with data
                    // If an exception is thrown here, the rogue thread issue happens when I stop the service. 
                    // Otherwise, everything is good - I stop the service and no rogue thread, I can reuse the listener port. 
                    }
            }
        }

EDIT: Updated the code with suggested changes. I've also discovered that this rogue thread issue only seems to occur if an exception is thrown in one of my client threads. If everything runs ok, there is no rogue thread when I stop the service.

1 个答案:

答案 0 :(得分:0)

停止意味着停止当前连接并开始收听新连接,因此请确保正确关闭客户端,以便不创建新连接。

  

Stop方法也会关闭底层Socket,并创建一个新的   TcpListener的套接字。如果你在上面设置了任何属性   在调用Stop方法之前的底层Socket,那些属性   不会转移到新的Socket。

https://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.stop(v=vs.110).aspx#

此外,请确保将using与客户端

一起使用
using(TcpClient client = obj as TcpClient){//DO SOMETHING}