天道酬勤,学无止境

停止TcpListener的正确方法(Proper way to stop TcpListener)

问题

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

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()有什么作用?

回答1

给定代码和我认为是您的设计,这是您可以使用的两个快速修复程序:

1. Thread.Abort()

如果您是从另一个线程启动此TcpListener线程的,则只需在该线程上调用Abort() ,这将在阻塞调用内导致ThreadAbortException并向上移动堆栈。

2. TcpListener.Pending()

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

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。 这也不是那么困难,它只需要对代码有一点不同的思考即可。

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

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

  1. 引导一切;
  2. 作为调用EndAcceptTcpClient()的目标,将TcpClient到其自己的线程,然后再次调用自身;
  3. 处理客户端请求并在完成后将其关闭。

:你应该附上您TcpClient电话在using(){}块,确保TcpClient.Dispose()TcpClient.Close()方法调用,即使在异常的情况下,或者你可以把这个在。 finally try {} finally {}阻止。)

回答2

来自另一个线程的listener.Server.Close()中断了阻塞调用。

A blocking operation was interrupted by a call to WSACancelBlockingCall
回答3

不要使用循环。 而是,不循环调用BeginAcceptTcpClient()。 如果仍然设置了侦听标志,则在回调中只需发出另一个对BeginAcceptTcpClient()的调用即可。

要停止侦听器,因为您尚未阻止,所以您的代码只能在其上调用Close()。

回答4

套接字提供了强大的异步功能。 看看使用异步服务器套接字

这是有关代码的几个注释。

在这种情况下,使用手动创建的线程可能会产生开销。

下面的代码受竞争条件的影响-TcpClient.Close()关闭您通过TcpClient.GetStream()获得的网络流。 考虑关闭客户,您可以肯定地说不再需要该客户。

 clientThread.Start(client.GetStream());
 client.Close();

TcpClient.Stop()关闭基础套接字。 TcpCliet.AcceptTcpClient()在基础套接字上使用Socket.Accept()方法,该方法将在关闭后抛出SocketException。 您可以从其他线程调用它。

无论如何,我建议使用异步套接字。

回答5

在这里查看我的答案https://stackoverflow.com/a/17816763/2548170 TcpListener.Pending()不是一个好的解决方案

回答6

只是为了增加使用异步方法的更多理由,我很确定Thread.Abort将无法工作,因为该调用在OS级别的TCP堆栈中被阻止。

另外...如果您在回调中调用BeginAcceptTCPClient来侦听除第一个连接外的每个连接,请注意确保执行初始BeginAccept的线程不会终止,否则框架将自动丢弃该侦听器。 我想这是一个功能,但实际上这很烦人。 在桌面应用程序中,这通常不是问题,但是在Web上,您可能要使用线程池,因为这些线程永远不会真正终止。

回答7

上面已经提到,改为使用BeginAcceptTcpClient,异步管理要容易得多。

这是一些示例代码:

        ServerSocket = new TcpListener(endpoint);
        try
        {
            ServerSocket.Start();
            ServerSocket.BeginAcceptTcpClient(OnClientConnect, null);
            ServerStarted = true;

            Console.WriteLine("Server has successfully started.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Server was unable to start : {ex.Message}");
            return false;
        }
回答8

最好使用异步BeginAcceptTcpClient函数。 然后,您可以在侦听器上调用Stop(),因为它不会被阻塞。

回答9

