窗体1:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace server
{
    public partial class Form1 : Form
    {
        #region TCP服务全局变量
        //监听端口
        private const int Port = 8899;
        //保存所有在线客户端Socket
        private readonly List<Socket> _clientSockets = new List<Socket>();
        private Socket? _listenSocket;
        //标记服务是否运行
        private bool _serverRunning = false;

        //异步任务保存,防止GC回收
        private Task? _serverTask;
        private CancellationTokenSource? _cts;
        #endregion

        public Form1()
        {
            InitializeComponent();
            // 初始化占位文字灰色
            textBox1.Text = "请输入对话内容";
            textBox1.ForeColor = Color.Gray;
        }

        //按钮点击事件:启动/停止服务器
        private void button1_Click(object sender, EventArgs e)
        {
            if (!_serverRunning)
            {
                //启动服务器
                _cts = new CancellationTokenSource();
                _serverTask = StartServerAsync(_cts.Token);
                button1.Text = "停止服务器";
            }
            else
            {
                //关闭服务器
                StopServer();
                button1.Text = "启动服务器";
                AppendLog("服务已经关闭");
            }
        }

        #region TCP核心方法
        //启动TCP监听
        private async Task StartServerAsync(CancellationToken token)
        {
            try
            {
                AppendLog("    ");
                AppendLog("====== C# WinForm TCP服务端启动 =====");
                AppendLog($"监听端口:{Port}");
                //AppendLog("客户端消息会实时显示在此窗口\r\n");

                _listenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                _listenSocket.Bind(new IPEndPoint(IPAddress.Any, Port));
                _listenSocket.Listen(100);//最大连接数100
                _serverRunning = true;

                //启动接受连接循环
                _ = AcceptClientLoop(token);

                //保持服务运行
                while (_serverRunning && !token.IsCancellationRequested)
                {
                    await Task.Delay(100, token);
                }
            }
            catch (OperationCanceledException)
            {
                AppendLog("服务监听已取消");
            }
            catch (Exception ex)
            {
                AppendLog($"服务器异常:{ex.Message}");
                _serverRunning = false;
                Invoke(new Action(() => { button1.Text = "启动服务器"; }));
            }
        }

        //循环等待新的客户端连接
        private async Task AcceptClientLoop(CancellationToken token)
        {
            while (_serverRunning && !token.IsCancellationRequested)
            {
                try
                {
                    if (_listenSocket is null)
                    {
                        break;
                    }
                    Socket client = await _listenSocket.AcceptAsync(token);
                    lock (_clientSockets)
                    {
                        _clientSockets.Add(client);
                    }
                    IPEndPoint clientEp = client.RemoteEndPoint as IPEndPoint ?? new IPEndPoint(IPAddress.None, 0);
                    AppendLog($"客户端已连接,在线总数:{_clientSockets.Count}");

                    //开启异步循环接收客户端消息
                    _ = ReceiveClientMessageLoop(client, token);
                }
                catch (OperationCanceledException)
                {
                    break;
                }
                catch (Exception ex)
                {
                    AppendLog($"等待客户端连接出错:{ex.Message}");
                }
            }
        }

        //单个客户端循环接收消息
        private async Task ReceiveClientMessageLoop(Socket client, CancellationToken token)
        {
            byte[] buffer = new byte[1024 * 4];
            IPEndPoint ep = client.RemoteEndPoint as IPEndPoint ?? new IPEndPoint(IPAddress.None, 0);
            try
            {
                while (true && !token.IsCancellationRequested)
                {
                    int len = await client.ReceiveAsync(buffer, SocketFlags.None, token);
                    if (len <= 0)
                    {
                        //客户端断开连接
                        break;
                    }
                    string msg = Encoding.UTF8.GetString(buffer, 0, len);
                    AppendLog(msg);
                    //收到消息自动广播给所有客户端
                    await BroadcastMsgAsync(msg);
                }
            }
            catch (OperationCanceledException)
            {

            }
            catch
            {
                //客户端异常断开
            }
            finally
            {
                //移除离线客户端
                lock (_clientSockets)
                {
                    _clientSockets.Remove(client);
                }
                SafeCloseSocket(client);
                AppendLog($"客户端已断开,在线总数:{_clientSockets.Count}");
            }
        }

        //异步广播消息给所有客户端
        private async Task BroadcastMsgAsync(string msg)
        {
            byte[] data = Encoding.UTF8.GetBytes(msg);
            List<Socket> tempList;
            lock (_clientSockets)
            {
                tempList = new List<Socket>(_clientSockets);
            }
            foreach (Socket sock in tempList)
            {
                try
                {
                    await sock.SendAsync(data, SocketFlags.None);
                }
                catch (Exception ex)
                {
                    AppendLog($"向客户端发送消息失败:{ex.Message}");
                }
            }
        }

        //安全关闭单个Socket
        private void SafeCloseSocket(Socket sock)
        {
            if (sock == null) return;
            try
            {
                sock.Shutdown(SocketShutdown.Both);
            }
            catch { }
            sock.Close();
        }

        //停止服务、释放全部资源
        private void StopServer()
        {
            _serverRunning = false;
            _cts?.Cancel();

            //关闭监听Socket
            if (_listenSocket != null)
            {
                SafeCloseSocket(_listenSocket);
                _listenSocket = null;
            }

            //关闭所有客户端连接
            lock (_clientSockets)
            {
                foreach (var sock in _clientSockets)
                {
                    SafeCloseSocket(sock);
                }
                _clientSockets.Clear();
            }
        }
        #endregion

        #region 服务端发送按钮逻辑 button2
        private async void button2_Click(object sender, EventArgs e)
        {
            // 获取底部输入框内容
            string sendContent = textBox1.Text.Trim();
            // 拦截占位文字、空内容
            if (string.IsNullOrWhiteSpace(sendContent) || sendContent == "请输入对话内容")
            {
                AppendLog("【提示】输入框不能为空或占位提示文字!");
                return;
            }
            if (!_serverRunning)
            {
                AppendLog("【错误】服务器未启动,无法发送消息");
                return;
            }

            // 拼接服务端标识消息
            string sendMsg = $"【服务端】:{sendContent}";
            // 本地txtLog打印自己发送的消息
            AppendLog(sendMsg);
            // 广播给所有客户端
            await BroadcastMsgAsync(sendMsg);
            // 清空输入框,恢复占位文字
            textBox1.Clear();
            textBox1.Text = "请输入对话内容";
            textBox1.ForeColor = Color.Gray;
        }

        //可选:输入框回车快捷发送(绑定textBox1 KeyDown事件)
        private void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                button2.PerformClick();
                e.SuppressKeyPress = true; //取消回车换行
            }
        }
        #endregion

        #region 辅助:跨线程添加日志到文本框
        private void AppendLog(string text)
        {
            if (txtLog.InvokeRequired)
            {
                txtLog.Invoke(new Action(() =>
                {
                    string logLine = $"{DateTime.Now:HH:mm:ss} {text}\r\n";
                    txtLog.AppendText(logLine);
                    txtLog.ScrollToCaret();
                }));
                return;
            }
            string line = $"{DateTime.Now:HH:mm:ss} {text}\r\n";
            txtLog.AppendText(line);
            txtLog.ScrollToCaret();
        }
        #endregion

        //窗体关闭时强制停止服务,释放资源
        protected override void OnFormClosed(FormClosedEventArgs e)
        {
            StopServer();
            base.OnFormClosed(e);
        }

        // 输入框获得焦点,清空占位文字
        private void textBox1_Enter(object sender, EventArgs e)
        {
            if (textBox1.Text == "请输入对话内容")
            {
                textBox1.Clear();
                // 文字颜色改为黑色
                textBox1.ForeColor = Color.Black;
            }
        }

        // 输入框失去焦点,无内容则恢复占位提示
        private void textBox1_Leave(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(textBox1.Text))
            {
                textBox1.Text = "请输入对话内容";
                // 占位文字灰色区分
                textBox1.ForeColor = Color.Gray;
            }
        }

        private void button3_Click(object sender, EventArgs e)
        {
            Form2 client = new Form2();
            client.Show();
        }
        //private void textBox1_KeyDown1(object sender, KeyEventArgs e)
        //{
        //    if (e.KeyCode == Keys.Enter)
        //    {
        //        // 禁止占位文字发送
        //        if (textBox1.Text == "请输入对话内容")
        //            return;

        //        button2.PerformClick();
        //        e.SuppressKeyPress = true; //取消回车换行
        //    }
        //}
    }
}

