C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C#即时通讯工具

基于C#实现一个简单的即时通讯工具

作者:jllllyuz

这篇文章主要为大家详细介绍了如何基于C#实现一个简单的即时通讯工具,主要内容包括系统架构设计,技术选型与模块实现,关键技术实现等,有需要的小伙伴可以了解下

系统架构设计

整体架构

技术选型

模块技术方案说明
通信协议TCP+自定义二进制协议高效可靠,支持流式传输
网络层System.Net.Sockets + Async/Await异步非阻塞IO模型
消息队列Redis Pub/Sub解耦消息生产消费
数据库SQL Server + Entity Framework用户数据持久化
推送服务SignalR实时消息推送
文件传输chunked transfer + 断点续传支持大文件传输

核心模块实现

通信网关(TCP服务端)

public class TcpGateway
{
    private TcpListener _listener;
    private ConcurrentDictionary<string, Socket> _clients = new();
    public async Task StartAsync(string ip, int port)
    {
        _listener = new TcpListener(IPAddress.Parse(ip), port);
        await _listener.StartAsync();
        while (true)
        {
            var client = await _listener.AcceptTcpClientAsync();
            _ = HandleClientAsync(client);
        }
    }
    private async Task HandleClientAsync(TcpClient client)
    {
        using (client)
        {
            var stream = client.GetStream();
            var buffer = new byte[1024 * 4];
            while (client.Connected)
            {
                int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                if (bytesRead == 0) break;
                var msg = MessageParser.Decode(buffer, bytesRead);
                await MessageDispatcher.Dispatch(msg);
            }
        }
    }
}

消息协议设计

public class MessageProtocol
{
    public ushort Magic { get; set; } = 0xA55A;
    public byte Version { get; set; }
    public MessageType Type { get; set; }
    public int BodyLength { get; set; }
    public byte[] Body { get; set; }
}

public enum MessageType
{
    Login = 1,
    Text = 2,
    File = 3,
    Heartbeat = 4
}

消息处理中心

public static class MessageDispatcher
{
    public static async Task Dispatch(MessageProtocol msg)
    {
        switch (msg.Type)
        {
            case MessageType.Login:
                await AuthService.Authenticate(msg.Body);
                break;
            case MessageType.Text:
                await ChatService.SendMessage(msg);
                break;
            case MessageType.File:
                await FileService.Upload(msg);
                break;
            case MessageType.Heartbeat:
                HeartbeatManager.KeepAlive(msg.SessionId);
                break;
        }
    }
}

关键技术实现

高并发处理

// 使用线程池优化资源
ThreadPool.SetMinThreads(50, 50);

// 异步处理消息
public async Task ProcessMessageAsync(Socket socket)
{
    var buffer = new byte[1024 * 10];
    while (true)
    {
        int bytesRead = await socket.ReceiveAsync(
            new ArraySegment&lt;byte&gt;(buffer), 
            SocketFlags.None);
        
        if (bytesRead == 0) break;
        
        _ = Task.Run(() =&gt; HandleData(buffer, bytesRead));
    }
}

消息持久化

public class ChatRepository
{
    private readonly DbContext _context;

    public async Task SaveMessage(ChatMessage message)
    {
        _context.Messages.Add(message);
        await _context.SaveChangesAsync();
        
        // 写入Redis消息队列
        await Redis.PublishAsync("chat_messages", message);
    }
}

文件传输

public class FileTransferService
{
    public async Task SendFile(string filePath, Socket client)
    {
        using (var fileStream = File.OpenRead(filePath))
        {
            var buffer = new byte[1024 * 1024]; // 1MB分块
            int bytesRead;
            
            while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) &gt; 0)
            {
                await client.SendAsync(
                    new ArraySegment&lt;byte&gt;(buffer, 0, bytesRead),
                    SocketFlags.None);
            }
        }
    }
}

安全机制

通信加密

public class SecurityManager
{
    private readonly SslStream _sslStream;

    public async Task EstablishSecureConnectionAsync(Socket socket)
    {
        _sslStream = new SslStream(socket.GetStream(), false);
        await _sslStream.AuthenticateAsServerAsync(serverCertificate);
    }

    public byte[] EncryptData(byte[] data)
    {
        return _sslStream.Encrypt(data);
    }
}

心跳机制

public class HeartbeatMonitor
{
    private readonly ConcurrentDictionary&lt;string, DateTime&gt; _sessions = new();

    public void UpdateHeartbeat(string sessionId)
    {
        _sessions[sessionId] = DateTime.Now;
    }

    public bool CheckTimeout(string sessionId, int timeoutSeconds = 30)
    {
        return (DateTime.Now - _sessions[sessionId]).TotalSeconds &gt; timeoutSeconds;
    }
}

扩展功能实现

群组管理

public class GroupService
{
    public async Task CreateGroup(string groupName, List&lt;string&gt; members)
    {
        var groupId = Guid.NewGuid().ToString();
        await _redis.HashSetAsync($"group:{groupId}", 
            new HashEntry[] { 
                new("name", groupName),
                new("members", string.Join(",", members))
            });
    }
}

消息撤回

