基于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

相关推荐
__water8 分钟前
14_音乐播放服务_字典缓存避免重复加载
单例模式·c#·unity6000·字段缓存·audiosource
AitTech1 小时前
C#编程:List.ForEach与foreach循环的深度对比
开发语言·c#·list
军训猫猫头2 小时前
56.命令绑定 C#例子 WPF例子
开发语言·c#·wpf
MasterNeverDown2 小时前
WPF 使用iconfont
hadoop·ui·wpf
小唐C++4 小时前
C++小病毒-1.0勒索
开发语言·c++·vscode·python·算法·c#·编辑器
菜鸟记录4 小时前
C#AWS signatureV4对接Amazon接口
c#·aws·amazon·aksk
上位机付工5 小时前
浅谈单例模式
开发语言·c#
步、步、为营5 小时前
从0到1:.NET Core微服务的Docker容器奇幻冒险
微服务·c#·asp.net·.net·.netcore
xcLeigh5 小时前
WPF基础 | WPF 常用控件实战:Button、TextBox 等的基础应用
c#·wpf
Maybe_ch6 小时前
Blazo-Blazor Web App项目结构
c#·blazor