C#TCP客户端在关闭并重新打开客户端表单后重新连接

C# TCP Client reconnect after closing and reopening client form

我用 C# WinForms 编写了一个聊天应用程序。但是如果我关闭客户端表单(当此表单连接到服务器表单时)并重新打开客户端表单并尝试重新连接,客户端不会连接到服务器..

如果应用程序关闭并重新打开,如何将客户端重新连接到服务器?

(抱歉英语不好)

服务器:

    public frmServer()
    {
        InitializeComponent();
        textBox_Hostname.Text = GetLocalIPAddress();
        Configuration Programmkonfiguration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
        Port = Programmkonfiguration.AppSettings.Settings["Port"].Value;
        textBox_Port.Text = Port;
        toolStripStatusLabel_Serverstatus.Text = "deaktiviert";
        toolStripStatusLabel_Serverstatus.ForeColor = Color.Red;
        textBox_Output.Focus();
    }

    private string GetLocalIPAddress()
    {
        var host = Dns.GetHostEntry(Dns.GetHostName());
        foreach (var ip in host.AddressList)
        {
            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                return ip.ToString();
            }
        }
        throw new Exception("No network adapters with an IPv4 address in the system!");
    }

    private void button_Send_Click(object sender, EventArgs e)
    {
        if (!(string.IsNullOrWhiteSpace(textBox_Output.Text)))
        {
            String s = "Server: " + textBox_Output.Text + Environment.NewLine;
            textBox_Input.Text += s;
            byte[] byteTime = Encoding.ASCII.GetBytes(s);
            ns.Write(byteTime, 0, byteTime.Length);
            textBox_Output.Clear();
        }
    }

    public void DoWork()
    {
        byte[] bytes = new byte[1024];
        while (true)
        {
            int bytesRead = ns.Read(bytes, 0, bytes.Length);
            this.SetText(Encoding.ASCII.GetString(bytes, 0, bytesRead));
        }
    }

    private void SetText(string text)
    {
        if (this.textBox_Input.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(SetText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.textBox_Input.Text = this.textBox_Input.Text + text;
        }
    }

    private void button_Starten_Click(object sender, EventArgs e)
    {
        IPAddress hostname = IPAddress.Parse(textBox_Hostname.Text);
        int portNum = Convert.ToInt32(textBox_Port.Text);
        listener = new TcpListener(hostname, portNum);
        listener.Start();
        Task TCPListener = new Task(() => AcceptTCP());
        TCPListener.Start();
        textBox_Input.Text += "Server gestartet." + Environment.NewLine;
        button_Starten.Enabled = false;
        toolStripStatusLabel_Serverstatus.Text = "aktiviert";
        toolStripStatusLabel_Serverstatus.ForeColor = Color.Green;
    }

    private void AcceptTCP()
    {
        client = listener.AcceptTcpClient();
        ns = client.GetStream();
        Task Work = new Task(() => DoWork());
        Work.Start();
    }

    private void textBox_Output_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter)
        {
            button_Send_Click(sender, e);
            e.Handled = true;
            textBox_Output.Focus();
        }
    }
}

客户:

    public frmClient()
    {
        InitializeComponent();
        Configuration Programmkonfiguration = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
        HostnameServer = Programmkonfiguration.AppSettings.Settings["HostnameServer"].Value;
        Port = Programmkonfiguration.AppSettings.Settings["Port"].Value;
        textBox_Hostname.Text = GetLocalIPAddress();
        textBox_Port.Text = Port;
        toolStripStatusLabel_Status.Text = " nicht verbunden";
        toolStripStatusLabel_Status.ForeColor = Color.Red;
        textBox_Output.Focus();
    }

    private string GetLocalIPAddress()
    {
        var host = Dns.GetHostEntry(HostnameServer);
        foreach (var ip in host.AddressList)
        {
            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                return ip.ToString();
            }
        }
        throw new Exception("No network adapters with an IPv4 address in the system!");
    }


    private void button_Senden_Click(object sender, EventArgs e)
    {
        if (!(string.IsNullOrWhiteSpace(textBox_Output.Text)))
        {
            String s = "Client: " + textBox_Output.Text + Environment.NewLine;
            textBox_Input.Text += s;
            byte[] byteTime = Encoding.ASCII.GetBytes(s);
            ns.Write(byteTime, 0, byteTime.Length);
            textBox_Output.Clear();
        }
    }

    public void DoWork()
    {
        byte[] bytes = new byte[1024];
        while (true)
        {
            if (ns.DataAvailable)
            {
                int bytesRead = ns.Read(bytes, 0, bytes.Length);
                this.SetText(Encoding.ASCII.GetString(bytes, 0, bytesRead));
            }
        }
    }

    private void SetText(string text)
    {
        if (this.textBox_Input.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(SetText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.textBox_Input.Text = this.textBox_Input.Text + text;
        }
    }

    private void button_Connect_Click(object sender, EventArgs e)
    {
        string hostName = textBox_Hostname.Text;
        int portNum = Convert.ToInt32(textBox_Port.Text);
        client = new TcpClient(hostName, portNum);
        ns = client.GetStream();
        Work = new Task(() => DoWork());
        Work.Start();
        textBox_Input.Text += "Verbindung hergestellt." + Environment.NewLine;
        button_Connect.Enabled = false;
        toolStripStatusLabel_Status.Text = "verbunden";
        toolStripStatusLabel_Status.ForeColor = Color.Green;
    }

    private void textBox_Output_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter)
        {
            button_Senden_Click(sender, e);
            e.Handled = true;
            textBox_Output.Focus();
        }
    }
}

在侦听器套接字上对 Accept 的每次调用都接受一个连接。目前,您的服务器接受单个连接请求,然后从那时起忽略侦听器套接字。

通常,您需要某种形式的“Accept 循环”不断调用 Accept,为该连接设置服务器端资源,然后循环回调用 Accept 再次.

例如微不足道的改变是这样做:

private void AcceptTCP()
{
    while(true)
    {
      client = listener.AcceptTcpClient();
      ns = client.GetStream();
      Task Work = new Task(() => DoWork());
      Work.Start();
    }
}

但现在您真的必须考虑 ns 会发生什么 - 会发生什么 if/when 您想要同时连接第二个客户端?让 NetworkStream 成为服务器中的单个共享实例变量不会很好地扩展。

通常,您希望创建某种形式的简单 class 来表示每个连接 + 与该连接相关的服务器特定信息。例如。它将包含 NetworkStream、此连接的当前 "state"(如果您有模式或状态),可能是最后传递给 Read 调用 as/when 的缓冲区开始 async 这项工作,等等