简体   繁体   English

在WinForms应用程序中关闭套接字服务器的正确方法?

[英]Proper way to shut down socket server in WinForms application?

I'm working on a basic socket server in C# that needs to run as part of a Windows Forms application. 我正在使用C#中的基本套接字服务器进行工作,该服务器需要作为Windows Forms应用程序的一部分运行。 I started with the asynchronous socket server code from MSDN . 我从MSDN的异步套接字服务器代码开始。 Like many socket server code samples, this one is a console mode application. 像许多套接字服务器代码示例一样,这是一个控制台模式应用程序。 Here's a sample of the StartListening() method that gets called from Main(): 这是从Main()调用的StartListening()方法的示例:

public static void StartListening()
{
    // data buffer for incoming data
    byte[] bytes = new byte[1024];

    IPAddress ipAddress = IPAddress.Parse(serverIpAddress);
    IPEndPoint localEndPoint = new IPEndPoint(ipAddress, portNumber);

    // create TCP/IP socket
    Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    // bind socket to the local endpoint and listen for incoming connections
    try
    {
        serverSocket.Bind(localEndPoint);
        serverSocket.Listen(100);

        while (true)
        {
            // set the allDone ManualResetEvent to nonsignaled state
            allDone.Reset();

            // start an asynchronous socket to listen for connections
            Console.WriteLine("Waiting for a connection...");
            serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), serverSocket);

            // wait until a connection is made before continuing
            allDone.WaitOne();
        }

    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }

    Console.WriteLine("\nPress ENTER to continue...");
    Console.Read();
}

As you can see, there's a while(true) loop in there which uses a ManualResetEvent to wait until a client connection is established before entering the next iteration of the while loop. 如您所见,其中有一个while(true)循环,该循环使用ManualResetEvent等待进入客户端连接,然后进入While循环的下一个迭代。 I'm not sure why they included the Console.WriteLine(\\n"Press ENTER to continue...") statement since there's no way to break out of the while(true) loop. 我不确定为什么它们包含Console.WriteLine(\\n"Press ENTER to continue...")语句,因为没有办法打破while(true)循环。

Anyway, in thinking about converting this into a WinForms application, I would obviously need to run the server on another thread but when the user closes my application, I want to make sure that the socket server gets shutdown "properly". 无论如何,在考虑将其转换为WinForms应用程序时,我显然需要在另一个线程上运行服务器,但是当用户关闭我的应用程序时,我要确保套接字服务器“正确”关闭。 My best guess at what that means is closing any open client connections, breaking out of the listening loop and then shutting down the server socket (not necessarily in that order). 我的最佳猜测是关闭任何打开的客户端连接,中断侦听循环,然后关闭服务器套接字(不一定按此顺序)。

I've seen some suggestions that recommend just making the server thread a background thread and letting it get destroyed when the application is shutdown but that doesn't seem very "clean". 我已经看到一些建议,这些建议只建议使服务器线程成为后台线程,并在应用程序关闭时让它被销毁,但这似乎不太“干净”。 If, for example, a client is connected and sending data when the application is closed, what does that do to the client? 例如,如果关闭了客户端的连接并在应用程序关闭时发送数据,那么这对客户端有什么作用? Can anyone suggest a "best practice" with regard to shutting down a socket server properly? 任何人都可以建议有关正确关闭套接字服务器的“最佳实践”吗?

I would wrap that entire thing in a Task and use a cancellation token. 我会将整个内容包装在Task中,并使用取消标记。

See https://msdn.microsoft.com/en-us/library/hh160373(v=vs.110).aspx for a good example on using tasks and cancellation tokens. 有关使用任务和取消令牌的良好示例,请参阅https://msdn.microsoft.com/zh-cn/library/hh160373(v=vs.110).aspx You can make the cancellation token cancel the task when you want to shut down the socket server. 当您要关闭套接字服务器时,可以使取消令牌取消任务。

Inside the task an exception is raised. 在任务内部会引发异常。 In the exception handler you call Socket.Close which will stop the BeginAccept call (EndAccept will be called but the Socket.Handle will be -1). 在异常处理程序中,您调用Socket.Close,这将停止BeginAccept调用(将调用EndAccept,但Socket.Handle将为-1)。

Actually you don't need a thread, cause you're already listening asynchronously. 实际上,您不需要线程,因为您已经在异步监听。

Just call serverSocket.BeginAccept again at the end of AcceptCallback . 只需在AcceptCallback的结尾再次调用serverSocket.BeginAccept Then the shutdown of your server reduces to serverSocket.Close() (obviously, serverSocket needs to be a class field). 然后,服务器的关闭将减少为serverSocket.Close() (显然, serverSocket必须是一个类字段)。

Finally, in AcceptCallback you would need to catch the exception of EndAccept . 最后,在AcceptCallback您需要捕获EndAccept的异常。 That, btw, is the reason, why there's Console.Writeline and Console.Readkey in the example: it's executed when an exception occurs. 顺便说一句,这就是为什么在示例中出现Console.WritelineConsole.Readkey的原因:当发生异常时执行。

  1. This block: Console.WriteLine("\\nPress ENTER to continue..."); 此块:Console.WriteLine(“ \\ n按ENTER继续...”); Console.Read(); Console.Read(); is there because the way this method is written it will exit the loop if an exception is thrown. 那里是因为编写此方法的方式,如果引发异常,它将退出循环。

  2. I would write it like this to fit your parameters: 我会这样写以适合您的参数:

     using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class Form1 : Form { private Socket serverSocket; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { StartListening(); } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { serverSocket.Close(); } public void StartListening() { byte[] bytes = new byte[1024]; IPAddress ipAddress = IPAddress.Parse(serverIpAddress); IPEndPoint localEndPoint = new IPEndPoint(ipAddress, portNumber); serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { serverSocket.Bind(localEndPoint); serverSocket.Listen(100); while(true) { allDone.Reset(); Console.WriteLine("Waiting for a connection..."); serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), serverSocket); // wait until a connection is made before continuing allDone.WaitOne(); } } catch(Exception e) { Console.WriteLine(e.ToString()); } finally { serverSocket.Close(); } Console.WriteLine("\\nPress ENTER to continue..."); Console.Read(); } } } 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM