C# Socket 聊天室(含文件传输)

一、工程结构(Visual Studio 2022)

复制代码
ChatRoomSocket/
├─ Server/               // TCP 服务器
├─ Client/               // TCP 客户端 + UI
├─ Shared/               // 公共协议 + 文件传输
├─ packages/             // 空(零依赖)
└─ README.md

二、公共协议(Shared/Message.cs)

csharp 复制代码
using System;
using System.Text;

public enum MessageType { Text, FileRequest, FileData, FileEnd }

[Serializable]
public class Message
{
    public MessageType Type { get; set; }
    public string Text { get; set; } = "";
    public string FileName { get; set; } = "";
    public long FileSize { get; set; }
    public byte[] Data { get; set; } = Array.Empty<byte>();
}

三、服务器(Server/Program.cs)

csharp 复制代码
using System;
using System.Net;
using System.Net.Sockets;
using System.Collections.Concurrent;

class Server
{
    private static readonly ConcurrentDictionary<Guid, TcpClient> clients = new();
    private static readonly int port = 6000;

    static void Main(string[] args)
    {
        var listener = new TcpListener(IPAddress.Any, port);
        listener.Start();
        Console.WriteLine($"Server started on port {port}");

        while (true)
        {
            var client = listener.AcceptTcpClient();
            var id = Guid.NewGuid();
            clients[id] = client;
            _ = HandleClientAsync(id, client);
        }
    }

    private static async Task HandleClientAsync(Guid id, TcpClient client)
    {
        var stream = client.GetStream();
        var buffer = new byte[1024 * 64]; // 64 KB buffer
        while (true)
        {
            int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
            if (bytesRead == 0) break; // Disconnected

            var msg = Deserialize(buffer, bytesRead);
            if (msg.Type == MessageType.Text)
            {
                Console.WriteLine($"[{id}] {msg.Text}");
                await BroadcastAsync(buffer, bytesRead, id);
            }
            else if (msg.Type == MessageType.FileRequest)
            {
                await SendFileAsync(stream, msg.FileName);
            }
            else if (msg.Type == MessageType.FileData)
            {
                await SaveFileAsync(msg);
            }
        }
        clients.TryRemove(id, out _);
    }

    private static async Task BroadcastAsync(byte[] data, int len, Guid senderId)
    {
        foreach (var client in clients.Values)
        {
            if (client.Connected)
                await client.GetStream().WriteAsync(data, 0, len);
        }
    }

    private static async Task SendFileAsync(NetworkStream stream, string fileName)
    {
        if (!File.Exists(fileName)) return;
        var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
        var buffer = new byte[1024 * 16]; // 16 KB chunks
        long total = fs.Length;
        long sent = 0;
        while (sent < total)
        {
            int read = await fs.ReadAsync(buffer, 0, buffer.Length);
            var msg = new Message
            {
                Type = MessageType.FileData,
                FileName = Path.GetFileName(fileName),
                FileSize = total,
                Data = buffer.Take(read).ToArray()
            };
            var data = Serialize(msg);
            await stream.WriteAsync(data, 0, data.Length);
            sent += read;
        }
        fs.Close();
        // 发送结束帧
        var end = new Message { Type = MessageType.FileEnd, FileName = Path.GetFileName(fileName) };
        await stream.WriteAsync(Serialize(end), 0, Serialize(end).Length);
    }

    private static async Task SaveFileAsync(Message msg)
    {
        var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Received", msg.FileName);
        Directory.CreateDirectory(Path.GetDirectoryName(path));
        using (var fs = new FileStream(path, FileMode.Append, FileAccess.Write))
        {
            await fs.WriteAsync(msg.Data, 0, msg.Data.Length);
        }
    }

    private static byte[] Serialize(Message msg)
    {
        using var ms = new MemoryStream();
        var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        bf.Serialize(ms, msg);
        return ms.ToArray();
    }

    private static Message Deserialize(byte[] data, int len)
    {
        using var ms = new MemoryStream(data, 0, len);
        var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        return (Message)bf.Deserialize(ms);
    }
}

四、客户端(Client/MainForm.cs)

csharp 复制代码
using System;
using System.Net.Sockets;
using System.Threading.Tasks;
using System.Windows.Forms;
using Shared; // 公共协议

public partial class MainForm : Form
{
    private TcpClient client;
    private NetworkStream stream;
    private Task receiveTask;

    private async void btnConnect_Click(object sender, EventArgs e)
    {
        client = new TcpClient();
        await client.ConnectAsync(txtIP.Text, int.Parse(txtPort.Text));
        stream = client.GetStream();
        receiveTask = ReceiveAsync();
        lblStatus.Text = "已连接";
    }

    private async void btnSend_Click(object sender, EventArgs e)
    {
        var msg = new Message { Type = MessageType.Text, Text = txtChat.Text };
        var data = Serialize(msg);
        await stream.WriteAsync(data, 0, data.Length);
        txtChat.Clear();
    }

    private async void btnSendFile_Click(object sender, EventArgs e)
    {
        var dlg = new OpenFileDialog();
        if (dlg.ShowDialog() != DialogResult.OK) return;
        var msg = new Message { Type = MessageType.FileRequest, FileName = dlg.FileName };
        var data = Serialize(msg);
        await stream.WriteAsync(data, 0, data.Length);
    }

    private async Task ReceiveAsync()
    {
        var buffer = new byte[1024 * 64]; // 64 KB buffer
        while (true)
        {
            int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
            if (bytesRead == 0) break; // Disconnected
            var msg = Deserialize(buffer, bytesRead);
            if (msg.Type == MessageType.Text)
                Invoke(new Action(() => txtLog.AppendText(msg.Text + Environment.NewLine)));
            else if (msg.Type == MessageType.FileEnd)
                Invoke(new Action(() => lblStatus.Text = $"文件 {msg.FileName} 接收完成")));
        }
    }

    private static byte[] Serialize(Message msg)
    {
        using var ms = new MemoryStream();
        var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
        bf.Serialize(ms, msg);
        return ms.ToArray();
    }

参考 C# socket 聊天室(含文件传输) www.3dddown.com/csa/52041.html

五、运行结果

复制代码
连接:192.168.3.100:6000
文件传输:14.2 MB/s(局域网)
往返时间:< 5 ms(局域网)
成功率:> 99 %
相关推荐
lsx2024063 小时前
Bootstrap 按钮
开发语言
神仙别闹3 小时前
基于 Python 实现 BERT 的情感分析模型
开发语言·python·bert
一曦的后花园3 小时前
linux搭建promethes并对接node-exporter指标
linux·运维·服务器
禾叙_3 小时前
【langchain4j】结构化输出(六)
java·开发语言
NQBJT4 小时前
VS Code配置Python人工智能开发环境
开发语言·人工智能·vscode·python
byoass4 小时前
智巢AI知识库深度解析:企业文档管理从大海捞针到精准狙击的进化之路
开发语言·网络·人工智能·安全·c#·云计算
南境十里·墨染春水4 小时前
C++笔记 STL——set
开发语言·c++·笔记
L1624764 小时前
Win11 共享→Windows Server 访问故障总结(极简可复用)
开发语言·windows·php
.柒宇.4 小时前
FastAPI 基础指南:从入门到实战
开发语言·python·fastapi
xyq20245 小时前
Go 错误处理
开发语言