Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 阻止TcpListener的正确方法_C#_Sockets_Networking_Tcplistener - Fatal编程技术网

C# 阻止TcpListener的正确方法

C# 阻止TcpListener的正确方法,c#,sockets,networking,tcplistener,C#,Sockets,Networking,Tcplistener,我目前正在使用TcpListener来处理传入的连接,每个连接都有一个线程来处理通信,然后关闭单个连接。代码如下所示: TcpListener listener = new TcpListener(IPAddress.Any, Port); System.Console.WriteLine("Server Initialized, listening for incoming connections"); listener.Start(); while (listen) { // St

我目前正在使用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 to
false
将阻止它进行更多连接,但由于
AcceptTcpClient
是一个阻塞调用,它将至少占用下一个客户端,然后退出。有没有办法迫使它突然爆发并停止,就在此时此地?当另一个阻塞调用正在运行时,调用listener.Stop()会产生什么影响?

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

不要使用循环。相反,在不使用循环的情况下调用BeginAcceptTcpClient()。在回调中,如果仍然设置了listen标志,只需对BeginAcceptTcpClient()发出另一个调用


要停止侦听器,由于您没有阻止,您的代码可以对其调用Close()。

鉴于代码和我认为是您的设计,您可以使用以下两种快速修复方法:

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个不同的方法,然后由不同的部分调用:

  • 引导一切
  • 要成为调用
    EndAcceptTcpClient()
    的目标,启动
    TcpClient
    到它自己的线程,然后再次调用它自己
  • 处理客户端请求并在完成时关闭它

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

    套接字提供了强大的异步功能

    下面是关于代码的几个注释

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

    下面的代码是受RACE条件约束的——TCPclient .Couter()关闭TCP Client。GETStand()中的网络流。考虑关闭客户端,在这里你可以肯定地说它不再需要。

     clientThread.Start(client.GetStream());
     client.Close();
    
    TcpClient.Stop()关闭基础套接字。TcpCliet.AcceptTcpClient()在基础套接字上使用socket.Accept()方法,一旦关闭,该方法将抛出SocketException。您可以从其他线程调用它


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

    为了增加使用异步方法的更多理由,我非常确定Thread.Abort不会起作用,因为调用在操作系统级TCP堆栈中被阻止


    另外…如果您在回调中调用BeginAcceptTCPClient来侦听除第一个连接之外的每个连接,请小心确保执行初始BeginAccept的线程不会终止,否则侦听器将自动被框架处理。我想这是一个功能,但实际上它非常烦人。在桌面应用程序这通常不是问题,但在web上,您可能希望使用线程池,因为这些线程从未真正终止。

    一些更改使Peter Oehlert anwer变得完美。因为在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();
           }
       }
    

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

    A blocking operation was interrupted by a call to WSACancelBlockingCall
    
    看到我的答案了吗
    TcpListener.Pending()
    不是上面提到的好解决方案

    请改用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;
            }
    

    文档中明确指出,关闭TcpClient不会关闭底层流。是的,文档中这样说,但实现会关闭流…请检查TcpClient TcpClient=new TcpClient();TcpClient.Connect(“www.google.com”,80);NetworkStream NetworkStream=TcpClient.GetStream();TcpClient.Clos