断开/关闭客户端

本文关键字:客户端 断开 | 更新日期: 2023-09-27 17:58:00

我有一个简单的客户端-服务器应用程序,其工作方式如下:服务器始终在侦听(在一个单独的线程中)客户端连接(它发送希望服务器终止的进程的名称)。

这是服务器:

private void btnStart_Click(object sender, EventArgs e)
{
  _port = int.Parse(comboBoxPorts.SelectedItem.ToString());
  _tcpListener = new TcpListener(_ipAddress, _port);
  _keepRunning = true;
  _listenerThread = new Thread(Listen);
  HandleListenerThreadStartListenEvent += HandleListenerThreadStartedEventMethod;
  ListenerThreadStartedEvent += HandleListenerThreadStartListenEvent;
  _listenerThread.Start();
}
private void btnStop_Click(object sender, EventArgs e)
{ 
  if (_tcpListener != null)
  {
    _keepRunning = false; 
    if (_tcpListener.Server.Connected)
    {
      _tcpListener.Server.Disconnect(true); 
    } 
    _tcpListener.Stop(); 
  }
  labelServerStatus.Text = "Server is stopped";   
  comboBoxPorts.Enabled = true; 
  btnStart.Enabled = true; 
  btnStop.Enabled = false; 
} 
private void Listen()
{
  try
  {
    _tcpListener.Start();
    OnListenerThreadStartListenEvent(); // just update the GUI
  }
  catch(Exception e)
  {
    MessageBox.Show("Port " + _port + " is NOT available." + Environment.NewLine +
                    "Please choose another one: " + e.Message);
    return;
  }
  _keepRunning = true;
  string ballonMessage = "Socket Server Running at " + _ipAddress + ", port: " + _port;
  notifyIcon1.ShowBalloonTip(2000, "Simplex Listener", ballonMessage, ToolTipIcon.Info);
  while (_keepRunning)
  {
    try
    {          
      #region using AcceptSocket()
      _clientSocket = _tcpListener.AcceptSocket();
      string checkString = string.Empty;
      IPAddress ipOfClient = ((IPEndPoint) _clientSocket.LocalEndPoint).Address;
      notifyIcon1.ShowBalloonTip(2000, "Simplex Listener", "New client has connected from ip " + ipOfClient, ToolTipIcon.Info);
      byte[] buffer = new byte[SIZE_OF_BUFFER];
      int bytesReceived = _clientSocket.Receive(buffer);
      // Concatenate chars as bytes to a received string.
      for (int i = 0; i < bytesReceived; i++)
        checkString += Convert.ToChar(buffer[i]);

      //..... getting the name of process and kill it (and than open it...
      RestartProcess(nameOfProcess, windowName, pathToExeFile);
      // Client is waiting to know operation is complete- so send him some char...
      ASCIIEncoding encoder = new ASCIIEncoding();
      _clientSocket.Send(encoder.GetBytes("v"));
      _clientSocket.Disconnect(true);
      _clientSocket.Close();
      #endregion
    }
    catch (Exception )
    {    
    }
  }
}

客户端:

public void RestartTheSoftwareInServerComputer(string ipOfServer, int portNumber)
{
  TcpClient client = new TcpClient();
  if (_serverEndPoint == null)
  {
    _serverEndPoint = new IPEndPoint(IPAddress.Parse(ipOfServer), portNumber);     
  }
  client.Connect(_serverEndPoint);
  // Send the command to the server:
  NetworkStream clientStream = client.GetStream();
  ASCIIEncoding encoder = new ASCIIEncoding();
  byte[] buffer = encoder.GetBytes("....detailsOfProcess....");
  clientStream.Write(buffer, 0, buffer.Length);
  clientStream.Flush();
  // Now, wait for the server's response [which means the process had been restart].
  NetworkStream stream = client.GetStream();
  byte[] bytes = new byte[5];
  stream.Read(bytes, 0, 5);
  string response = Encoding.UTF8.GetString(bytes, 0, 1);
  if (response.Equals("x"))
  {
    throw new Exception("Failed to restart X software.");
  }
  stream.Close();
  client.Close();
}