public class MessageService
{
    public async Task RecallMessage(string msgId)
    {
        var msg = await _redis.GetAsync&lt;Message&gt;($"msg:{msgId}");
        if (msg != null &amp;&amp; msg.Timestamp &gt; DateTime.Now.AddMinutes(-2))
        {
            await _redis.PublishAsync("recall_message", msgId);
        }
    }
}

部署方案

组件部署方式推荐配置
通信网关Docker容器化4核8G,Kestrel并发5000+
Redis集群模式3主3从,持久化AOF
数据库主从复制16核32G,SSD RAID10
文件存储MinIO分布式集群4节点,10Gbps内网带宽

测试方案

压力测试

# 使用wrk进行压力测试
wrk -t12 -c400 -d30s http://localhost:5000/api/chat

监控指标

public class PerformanceMetrics
{
    public long MessagesSent { get; set; }
    public long MessagesReceived { get; set; }
    public double CPUUsage { get; set; }
    public double MemoryUsage { get; set; }
}

知识扩展

用 C# 搭建一个即时通讯应用,最推荐的方式是使用微软官方提供的 SignalR。它上手快、功能强大,能帮你省去很多底层的麻烦。下面是具体的实现思路:

第一步:创建项目并配置 SignalR

创建一个新的 ASP.NET Core Web 应用(使用 Razor 页面或 MVC 模型均可)。然后,在 Program.cs 文件中配置 SignalR 服务:

// Program.cs
using SignalRChat.Hubs;
var builder = WebApplication.CreateBuilder(args);
// 1. 添加 SignalR 服务
builder.Services.AddSignalR();
var app = builder.Build();
// ... 其他配置
// 2. 映射 Hub 的端点,客户端将通过 "/chatHub" 访问
app.MapHub<ChatHub>("/chatHub");
app.Run();

第二步:创建核心枢纽 (Hub)

在项目根目录下创建一个 Hubs 文件夹,并新建 ChatHub.cs 类。这是整个通信的核心中枢。

// Hubs/ChatHub.cs
using Microsoft.AspNetCore.SignalR;
namespace SignalRChat.Hubs;
// 继承 SignalR 的 Hub 基类
public class ChatHub : Hub
{
    // 这个方法将被客户端调用,以广播消息
    // Hub 是瞬时的,不要在字段中存储状态
    public async Task SendMessage(string user, string message)
    {
        // Clients.All 代表所有连接的客户端
        // "ReceiveMessage" 是客户端需要监听的方法名
        await Clients.All.SendAsync("ReceiveMessage", user, message);
    }
    // 你也可以在此添加其他方法,如点对点聊天、加群、退群等
    // public async Task SendPrivateMessage(string targetUserId, string message)
    // {
    //     await Clients.User(targetUserId).SendAsync("ReceiveMessage", ...);
    // }
}

要点:ChatHub 类继承自 SignalR 的 Hub 基类。你定义在其中的 public 方法,都可以被客户端直接调用。需要注意,每次调用都会创建一个新的 Hub 实例,所以请不要在它的字段里存储状态。

第三步:构建客户端界面

在 wwwroot 文件夹下创建一个 HTML 文件,例如 index.html,用来作为聊天界面。

<!-- wwwroot/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>SignalR 聊天室</title>
</head>
<body>
    <h2>SignalR 聊天室</h2>
    <div>用户: <input type="text" id="userInput" /> 消息: <input type="text" id="messageInput" /></div>
    <button id="sendButton">发送</button>
    <ul id="messagesList"></ul>
    <!-- 1. 引用 SignalR 的 JavaScript 客户端库 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js"></script>
    <script>
        "use strict";
        // 2. 创建连接对象,指定 Hub 的 URL
        const connection = new signalR.HubConnectionBuilder()
            .withUrl("/chatHub")
            .build();
        // 3. 设置消息接收器:监听来自 Hub 的 "ReceiveMessage" 调用
        // 这里的 "ReceiveMessage" 必须与 C# Hub 中 SendAsync 的第一个参数一致
        connection.on("ReceiveMessage", (user, message) => {
            const li = document.createElement("li");
            li.textContent = `${user}: ${message}`;
            document.getElementById("messagesList").appendChild(li);
        });
        // 4. 启动连接
        connection.start().catch(err => console.error(err.toString()));
        // 5. 页面逻辑:点击按钮时,调用 Hub 的 "SendMessage" 方法
        document.getElementById("sendButton").addEventListener("click", event => {
            const user = document.getElementById("userInput").value;
            const message = document.getElementById("messageInput").value;
            // 这里的 "SendMessage" 必须与 C# Hub 中的方法名完全一致
            connection.invoke("SendMessage", user, message).catch(err => console.error(err.toString()));
            event.preventDefault();
        });
    </script>
</body>
</html>

要点:客户端通过 HubConnectionBuilder 建立连接,并通过 .on() 监听来自服务器的消息,通过 .invoke() 调用服务器上的方法。最重要的,是确保 JavaScript 中的方法名和 C# Hub 里的方法名完全一致。

第四步:运行和测试

调整 Program.cs,确保应用能够提供这个静态 HTML 文件。然后运行项目,就可以在聊天室里畅聊了。

到此这篇关于基于C#实现一个简单的即时通讯工具的文章就介绍到这了,更多相关C#即时通讯工具内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文