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

相关推荐
wuguan_20 小时前
C#中的静态成员、常量和只读变量
开发语言·c#
秋雨雁南飞20 小时前
C# SignalR 添加Swagger
c#·swagger·signalr
张人玉20 小时前
C# 与西门子 PLC 通信:地址相关核心知识点
开发语言·microsoft·c#·plc
Yuuuuuuu21 小时前
WPF基于Canvas绘制多边形ROI
c#
缺点内向21 小时前
如何在 C# 中重命名 Excel 工作表并设置标签颜色
开发语言·c#·excel
a努力。1 天前
网易Java面试被问:偏向锁在什么场景下反而降低性能?如何关闭?
java·开发语言·后端·面试·架构·c#
专注VB编程开发20年1 天前
c#语法和java相差多少
java·开发语言·microsoft·c#
SmoothSailingT1 天前
C#——Lazy<T>懒加载机制
开发语言·单例模式·c#·懒加载
czhc11400756631 天前
c# 1216
windows·microsoft·c#
幸存者letp1 天前
为什么 max(words, key=len) 中需要传 key=len
服务器·开发语言·c#