簡體   English   中英

在WinForms應用程序中關閉套接字服務器的正確方法?

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

我正在使用C#中的基本套接字服務器進行工作,該服務器需要作為Windows Forms應用程序的一部分運行。 我從MSDN的異步套接字服務器代碼開始。 像許多套接字服務器代碼示例一樣,這是一個控制台模式應用程序。 這是從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();
}

如您所見,其中有一個while(true)循環,該循環使用ManualResetEvent等待進入客戶端連接,然后進入While循環的下一個迭代。 我不確定為什么它們包含Console.WriteLine(\\n"Press ENTER to continue...")語句,因為沒有辦法打破while(true)循環。

無論如何,在考慮將其轉換為WinForms應用程序時,我顯然需要在另一個線程上運行服務器,但是當用戶關閉我的應用程序時,我要確保套接字服務器“正確”關閉。 我的最佳猜測是關閉任何打開的客戶端連接,中斷偵聽循環,然后關閉服務器套接字(不一定按此順序)。

我已經看到一些建議,這些建議只建議使服務器線程成為后台線程,並在應用程序關閉時讓它被銷毀,但這似乎不太“干凈”。 例如,如果關閉了客戶端的連接並在應用程序關閉時發送數據,那么這對客戶端有什么作用? 任何人都可以建議有關正確關閉套接字服務器的“最佳實踐”嗎?

我會將整個內容包裝在Task中,並使用取消標記。

有關使用任務和取消令牌的良好示例,請參閱https://msdn.microsoft.com/zh-cn/library/hh160373(v=vs.110).aspx 當您要關閉套接字服務器時,可以使取消令牌取消任務。

在任務內部會引發異常。 在異常處理程序中,您調用Socket.Close,這將停止BeginAccept調用(將調用EndAccept,但Socket.Handle將為-1)。

實際上,您不需要線程,因為您已經在異步監聽。

只需在AcceptCallback的結尾再次調用serverSocket.BeginAccept 然后,服務器的關閉將減少為serverSocket.Close() (顯然, serverSocket必須是一個類字段)。

最后,在AcceptCallback您需要捕獲EndAccept的異常。 順便說一句,這就是為什么在示例中出現Console.WritelineConsole.Readkey的原因:當發生異常時執行。

  1. 此塊:Console.WriteLine(“ \\ n按ENTER繼續...”); Console.Read(); 那里是因為編寫此方法的方式,如果引發異常,它將退出循環。

  2. 我會這樣寫以適合您的參數:

     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