一、C# Socket 核心讲解

1. 什么是 Socket?

Socket(套接字)是网络通信的基石,是两台设备(客户端 / 服务端)之间建立网络连接、收发数据的通信端点。简单理解:Socket 就像网络电话,服务端是「座机」,提前开机等待呼叫;客户端是「手机」,主动拨号连接,连接成功后双方可以互相通话(收发数据)。

2. Socket 核心工作流程

  1. 服务端:创建 Socket → 绑定 IP + 端口 → 监听连接 → 接受客户端连接 → 收发数据 → 关闭连接
  2. 客户端:创建 Socket → 主动连接服务端 → 收发数据 → 关闭连接

3. C# 中 Socket 常用类 / 方法

  • Socket 类:核心类,负责创建套接字、连接、收发数据
  • IPAddress:解析 IP 地址
  • IPEndPoint:封装 IP+端口,作为通信端点
  • Listen():服务端开启监听
  • Accept():服务端接受客户端连接
  • Connect():客户端主动连接服务端
  • Send()/Receive():收发字节数据(网络传输统一用字节)
  • 异步方法AcceptAsync()/ReceiveAsync()(WinForm 必须用异步,避免界面卡死)

4. 关键注意点

  • WinForm 是界面线程,Socket 网络操作必须用异步,否则界面会卡死
  • 数据传输:字符串需要转 byte[] 发送,接收后转回字符串
  • 必须处理异常(断网、连接失败、客户端退出等)

二、WinForm Socket 服务端

1. 界面设计(拖控件)

新建 WinForm 项目,拖入以下控件:

  1. TextBox(命名:txtIP):默认填 127.0.0.1
  2. TextBox(命名:txtPort):默认填 8888
  3. Button(命名:btnStart):文本「启动服务」
  4. Button(命名:btnStop):文本「停止服务」
  5. RichTextBox(命名:rtbMsg):显示日志
  6. TextBox(命名:txtSendMsg):发送消息输入框
  7. Button(命名:btnSend):文本「发送消息」

2. 服务端代码

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

namespace SocketServer_WinForm
{
    public partial class ServerForm : Form
    {
        // 服务端Socket
        private Socket serverSocket;
        // 客户端连接Socket(单客户端,如需多客户端需用列表管理)
        private Socket clientSocket;

        public ServerForm()
        {
            InitializeComponent();
            // 初始化默认值
            txtIP.Text = "127.0.0.1";
            txtPort.Text = "8888";
            // 关闭时释放资源
            FormClosing += ServerForm_FormClosing;
        }

        // 启动服务
        private async void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                btnStart.Enabled = false;
                btnStop.Enabled = true;

                // 1. 创建Socket(IPv4,流式连接,TCP协议)
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                
                // 2. 绑定IP和端口
                IPAddress ip = IPAddress.Parse(txtIP.Text.Trim());
                IPEndPoint point = new IPEndPoint(ip, int.Parse(txtPort.Text.Trim()));
                serverSocket.Bind(point);
                
                // 3. 开启监听(最多挂起10个连接)
                serverSocket.Listen(10);
                AddMsg("服务启动成功,等待客户端连接...");

                // 4. 异步等待客户端连接(不卡死界面)
                await AcceptClientAsync();
            }
            catch (Exception ex)
            {
                AddMsg($"启动失败:{ex.Message}");
                btnStart.Enabled = true;
                btnStop.Enabled = false;
            }
        }

        // 异步接受客户端连接
        private async Task AcceptClientAsync()
        {
            try
            {
                // 等待客户端连接
                clientSocket = await serverSocket.AcceptAsync();
                AddMsg($"客户端【{clientSocket.RemoteEndPoint}】已连接!");
                
                // 连接成功后,异步接收客户端消息
                await ReceiveMsgAsync();
            }
            catch (Exception ex)
            {
                AddMsg($"客户端连接异常:{ex.Message}");
            }
        }

        // 异步接收客户端消息
        private async Task ReceiveMsgAsync()
        {
            try
            {
                byte[] buffer = new byte[1024 * 4]; // 4K缓冲区
                while (true)
                {
                    // 接收数据
                    int length = await clientSocket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
                    if (length == 0) // 客户端断开
                    {
                        AddMsg("客户端已断开连接");
                        break;
                    }
                    // 字节转字符串
                    string msg = Encoding.UTF8.GetString(buffer, 0, length);
                    AddMsg($"客户端:{msg}");
                }
            }
            catch (Exception ex)
            {
                AddMsg($"接收消息异常:{ex.Message}");
            }
        }

        // 发送消息给客户端
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (clientSocket == null || !clientSocket.Connected)
            {
                MessageBox.Show("无客户端连接!");
                return;
            }
            if (string.IsNullOrWhiteSpace(txtSendMsg.Text))
            {
                MessageBox.Show("请输入消息!");
                return;
            }

            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(txtSendMsg.Text.Trim());
                clientSocket.Send(buffer);
                AddMsg($"服务端:{txtSendMsg.Text.Trim()}");
                txtSendMsg.Clear();
            }
            catch (Exception ex)
            {
                AddMsg($"发送失败:{ex.Message}");
            }
        }

        // 停止服务
        private void btnStop_Click(object sender, EventArgs e)
        {
            CloseSocket();
            btnStart.Enabled = true;
            btnStop.Enabled = false;
            AddMsg("服务已停止");
        }

        // 关闭Socket
        private void CloseSocket()
        {
            clientSocket?.Close();
            serverSocket?.Close();
            clientSocket = null;
            serverSocket = null;
        }

        // 窗体关闭时释放资源
        private void ServerForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            CloseSocket();
        }

        // 安全添加日志到界面
        private void AddMsg(string msg)
        {
            if (rtbMsg.InvokeRequired)
            {
                rtbMsg.Invoke(new Action(() => AddMsg(msg)));
            }
            else
            {
                rtbMsg.AppendText($"{DateTime.Now:HH:mm:ss} {msg}\r\n");
                rtbMsg.ScrollToCaret(); // 自动滚动到底部
            }
        }
    }
}