当我停止并重新启动服务器时(当没有客户端连接时),一切都正常

问题是,当服务器连接了一些客户端并重新启动时,客户端已经断开连接,需要重新启动服务器。当我们再次点击"启动服务器"时,它将得到异常:

每个套接字地址(协议/网络地址/端口)只能使用一次通常是允许的。

我应该如何关闭端口?

断开/关闭客户端

退出服务器时,应调用_tcpListener.Stop()关闭服务器正在侦听的主套接字。

编辑:您也可以尝试在单击停止按钮时调用_listenerThread.Join(),以等待侦听器线程完成,然后再启动下一个线程。

private void btnStop_Click(object sender, EventArgs e)
{ 
  if (_tcpListener != null)
  {
    _keepRunning = false;
    if (_tcpListener.Server.Connected)
    {
      _tcpListener.Server.Disconnect(true); 
      _tcpListener.Stop();
      if (_clientSocket != null)
      {
        _clientSocket.Close();
        _clientSocket = null;
      }
      _listenerThread.Join();
    }
  }
  labelServerStatus.Text = "Server is stopped";   
  comboBoxPorts.Enabled = true; 
  btnStart.Enabled = true; 
  btnStop.Enabled = false; 
} 

编辑2:

这里有一个windows窗体,它的作用与您的服务器类似。我不需要客户端,只需在命令提示符下使用"telnet localhost 49152"来"假装"是正在连接的客户端。

public partial class Form1 : Form
{
    private TcpListener _tcpListener;
    private bool _keepRunning;
    private Thread _listenerThread;
    private Socket _clientSocket;

    public Form1()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, EventArgs e)
    {
        var address = IPAddress.Parse("127.0.0.1");
        _tcpListener = new TcpListener(address, 49152);
        _keepRunning = true;
        _listenerThread = new Thread(Listen);
        _listenerThread.Start();
        button1.Enabled = false;
        button2.Enabled = true;
    }
    private void Listen()
    {
        try
        {
            _tcpListener.Start();
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message);
            return;
        }
        _keepRunning = true;
        while (_keepRunning)
        {
            try
            {
                _clientSocket = _tcpListener.AcceptSocket();
                var buffer = new byte[8192];
                var bytesReceived = _clientSocket.Receive(buffer);
                var checkString = String.Empty;
                if (_keepRunning)
                {
                    // bytesReceived can be 0 if the remote socket disconnected
                    if (bytesReceived > 0)
                    {
                        checkString = Encoding.ASCII.GetString(buffer, 0, bytesReceived);
                        // Client is waiting to know operation is complete- so send him some char...
                        var encoder = new ASCIIEncoding();
                        _clientSocket.Send(encoder.GetBytes("v"));
                    }
                    if (_clientSocket.Connected) _clientSocket.Disconnect(true);
                }
                _clientSocket.Close();
            }
            catch (Exception ex)
            {
                // really should do something with these exceptions
            }
        }
    }
    private void button2_Click(object sender, EventArgs e)
    {
        if (_tcpListener != null)
        {
            _keepRunning = false;
            if (_tcpListener.Server.Connected)
            {
                _tcpListener.Server.Disconnect(true);
            }
            _tcpListener.Stop();
            if (_clientSocket != null)
            {
                _clientSocket.Close();
                _clientSocket = null;
            }
            _listenerThread.Join();
        }
        button1.Enabled = true;
        button2.Enabled = false;
    }
}

这段代码有很多问题,例如在线程之间共享变量等,但在我的机器上,Join似乎不会阻塞任何时间。_keepRunning的问题是,在某些系统上,一个线程可能看不到从true到false的变化,因为它得到了优化或缓存。你真的应该使用某种形式的线程同步,使其不稳定,或者将其封装在锁中等等。我建议你在这里阅读一下。我还建议你也阅读一下Sockets,或者像其他评论者提到的那样,如果你对学习套接字和线程的所有特性不感兴趣,也许你应该寻找一个更高级别的库来隐藏它?