0

我正在使用 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 应用程序时,我显然需要在另一个线程上运行服务器,但是当用户关闭我的应用程序时,我想确保套接字服务器“正确”关闭。我对这意味着什么的最佳猜测是关闭任何打开的客户端连接,打破侦听循环,然后关闭服务器套接字(不一定按此顺序)。

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

4

3 回答 3

1
  1. 这个块: Console.WriteLine("\nPress ENTER to continue..."); 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();
     }
    
      }
    }
    
于 2015-04-17T13:33:42.367 回答
1

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

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

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

于 2015-04-17T13:22:11.837 回答
1

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

只需serverSocket.BeginAccept在结束时再次调用AcceptCallback。然后你的服务器的关闭减少到serverSocket.Close()(显然,serverSocket需要是一个类字段)。

最后,AcceptCallback您需要捕获EndAccept. 顺便说一句,这就是为什么在示例中存在Console.Writelineand的原因Console.Readkey:它在发生异常时执行。

于 2015-04-17T13:22:27.013 回答