窗体2:

using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace server
{
    public partial class Form2 : Form
    {
        // 客户端全局变量
        private Socket? _clientSocket;
        private bool _isConnected = false;
        private CancellationTokenSource? _cts;

        public Form2()
        {
            InitializeComponent();
            // 默认填充本地测试地址
            txtIP.Text = "127.0.0.1";
            txtPort.Text = "8899";
            txtInput.Text = "请输入对话内容";
            txtInput.ForeColor = Color.Gray;
            btnSend.Enabled = false; // 未连接时发送按钮不可用
        }

        #region 连接/断开按钮事件
        private async void btnConnect_Click(object sender, EventArgs e)
        {
            if (!_isConnected)
            {
                //执行连接
                await ConnectServer();
            }
            else
            {
                //断开连接
                DisconnectServer();
            }
        }

        // 连接服务端
        private async Task ConnectServer()
        {
            try
            {
                // 校验IP和端口
                if (string.IsNullOrWhiteSpace(txtIP.Text) || string.IsNullOrWhiteSpace(txtPort.Text))
                {
                    AppendLog("【错误】IP或端口不能为空!");
                    return;
                }
                if (!int.TryParse(txtPort.Text, out int port))
                {
                    AppendLog("【错误】端口号必须是数字!");
                    return;
                }

                IPAddress ip = IPAddress.Parse(txtIP.Text.Trim());
                IPEndPoint endPoint = new IPEndPoint(ip, port);

                // 创建Socket
                _clientSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
                AppendLog("正在连接服务端...");
                await _clientSocket.ConnectAsync(endPoint);

                // 连接成功
                _isConnected = true;
                btnConnect.Text = "断开";
                btnSend.Enabled = true;
                AppendLog($"【成功】已连接服务端 {txtIP.Text}:{port}");

                // 启动异步接收消息循环
                _cts = new CancellationTokenSource();
                _ = ReceiveMessageLoop(_cts.Token);
            }
            catch (Exception ex)
            {
                AppendLog($"【连接失败】{ex.Message}");
                SafeCloseSocket();
            }
        }

        /// 断开服务端连接,释放资源
        private void DisconnectServer()
        {
            _isConnected = false;
            btnConnect.Text = "连接/断开";
            btnSend.Enabled = false;
            _cts?.Cancel();
            SafeCloseSocket();
            AppendLog("已断开与服务端的连接");
        }

        /// 安全释放Socket
        private void SafeCloseSocket()
        {
            if (_clientSocket == null) return;
            try
            {
                _clientSocket.Shutdown(SocketShutdown.Both);
            }
            catch { }
            _clientSocket.Close();
            _clientSocket = null;
        }
        #endregion

        #region 持续接收服务端消息循环
        private async Task ReceiveMessageLoop(CancellationToken token)
        {
            byte[] buffer = new byte[1024 * 4];
            try
            {
                while (_isConnected && !token.IsCancellationRequested)
                {
                    // 新增空校验,消除警告
                    if (_clientSocket is null)
                        break;

                    int len = await _clientSocket.ReceiveAsync(buffer, SocketFlags.None, token);
                    if (len <= 0)
                    {
                        // 服务端主动断开
                        AppendLog("【提示】服务端已断开连接");
                        break;
                    }
                    string msg = Encoding.UTF8.GetString(buffer, 0, len);
                    AppendLog(msg);
                }
            }
            catch (OperationCanceledException)
            {
                AppendLog("【接收】监听已取消");
            }
            catch (Exception ex)
            {
                AppendLog($"【接收消息异常】{ex.Message}");
            }
            finally
            {
                // 自动断开
                Invoke(new Action(DisconnectServer));
            }
        }
        #endregion

        #region 发送消息按钮
        private async void btnSend_Click(object sender, EventArgs e)
        {
            string content = txtInput.Text.Trim();
            // 过滤占位文字、空内容
            if (string.IsNullOrWhiteSpace(content) || content == "请输入对话内容")
            {
                AppendLog("【提示】输入框不能为空!");
                return;
            }
            if (!_isConnected || _clientSocket == null)
            {
                AppendLog("【错误】未连接服务端,无法发送");
                return;
            }

            try
            {
                // 拼接客户端标识
                string sendMsg = $"【客户端】:{content}";
                byte[] data = Encoding.UTF8.GetBytes(sendMsg);
                await _clientSocket.SendAsync(data, SocketFlags.None);
                // 清空输入框,恢复占位
                txtInput.Clear();
                txtInput.Text = "请输入对话内容";
                txtInput.ForeColor = Color.Gray;
            }
            catch (Exception ex)
            {
                AppendLog($"【发送失败】{ex.Message}");
                DisconnectServer();
            }
        }
        #endregion

        #region 输入框占位文字逻辑(焦点事件)
        private void txtInput_Enter(object sender, EventArgs e)
        {
            if (txtInput.Text == "请输入对话内容")
            {
                txtInput.Clear();
                txtInput.ForeColor = Color.Black;
            }
        }

        private void txtInput_Leave(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(txtInput.Text))
            {
                txtInput.Text = "请输入对话内容";
                txtInput.ForeColor = Color.Gray;
            }
        }

        // 回车快捷发送
        private void txtInput_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                btnSend.PerformClick();
                e.SuppressKeyPress = true;
            }
        }
        #endregion

        #region 日志输出(跨线程安全)
        private void AppendLog(string text)
        {
            if (txtLog.InvokeRequired)
            {
                txtLog.Invoke(new Action(() =>
                {
                    string line = $"[{DateTime.Now:HH:mm:ss}] {text}\r\n";
                    txtLog.AppendText(line);
                    txtLog.ScrollToCaret();
                }));
                return;
            }
            string logLine = $"[{DateTime.Now:HH:mm:ss}] {text}\r\n";
            txtLog.AppendText(logLine);
            txtLog.ScrollToCaret();
        }
        #endregion

        #region 窗体关闭自动断开
        private void FormClient_FormClosed(object sender, FormClosedEventArgs e)
        {
            DisconnectServer();
        }
        #endregion
    }
}

更多推荐