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

时间:2015-04-17 13:02:39

标签: c# winforms sockets server application-shutdown

我正在使用C#中的基本套接字服务器,它需要作为Windows窗体应用程序的一部分运行。我从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应用程序时,我显然需要在另一个线程上运行服务器,但是当用户关闭我的应用程序时,我想确保套接字服务器关闭"正确& #34 ;.我最好的猜测是什么意思是关闭任何打开的客户端连接,打破监听循环,然后关闭服务器套接字(不一定按顺序)。

我已经看到一些建议,建议只是让服务器线程成为后台线程,并在应用程序关闭时让它被销毁,但这看起来不是很干净"。例如,如果客户端已连接并在应用程序关闭时发送数据,那么这对客户端有何影响?任何人都可以建议一个最佳实践"关于正确关闭套接字服务器?

3 个答案:

答案 0 :(得分:1)

我会将整个事情包装在一个Task中并使用取消令牌。

有关使用任务和取消令牌的好例子,请参阅https://msdn.microsoft.com/en-us/library/hh160373(v=vs.110).aspx。当您要关闭套接字服务器时,可以使取消令牌取消该任务。

在任务内部引发异常。在异常处理程序中,您调用Socket.Close将停止BeginAccept调用(将调用EndAccept,但Socket.Handle将为-1)。

答案 1 :(得分:1)

实际上你不需要线程,因为你已经异步监听了。

只需在serverSocket.BeginAccept结束时再次致电AcceptCallback。 然后关闭服务器的时间减少到serverSocket.Close()(显然,serverSocket必须是类字段。)

最后,在AcceptCallback中,您需要捕获EndAccept的例外情况。那个,顺便说一句,原因是,为什么示例中有Console.WritelineConsole.Readkey:它在异常发生时执行。

答案 2 :(得分:1)

  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();
     }
    
      }
    }