停止 TcpListener 的正确方法 [英] Proper way to stop TcpListener

查看:74
本文介绍了停止 TcpListener 的正确方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在使用 TcpListener 来处理传入连接,每个连接都有一个线程来处理通信,然后关闭该单个连接.代码如下:

I am currently using TcpListener to address incoming connections, each of which are given a thread for handling the communication and then shutdown that single connection. Code looks as follows:

TcpListener listener = new TcpListener(IPAddress.Any, Port);
System.Console.WriteLine("Server Initialized, listening for incoming connections");
listener.Start();
while (listen)
{
     // Step 0: Client connection
     TcpClient client = listener.AcceptTcpClient();
     Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
     clientThread.Start(client.GetStream());
     client.Close();
}

listen 变量是一个布尔值,它是类中的一个字段.现在,当程序关闭时,我希望它停止监听客户端.将 listen 设置为 false 将阻止它接受更多连接,但由于 AcceptTcpClient 是一个阻塞调用,它至少会接受下一个客户端,然后退出.有没有办法强迫它在当时和那里简单地爆发和停止?当另一个阻塞调用正在运行时,调用 listener.Stop() 有什么影响?

The listen variable is a boolean that is a field on the class. Now, when the program shuts down I want it to stop listening for clients. Setting listen to false will prevent it from taking on more connections, but since AcceptTcpClient is a blocking call, it will at minimum take the next client and THEN exit. Is there any way to force it to simply break out and stop, right then and there? What effect does calling listener.Stop() have while the other blocking call is running?

推荐答案

根据代码和我推测的设计,您可以使用以下两个快速修复:

These are two quick fixes you can use, given the code and what I presume is your design:

如果你已经从另一个线程启动了这个 TcpListener 线程,你可以简单地在线程上调用 Abort(),这将导致 ThreadAbortException在阻塞调用中并向上走栈.

If you have started this TcpListener thread from another, you can simply call Abort() on the thread, which will cause a ThreadAbortException within the blocking call and walk up the stack.

第二个低成本解决方法是使用 listener.Pending() 方法来实现轮询模型.然后使用 Thread.Sleep() 等待,然后查看是否有新连接挂起.一旦你有一个挂起的连接,你调用 AcceptTcpClient() 并释放挂起的连接.代码看起来像这样:

The second low cost fix is to use the listener.Pending() method to implement a polling model. You then use a Thread.Sleep() to wait before seeing if a new connection is pending. Once you have a pending connection, you call AcceptTcpClient() and that releases the pending connection. The code would look something like this:

while (listen) {
     // Step 0: Client connection
     if (!listener.Pending()) {
          Thread.Sleep(500); // choose a number (in milliseconds) that makes sense
          continue; // skip to next iteration of loop
     }
     TcpClient client = listener.AcceptTcpClient();
     Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
     clientThread.Start(client.GetStream());
     client.Close();
}

异步重写

但是,您确实应该为您的应用程序转向非阻塞方法.在幕后,框架将使用重叠的 I/O 和 I/O 完成端口来实现来自异步调用的非阻塞 I/O.这也不是特别难,只是需要以不同的方式思考您的代码.

Asynchronous Rewrite

However, you should really move to a non-blocking methodology for your application. Under the covers the framework will use overlapped I/O and I/O completion ports to implement non-blocking I/O from your asynchronous calls. It's not terribly difficult either, it just requires thinking about your code a little differently.

基本上,您将使用 BeginAcceptTcpClient() 方法开始您的代码,并跟踪您返回的 IAsyncResult.您指向一个方法,该方法负责获取 TcpClient 并将其从 NOT 传递给新线程,而是传递给 ThreadPool.QueueUserWorkerItem,因此您不会为每个客户端请求启动并关闭一个新线程(注意:如果您有特别长的请求,您可能需要使用自己的线程池,因为线程池是共享的,并且如果您独占所有系统实现的应用程序其他部分的线程可能会被饿死).一旦侦听器方法启动新的 TcpClient 到它自己的 ThreadPool 请求,它会再次调用 BeginAcceptTcpClient() 并将委托指向自身.

Basically you would start your code with the BeginAcceptTcpClient() method and keep track of the IAsyncResult that you are returned. You point that at a method whose responsible for getting the TcpClient and passing it off NOT to a new thread but to a thread off of the ThreadPool.QueueUserWorkerItem, so you're not spinning up and closing a new thread for each client request (Note: you may need to use your own thread pool if you have particularly long lived requests, because the thread pool is shared and if you monopolize all the threads other parts of your application implemented by the system may be starved). Once the listener method has kicked off your new TcpClient to its own ThreadPool request, it calls BeginAcceptTcpClient() again and points the delegate back at itself.

实际上,您只是将当前方法分解为 3 个不同的方法,然后由各个部分调用:

Effectively you're just breaking up your current method into 3 different methods that will then get called by the various parts:

  1. 引导一切;
  2. 成为调用EndAcceptTcpClient()的目标,启动TcpClient到它自己的线程,然后再次调用自己;
  3. 处理客户端请求并在完成后关闭它.
  1. to bootstrap everything;
  2. to be the target to call EndAcceptTcpClient(), kick off the TcpClient to it's own thread and then call itself again;
  3. to process the client request and close it when finished.

(注意:您应该将 TcpClient 调用包含在 using(){} 块中,以确保 TcpClient.Dispose()TcpClient.Close() 方法即使在发生异常时也会被调用.或者,您可以将其放在 finally 块中>try {} finally {} 块.)

(Note: you should enclose your TcpClient call in a using(){} block to ensure that TcpClient.Dispose() or TcpClient.Close() methods are called even in the event of an exception. Alternately you can put this in the finally block of a try {} finally {} block.)

这篇关于停止 TcpListener 的正确方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