一些更改使Peter Oehlert变得完美。 因为在500毫秒之前,听众再次冒充。 要更正此问题,请执行以下操作:

    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
       }
       else // Enter here only if have pending clients
       {
          TcpClient client = listener.AcceptTcpClient();
          Thread clientThread = new Thread(new ParameterizedThreadStart(HandleConnection));
          clientThread.Start(client.GetStream());
          client.Close();
       }
   }

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 取消阻止AcceptTcpClient调用(Cancel blocking AcceptTcpClient call)
    问题 众所周知,在C#中接受传入TCP连接的最简单方法是在TcpListener.AcceptTcpClient()上循环。 另外,这种方式将阻塞代码执行,直到获得连接为止。 这是对GUI的极端限制,因此我想在单独的线程或任务中侦听连接。 有人告诉我,线程有几个缺点,但是没有人向我解释这些缺点。 因此,我没有使用线程,而是使用任务。 这很好用,但是由于AcceptTcpClient方法阻止了执行,因此我找不到任何处理任务取消的方法。 当前代码看起来像这样,但是当我希望程序停止监听连接时,我不知道如何取消任务。 首先关闭任务中执行的功能: static void Listen () { // Create listener object TcpListener serverSocket = new TcpListener ( serverAddr, serverPort ); // Begin listening for connections while ( true ) { try { serverSocket.Start (); } catch ( SocketException ) { MessageBox.Show ( "Another server is currently listening at port " + serverPort ); } // Block
  • 取消不接受CancellationToken的异步操作的正确方法是什么?(What is the correct way to cancel an async operation that doesn't accept a CancellationToken?)
    问题 取消以下内容的正确方法是什么? var tcpListener = new TcpListener(connection); tcpListener.Start(); var client = await tcpListener.AcceptTcpClientAsync(); 简单地调用tcpListener.Stop()似乎会导致ObjectDisposedException ,并且AcceptTcpClientAsync方法不接受CancellationToken结构。 我是否完全缺少明显的东西? 回答1 假设您不想在TcpListener类上调用Stop方法,那么这里没有完美的解决方案。 如果您可以在某个时间段内未完成操作时收到通知,但允许原始操作完成,那么可以创建一个扩展方法,如下所示: public static async Task<T> WithWaitCancellation<T>( this Task<T> task, CancellationToken cancellationToken) { // The tasck completion source. var tcs = new TaskCompletionSource<bool>(); // Register with the cancellation token. using
  • Proper way to stop 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(); } The
  • 什么是ThreadPool服务器的异步/等待等效项?(What is the async/await equivalent of a ThreadPool server?)
    问题 我正在使用同步api和线程池在看起来像这样的tcp服务器上工作: TcpListener listener; void Serve(){ while(true){ var client = listener.AcceptTcpClient(); ThreadPool.QueueUserWorkItem(this.HandleConnection, client); //Or alternatively new Thread(HandleConnection).Start(client) } } 假设我的目标是使用最少的资源使用尽可能多的并发连接,这似乎很快就会受到可用线程数的限制。 我怀疑通过使用非阻塞任务api,我将能够用更少的资源来处理更多的事情。 我最初的印象是: async Task Serve(){ while(true){ var client = await listener.AcceptTcpClientAsync(); HandleConnectionAsync(client); //fire and forget? } } 但是令我震惊的是,这可能会导致瓶颈。 也许HandleConnectionAsync将花费很长的时间才能达到第一次等待,并且将阻止主接受循环继续进行。 这是否只会使用一个线程,还是运行时会在合适的情况下在多个线程上神奇地运行事物?
  • 在C#中,如何检查TCP端口是否可用?(In C#, how to check if a TCP port is available?)
    问题 在C#中使用TcpClient或通常连接到套接字,如何首先检查计算机上某个端口是否空闲? 更多信息:这是我使用的代码: TcpClient c; //I want to check here if port is free. c = new TcpClient(ip, port); 回答1 由于您使用的是TcpClient ,这意味着您正在检查打开的TCP端口。 System.Net.NetworkInformation命名空间中有很多可用的好对象。 使用IPGlobalProperties对象获取到的数组TcpConnectionInformation对象,然后你就可以诘约端点IP和端口。 int port = 456; //<--- This is your value bool isAvailable = true; // Evaluate current system tcp connections. This is the same information provided // by the netstat command line application, just in .Net strongly-typed object // form. We will look through the list, and if our port we would like
  • .net core 和 WPF 开发升讯威在线客服系统:使用 TCP协议 实现稳定的客服端
    本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程。本产品已经成熟稳定并投入商用。 请访问:https://kf.shengxunwei.com 文章目录列表请点击这里 对于在线客服与营销系统,客服端指的是后台提供服务的客服或营销人员,他们使用客服程序在后台观察网站的被访情况,开展营销活动或提供客户服务。在本篇文章中,我将详细介绍如何在 .net core 环境下使用 TCP 通信技术实现稳定高效与安全的客服端程序。 这里存在几个技术难点需要注意: 需要使客服端程序具备 24 小时不间断运行的能力,在处理网络通信时,必须100%的稳定。必须具备应对网络波动的能力,不能网络稍有波动就断线。即使出现了短暂的网络中断,客服程序也不能做掉线处理,而是要具备保持和自动重连的能力。要考虑安全性问题,服务端的端口监听,要能识别正常客服端连接,还是来自攻击者的连接。 访客端实现的效果: 访客端在手机上的效果: 后台客服的实现效果: 在服务端上通过 TcpListener 监听客服连接 TcpListener类提供了简单的方法,这些方法在阻止同步模式下侦听和接受传入连接请求。 可以使用 TcpClient 或 Socket 来连接 TcpListener 。 TcpListener使用 IPEndPoint 、本地 IP 地址和端口号或仅端口号创建一个。
  • 使用TcpListener取消NetworkStream.ReadAsync(Cancel NetworkStream.ReadAsync using TcpListener)
    问题 考虑下面的简化示例(准备在LinqPad中进行滚动,需要提升的帐户): void Main() { Go(); Thread.Sleep(100000); } async void Go() { TcpListener listener = new TcpListener(IPAddress.Any, 6666); try { cts.Token.Register(() => Console.WriteLine("Token was canceled")); listener.Start(); using(TcpClient client = await listener.AcceptTcpClientAsync() .ConfigureAwait(false)) using(var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5))) { var stream=client.GetStream(); var buffer=new byte[64]; try { var amtRead = await stream.ReadAsync(buffer, 0, buffer.Length, cts.Token); Console.WriteLine("finished"); } catch
  • TcpListener: how to stop listening while awaiting AcceptTcpClientAsync()?
    I don't know how to properly close a TcpListener while an async method await for incoming connections. I found this code on SO, here the code : public class Server { private TcpListener _Server; private bool _Active; public Server() { _Server = new TcpListener(IPAddress.Any, 5555); } public async void StartListening() { _Active = true; _Server.Start(); await AcceptConnections(); } public void StopListening() { _Active = false; _Server.Stop(); } private async Task AcceptConnections() { while (_Active) { var client = await _Server.AcceptTcpClientAsync(); DoStuffWithClient(client); } } private
  • TCP客户端\服务器-客户端并不总是读取(TCP client\server - client doesn't always read)
    问题 客户代码: TcpClient client = new TcpClient(); NetworkStream ns; private void Form1_Load(object sender, EventArgs e) { try { client.Connect("127.0.0.1", 560); ns = client.GetStream(); byte[] buffer = ReadFully(ns, client.Available); //working with the buffer... } catch { //displaying error... } } public static byte[] ReadFully(NetworkStream stream , int initialLength) { // If we've been passed an unhelpful initial length, just // use 32K. if (initialLength < 1) { initialLength = 32768; } byte[] buffer = new byte[initialLength]; long read = 0; int chunk; while ((chunk = stream.Read(buffer, (int
  • 如何设置TcpListener始终侦听并接受多个连接?(How to set up TcpListener to always listen and accept multiple connections?)
    问题 这是我的服务器应用程序: public static void Main() { try { IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); Console.WriteLine("Starting TCP listener..."); TcpListener listener = new TcpListener(ipAddress, 500); listener.Start(); while (true) { Console.WriteLine("Server is listening on " + listener.LocalEndpoint); Console.WriteLine("Waiting for a connection..."); Socket client = listener.AcceptSocket(); Console.WriteLine("Connection accepted."); Console.WriteLine("Reading data..."); byte[] data = new byte[100]; int size = client.Receive(data); Console.WriteLine("Recieved data: "); for (int i = 0; i <
  • C#检测TCP断开连接(c# detecting tcp disconnect)
    问题 我有两个简单的应用程序: 等待特定tcp端口等待客户端连接的Server应用程序。 然后听他说的话,发回一些反馈并断开该客户端的连接。 连接到服务器应用程序的Form应用程序,然后说一些话,然后等待反馈并与服务器断开连接,然后在表单中显示反馈。 尽管服务器应用程序似乎运行正常(我已经使用Telnet测试了它,并且看到了反馈,并且看到在反馈之后立即发生了断开连接),但是表单应用程序似乎并没有注意到与服务器的断开连接。 (即使服务器断开连接后,TcpClient.Connected似乎仍然是正确的) 我的问题是:为什么TcpClient.Connected保持真实,如何知道/何时断开服务器连接? 这是我的完整代码: 表格申请: using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Windows.Forms; using System.Threading; namespace Sender { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void sendButton_Click(object sender, EventArgs
  • What is the correct way to cancel an async operation that doesn't accept a CancellationToken?
    What is the correct way to cancel the following? var tcpListener = new TcpListener(connection); tcpListener.Start(); var client = await tcpListener.AcceptTcpClientAsync(); Simply calling tcpListener.Stop() seems to result in an ObjectDisposedException and the AcceptTcpClientAsync method doesn't accept a CancellationToken structure. Am I totally missing something obvious?
  • How to set up TcpListener to always listen and accept multiple connections?
    This is my Server App: public static void Main() { try { IPAddress ipAddress = IPAddress.Parse("127.0.0.1"); Console.WriteLine("Starting TCP listener..."); TcpListener listener = new TcpListener(ipAddress, 500); listener.Start(); while (true) { Console.WriteLine("Server is listening on " + listener.LocalEndpoint); Console.WriteLine("Waiting for a connection..."); Socket client = listener.AcceptSocket(); Console.WriteLine("Connection accepted."); Console.WriteLine("Reading data..."); byte[] data = new byte[100]; int size = client.Receive(data); Console.WriteLine("Recieved data: "); for (int i =
  • Cancel NetworkStream.ReadAsync using TcpListener
    Consider the following simplified example (ready to roll in LinqPad, elevated account required): void Main() { Go(); Thread.Sleep(100000); } async void Go() { TcpListener listener = new TcpListener(IPAddress.Any, 6666); try { cts.Token.Register(() => Console.WriteLine("Token was canceled")); listener.Start(); using(TcpClient client = await listener.AcceptTcpClientAsync() .ConfigureAwait(false)) using(var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5))) { var stream=client.GetStream(); var buffer=new byte[64]; try { var amtRead = await stream.ReadAsync(buffer, 0, buffer.Length, cts
  • TcpListener is queuing connections faster than I can clear them
    As I understand it, TcpListener will queue connections once you call Start(). Each time you call AcceptTcpClient (or BeginAcceptTcpClient), it will dequeue one item from the queue. If we load test our TcpListener app by sending 1,000 connections to it at once, the queue builds far faster than we can clear it, leading (eventually) to timeouts from the client because it didn't get a response because its connection was still in the queue. However, the server doesn't appear to be under much pressure, our app isn't consuming much CPU time and the other monitored resources on the machine aren't
  • how do i get TcpListener to accept multiple connections and work with each one individually?
    I have an SMTP listener that works well but is only able to receive one connection. My C# code is below and I am running it as a service. My goal is to have it runnign on a server and parsing multiple smtp messages sent to it. currently it parses the first message and stops working. how can I get it to accept the 2nd, 3rd, 4th... SMTP message and process it like it does the first? here is my code: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using System.IO; namespace SMTP_Listener { class Program { static
  • Sending Binary File TcpClient - File Is Larger Than Source
    To put my toe in the water of Network programming, I wrote a little Console App to send a png file to a server (another console app). The file being written by the server is slightly bigger than the source png file. And it will not open. The code for the client app is: private static void SendFile() { using (TcpClient tcpClient = new TcpClient("localhost", 6576)) { using (NetworkStream networkStream = tcpClient.GetStream()) { //FileStream fileStream = File.Open(@"E:\carry on baggage.PNG", FileMode.Open); byte[] dataToSend = File.ReadAllBytes(@"E:\carry on baggage.PNG"); networkStream.Write
  • Unity:实时视频流(Unity: Live Video Streaming)
    问题 我正在尝试将实时视频从一个应用程序流式传输到另一个应用程序,目前我有2个应用程序。 应用1是服务器/发送者,应用2是客户端/接收者。 在应用程序1中,我成功将视频字节发送到客户端。 在客户端,我也收到了所有字节。 我正在使用套接字和TCP。 我面临的问题是,当我收到视频字节并将其分配给“原始图像”纹理时,该纹理上的图像看起来放大得太多,因此像素化了。 更新的图片 这就是我流 这就是我在客户端上得到的。 这是第一期,但是我目前正在从台式机到另一台进行测试,我的目标是将IPAD流式传输到台式机,当我这样做时,它是如此之慢,并且会杀死ipad和台式机上的应用程序。 到目前为止,我尝试过一些故障排除。 1:我认为这是因为我有2种不同的分辨率,因为我是从ipad流到桌面 2:纹理图像太大,我将其输出并返回630。我尝试使用Unity Texture2D.resize调整其大小,但由于该函数将像素设置为未识别,因此我得到了灰色纹理 3:我使用了其他库来调整纹理的大小,但确实得到了我想要的东西,但是在12帧后,原始图像在视频和“?”之间开始闪烁。 纹理太多,然后冻结在两个应用程序(ipad和台式机)上 4:我相信我读取纹理的方式引起了问题,因为我同时使用了Setpixels和Getpixels函数,而且它们很重。 我的代码:服务器/发送方: using UnityEngine; using
  • Cancel blocking AcceptTcpClient call
    As everyone may already know, the simplest way to accept incoming TCP connections in C# is by looping over TcpListener.AcceptTcpClient(). Additionally this way will block code execution until a connection is obtained. This is extremely limiting to a GUI, so I want to listen for connections in either a seperate thread or task. I have been told, that threads have several disadvantages, however nobody explained me what these are. So instead of using threads, I used tasks. This works great, however since the AcceptTcpClient method is blocking execution, I can't find any way of handling a task
  • 使用异步/等待模式在C#5中编写高度可扩展的TCP / IP服务器?(Writing a highly scalable TCP/IP server in C# 5 with the async/await pattern?)
    问题 我的任务是设计一个必须接受来自多个客户端的连接的相当简单的TCP / IP服务器。 它需要用C#编写,而我使用的是.NET 4.5。 也就是说,我不确定.NET 4.5中TCP / IP服务器/客户端可伸缩性的当前“最新技术”是什么。 我确实看到了这篇文章:如何编写基于可伸缩Tcp / Ip的服务器。 但这与.NET 2.0和3.5有关,并且没有提及async / await模式。 我能够以“旧方式”编写服务器...但是我想知道“新方式”是什么。 在Socket,TcpClient或TcpListener上使用新的Async方法在C#中创建可伸缩服务器的最佳方法是什么? 新的异步方法是否利用I / O完成端口? 滚动自己的Socket侦听器是否更有效,或者TcpListener / TcpClient类现在还不错? 编辑:其他问题。 回答1 在Socket,TcpClient或TcpListener上使用新的Async方法在C#中创建可伸缩服务器的最佳方法是什么? Socket上没有任何新的async方法; Socket上名为*Async的方法是一组特殊的API,可以减少内存使用。 TcpClient和TcpListener确实获得了一些新的async方法。 如果要获得最佳的可伸缩性,则最好使用Stephen Toub的Socket自定义等待器。 如果您想要最简单的代码