三、WinForm Socket 客户端(完整可运行)

1. 界面设计(和服务端一致)

新建 WinForm 项目,拖入相同控件:

  1. TextBox(txtIP):127.0.0.1
  2. TextBox(txtPort):8888
  3. Button(btnConnect):「连接服务」
  4. Button(btnDisconnect):「断开连接」
  5. RichTextBox(rtbMsg):日志
  6. TextBox(txtSendMsg):发送框
  7. Button(btnSend):发送

2. 客户端代码

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

namespace SocketClient_WinForm
{
    public partial class ClientForm : Form
    {
        // 客户端Socket
        private Socket clientSocket;

        public ClientForm()
        {
            InitializeComponent();
            txtIP.Text = "127.0.0.1";
            txtPort.Text = "8888";
            FormClosing += ClientForm_FormClosing;
        }

        // 连接服务端
        private async void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                btnConnect.Enabled = false;
                btnDisconnect.Enabled = true;

                // 1. 创建Socket
                clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                
                // 2. 异步连接服务端
                IPAddress ip = IPAddress.Parse(txtIP.Text.Trim());
                IPEndPoint point = new IPEndPoint(ip, int.Parse(txtPort.Text.Trim()));
                await clientSocket.ConnectAsync(point);
                
                AddMsg("成功连接到服务端!");
                // 连接后异步接收服务端消息
                await ReceiveMsgAsync();
            }
            catch (Exception ex)
            {
                AddMsg($"连接失败:{ex.Message}");
                btnConnect.Enabled = true;
                btnDisconnect.Enabled = false;
            }
        }

        // 异步接收服务端消息
        private async Task ReceiveMsgAsync()
        {
            try
            {
                byte[] buffer = new byte[1024 * 4];
                while (true)
                {
                    int length = await clientSocket.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);
                    if (length == 0)
                    {
                        AddMsg("服务端已断开");
                        break;
                    }
                    string msg = Encoding.UTF8.GetString(buffer, 0, length);
                    AddMsg($"服务端:{msg}");
                }
            }
            catch (Exception ex)
            {
                AddMsg($"接收消息异常:{ex.Message}");
            }
        }

        // 发送消息给服务端
        private void btnSend_Click(object sender, EventArgs e)
        {
            if (clientSocket == null || !clientSocket.Connected)
            {
                MessageBox.Show("未连接服务端!");
                return;
            }
            if (string.IsNullOrWhiteSpace(txtSendMsg.Text))
            {
                MessageBox.Show("请输入消息!");
                return;
            }

            try
            {
                byte[] buffer = Encoding.UTF8.GetBytes(txtSendMsg.Text.Trim());
                clientSocket.Send(buffer);
                AddMsg($"客户端:{txtSendMsg.Text.Trim()}");
                txtSendMsg.Clear();
            }
            catch (Exception ex)
            {
                AddMsg($"发送失败:{ex.Message}");
            }
        }

        // 断开连接
        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            CloseSocket();
            btnConnect.Enabled = true;
            btnDisconnect.Enabled = false;
            AddMsg("已断开连接");
        }

        // 关闭Socket
        private void CloseSocket()
        {
            clientSocket?.Close();
            clientSocket = null;
        }

        // 窗体关闭释放资源
        private void ClientForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            CloseSocket();
        }

        // 安全添加日志
        private void AddMsg(string msg)
        {
            if (rtbMsg.InvokeRequired)
            {
                rtbMsg.Invoke(new Action(() => AddMsg(msg)));
            }
            else
            {
                rtbMsg.AppendText($"{DateTime.Now:HH:mm:ss} {msg}\r\n");
                rtbMsg.ScrollToCaret();
            }
        }
    }
}

四、运行使用步骤

  1. 先启动服务端:运行服务端项目 → 点击【启动服务】
  2. 再启动客户端:运行客户端项目 → 点击【连接服务】
  3. 互发消息:双方输入文字 → 点击【发送消息】,即可实时通信
  4. 关闭:先停止服务 / 断开连接,再关闭窗体

五、核心知识点总结

  1. TCP 协议:两份代码都用的可靠 TCP 连接(保证数据不丢失、不乱序)
  2. 异步 async/await:WinForm 必须用,解决界面卡死问题
  3. 字节编码:网络传输必须用 byte[],用 UTF8 编码保证中文不乱码
  4. 线程安全:跨线程更新界面用 Invoke,否则会报错
  5. 资源释放:关闭前必须 Close() Socket,避免端口占用

总结

  1. Socket 是网络通信端点,服务端监听、客户端主动连接,TCP 是最常用的可靠通信方式
  2. 两份代码是标准单客户端单服务端模板,直接复制到 WinForm 即可运行
  3. 核心:异步操作 + 字节编码 + 线程安全 + 资源释放
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