基于EasyTcp4Net开发一个功能较为完善的去持久化聊天软件

之前自己写了一篇介绍TCP的一些常用的功能介绍和特征,并且用代码做了示例,最终开发了一个EasyTcp4Net的TCP工具库,其最大的特色就是使用了微软提供的高性能库中的一些数据结构来处理TCP数据。

最近辞职待业在家,也没啥事做,就利用自己写的TCP通讯库基础上开发了一个示例的聊天程序,功能包括,文本发送,图片发送,断线重连,消息发送成功确认,消息发送失败提示等。

示例

发消息给自己
收到消息
发送图片
消息发送中
重连中
发送失败

数据包结构以及拆包

定义数据包结构

数据包结构定义了每次发送一个数据的完整的数据结构,我们将包体长度定义在包头中来解决粘包和断包的问题。

数据包我们采用了简单的序列化成byte数组的方式来发送。

复制代码
[StructLayout(LayoutKind.Sequential)]
public class Message<TBody> : IMsssage<TBody> where TBody : Packet
{
    //数据包包体长度 4字节
    public int BodyLength { get; private set; }
    //消息类型 4字节
    public MessageType MessageType { get; set; }
    public TBody? Body { get; set; }
    private Message<TBody> Deserialize(byte[] bodyData)
    {
        var bodyStr = System.Text.Encoding.Default.GetString(bodyData);
        Body = JsonSerializer.Deserialize<TBody>(bodyStr);

        return this;
    }

    public static Message<TBody> FromBytes(ReadOnlyMemory<byte> data)
    {
        Message<TBody> packet = new Message<TBody>();
        packet.BodyLength = BinaryPrimitives.ReadInt32BigEndian(data.Slice(0, 4).Span);
        packet.MessageType = (MessageType)BinaryPrimitives.ReadInt32BigEndian(data.Slice(4, 4).Span);
        packet.Deserialize(data.Slice(8, packet.BodyLength).Span.ToArray());

        return packet;
    }

    public byte[] Serialize()
    {
        var Length = 4 + 4;
        var bodyArray = System.Text.Encoding.Default.GetBytes(JsonSerializer.Serialize(Body));
        BodyLength = bodyArray.Length;
        Length += bodyArray.Length;
        byte[] result = new byte[Length];
        result.AddInt32(0, bodyArray.Length);
        result.AddInt32(4, (int)MessageType);
        Buffer.BlockCopy(bodyArray, 0, result, 8, bodyArray.Length);

        return result;
    }

    public TBody GetBody()
    {
        return Body;
    }
}

public interface IMsssage <out TBody> where TBody : Packet
{
    public TBody GetBody();
}

我们在服务端和客户端根据我们定义的数据结构,来调用EasyTcp4Net提供的固定包头来解析数据包

复制代码
_easyTcpClient.SetReceiveFilter(new FixedHeaderPackageFilter(8, 0, 4, false));

文本/图片发送

我们可以定义消息基类,再拓展两个消息类,一个文本消息,一个图片消息

复制代码
public class SendMessagePacket : Packet
{
    public string MessageId { get; set; } = Guid.NewGuid().ToString();
    public string From { get; set; }
    public string To { get; set; }
}

图片消息

复制代码
public class SendImageMessagePacket : SendMessagePacket
{
    public byte[] Data { get; set; }
    public string FileName { get; set; }
}

文本消息

复制代码
public class SendTextMessagePacket : SendMessagePacket
{
    public string Text { get; set; }
}

我们还需要在界面中增加相关的文本和图片的ViewModel

发送消息的时候,发送者可以立刻将消息添加到聊天界面,然后等待收到自己发送的消息从服务端发来的时候,根据状态判断消息是否发送成功,等待的时候可以将消息设置发送中的界面状态显示,这种发送消息逻辑和微信基本一致。

断线处理

利用EasyTcp4Net提供的断线的事件,可以非常方便的在服务端知道客户端突然断开了,或者在客户端知道和服务端连接断开了。

客户端

复制代码
_easyTcpClient.OnDisConnected += async (obj, e) =>
{
    Title = Title + _disConnectTip;
    await ReConnectAsync();
};

主要是触发了重连的机制。

服务端

复制代码
 _easyTcpServer.OnClientConnectionChanged += (obj, e) =>
 {
     if (e.Status == ConnectsionStatus.DisConnected)
     {
         _accounts.TryRemove(e.ClientSession.SessionId, out var account);
     }
 };

主要是将该用户从在线列表中移除。

总结

总体来说做一个聊天软件需要考虑的细节比较多。

示例地址:https://github.com/BruceQiu1996/EasyChat

相关推荐
快乐点吧20 分钟前
【Word】批注一键导出:VBA 宏
开发语言·c#·word
Dm_dotnet32 分钟前
为Avalonia应用添加图标
c#
Rabbb1 小时前
C# 切割数组的Linq扩展方法 Period,PeriodBy
后端·c#
阿ฅ( ̳• ε • ̳)ฅ2 小时前
C#窗体应用程序连接数据库
开发语言·数据库·c#
勘察加熊人7 小时前
wpf+c#路径迷宫鼠标绘制
开发语言·c#·wpf
OneByOneDotNet7 小时前
WPF 教程:给 TreeView 添加 SelectedItem 双向绑定支持(MVVM-Friendly)
wpf
小黄人软件8 小时前
C# ini文件全自动界面配置:打开界面时读ini配置到界面各控件,界面上的控件根据ini文件内容自动生成,点保存时把界面各控件的值写到ini里。
开发语言·c#
gc_229913 小时前
C#测试Excel开源组件ExcelDataReader
c#·excel·exceldatareader
勘察加熊人15 小时前
c#使用forms实现helloworld和login登录
开发语言·c#
我不是程序猿儿16 小时前
【C#】设备回帧太快、数据没收完整就被读取,导致帧被拆、混、丢,很常见,尤其在高频通信设备,解决方案
开发语言·c#