Avalonia 实现跨平台的IM即时通讯、语音视频通话(源码,支持信创国产OS,统信、银河麒麟)

在 Avalonia 如火如荼的现在,之前使用CPF实现的简单IM,非常有必要基于 Avalonia 来实现了。Avalonia 在跨平台上的表现非常出色,对信创国产操作系统(像银河麒麟、统信UOS、Deepin等)也很不错。

现在,我们就来使用 Avalonia 实现一个跨平台的简单IM,除了文字聊天外,还可以语音视频通话。废话不多说,我们开始吧!

下图是这个简单IM的Avalonia客户端在国产统信UOS上的运行的截图:

一. IM 即时通讯系统主要功能

这个简单的IM系统实现了以下功能:

1.基础功能、文字聊天

(1)客户端用户上下线时,通知其他在线用户。

(2)当客户端与服务端网络断开时,进行自动重连,当网络恢复后,重连成功。

(3)所有在线用户之间可以进行文字聊天(支持表情,支持撤回消息、删除消息)。

(4)文件传送。

2.语音视频聊天、远程桌面

(1)一方发起视频对话请求,对方同意后,即可开始视频对话。

(2)在对话的过程中,任何一方都可以挂断,以终止对话。

(3)在对话的过程中,任何一方掉线,都会自动终止对话。

(4)双击视频窗口,会全屏显示视频,按esc退出全屏。

(5)远程桌面或远程协助功能,也是跟视频聊天同样的流程,不再赘述。

二.开发环境

1.开发工具:

Visual Studio 2022

2. 开发框架:

.NET Core 3.1

3.开发语言:

C#

4.其它框架:

Avalonia UI 框架(版本:0.10.22)、ESFramework 通信框架 (版本:7.2)

注:建议 Avalonia 使用0.10.*的版本,精简而且很稳定,而最新的11.0的版本太庞大了。

三.具体实现

下面我们讲一下Demo中核心的代码实现,大家从文末下载源码并对照着源码看,会更清楚些。

1.自定义消息类型 InformationTypes

若要实现上述功能列表中列出来的所有功能,我们先要定义相应的通信消息的消息类型,如下所示:

复制代码
    public static class InformationTypes
    {
        /// <summary>
        /// 文字(表情)聊天信息
        /// </summary>
        public const int TextChat = 0;

        /// <summary>
        /// 文字(表情)聊天信息 (由服务端转发给消息接收方)
        /// </summary>
        public const int TextChat4Transit = 1;

        /// <summary>
        /// 图片聊天信息
        /// </summary>
        public const int ImageChat = 2;

        /// <summary>
        /// 收到消息发送者 撤回消息请求
        /// </summary>
        public const int RecallMsg = 3;

        /// <summary>
        /// 客户端异步调用服务端
        /// </summary>
        public const int ClientSyncCallServer = 4;

        /// <summary>
        /// 视频请求 5
        /// </summary>
        public const int VideoRequest = 5;

        /// <summary>
        /// 回复视频请求的结果 6
        /// </summary>
        public const int VideoResult = 6;

        /// <summary>
        /// 通知对方 挂断 视频连接 7
        /// </summary>
        public const int CloseVideo = 7;

        /// <summary>
        /// 通知好友 网络原因,导致 视频中断 8
        /// </summary>
        public const int NetReasonCloseVideo = 8;

        /// <summary>
        /// 通知对方(忙线中) 挂断 视频连接 9
        /// </summary>
        public const int BusyLine = 9;

        /// <summary>
        /// 收到远程协助请求
        /// </summary>
        public const int AssistReceive = 10;

        /// <summary>
        /// 协助方拒绝远程协助
        /// </summary>
        public const int AssistGusetReject = 11;

        /// <summary>
        /// 协助方同意远程协助
        /// </summary>
        public const int AssistGusetAgree = 12;

        /// <summary>
        /// 请求方关闭远程协助
        /// </summary>
        public const int AssistOwnerClose = 13;

        /// <summary>
        /// 协助方关闭远程协助
        /// </summary>
        public const int AssistGusetClose = 14;
    }

在约定好消息类型之后,我们就可以实现业务逻辑功能了。

2.定义协议类

信息类型定义好后,我们接下来定义信息协议。

对于聊天消息(InformationTypes.EmotionTextChat),专门定义了一个协议类:ChatMessageRecord。

复制代码
    public class ChatMessageRecord
    {
        public string Guid { get; set; }

        public DateTime MessageTime { get; set; }

        public string SpeakerID { get; set; }

        public string ListenerID { get; set; }

        public ChatMessageType ChatMessageType { get; set; }

        public string ContentStr { get; set; }

        public byte[] ImgData { get; set; }

        public string FilePath { get; set; }
    }

对于同步调用(InformationTypes.ClientSyncCallServer),我们示例的是向服务器请求加法运算的结果,协议类用的是MathModel。

3.实现自定义信息处理器

客户端的MainForm实现了ICustomizeHandler接口,其主要实现HandleInformation方法,来处理收到的聊天信息和振动提醒。

复制代码
  void HandleInformation(string sourceUserID, int informationType, byte[] info);

服务端的CustomizeHandler实现了服务端的ICustomizeHandler接口,其主要实现HandleQuery方法来处理来自客户端的同步调用(InformationTypes.ClientCallServer)。

复制代码
  byte[] HandleQuery(string sourceUserID, int informationType, byte[] info);

4.服务端验证用户登录的帐号

服务端的BasicHandler类实现IBasicHandler接口,以验证登录用户的账号密码。

复制代码
    public class BasicHandler : IBasicHandler
    { 
        /// <summary>
        /// 此处验证用户的账号和密码。返回true表示通过验证。
        /// </summary>  
        public bool VerifyUser(ClientType clientType, string systemToken, string userID, string password, out string failureCause)
        {
            failureCause = "";
            return true;
        }

        public string HandleQueryBeforeLogin(AgileIPE clientAddr, int queryType, string query)
        {
            return "";
        }
    }

本demo中,假设所有的验证都通过,所以验证方法直接返回true。

5.客户端实现文字聊天功能

通过IRapidPassiveEngine的 CustomizeOutter 的 Send 方法来发送文字表情聊天消息。

在发送文字聊天消息时,有两个发送按钮,"发送1"和"发送2",分别演示了两种发送消息给对方的方式:

(1)直接发给对方。(若P2P通道存在,则经由P2P通道发送)

复制代码
        internal static void SendTextMsgToClient(ChatMessageRecord record)
        {
            try
            {
                string cont = JsonConvert.SerializeObject(record);
                byte[] recordInfo = Encoding.UTF8.GetBytes(cont);
                //使用Tag携带 接收者的ID
                App.PassiveEngine.CustomizeOutter.Send(record.ListenerID, InformationTypes.TextChat4Transit, recordInfo);
            }
            catch (Exception e)
            {
                logger.Log(e, "GlobalHelper.SendTextMsgToClient", ErrorLevel.Standard);
            }
        }

聊天消息 ChatMessageRecord 对象先由JSON序列化成字符串,然后在使用UTF-8转成字节数组,然后通过通信引擎的CustomizeOutter发送出去。

(2)先发给服务器,再由服务器转发给对方。

具体实现,大家去参看源码,这里就不再赘述了。

6.客户端实现语音视频通话功能

语音视频通话实际运行起来后的效果如下所示:

我们先简单描述一下实现视频对话流程的要点,更详细的细节请查阅源代码。

(1)发起方发送InformationTypes.VideoRequest类型的信息给对方,以请求视频对话。

程序中是在 VideoChatWindow 窗口显示的时候,来做这件事的:

复制代码
        protected override void OnInitialized()
        {
            base.OnInitialized(); 
            this.SetWindowStats();
            if (!this.IsWorking)
            {
                VideoController.Singleton.SendMessage(this.DestID, InformationTypes.VideoRequest, null);
                CommonHelper.AddSystemMsg(this.DestID, "向对方发起视频通话邀请");
            }
        }

(2)接收方收到请求后,界面提示用户是同意还是拒绝,用户选择后,将发送InformationTypes.VideoResult类型的信息给请求方,信息的内容是一个bool值,true表示同意,false表示拒绝。

(3)发起方收到回复,如果回复为拒绝,则界面给出对应的提示;如果回复为同意,则进入(4)。

(4)先说接收方,如果同意视频,则发送回复后,立即调用DynamicCameraConnector和MicrophoneConnector的Connect方法,连接到对方的摄像头、麦克风。

复制代码
        internal void BeginConnect()
        {
            UiSafeInvoker.ActionOnUI(() =>
            {
                string tip = this.IsWorking ? "已同意对方的视频通话" : "对方同意了你的视频通话请求";
                CommonHelper.AddSystemMsg(this.DestID, tip);
                this.IsWorking = true;
                this.NotifyOther = true;
                this.Title = this.title.Text = this.RepeatedCallTip(false);
                this.startTime = DateTime.Now;
                this.timer.Start();
                this.otherCamera.Core.DisplayVideoParameters = true;
                this.otherCamera.Core.VideoDrawMode = VideoDrawMode.ScaleToFill;
                this.otherCamera.Core.ConnectEnded += DynamicCameraConnector_ConnectEnded;
                this.otherCamera.Core.Disconnected += DynamicCameraConnector_Disconnected;
                this.microphoneConnector.ConnectEnded += MicrophoneConnector_ConnectEnded;
                this.microphoneConnector.Disconnected += MicrophoneConnector_Disconnected;
                this.otherCamera.BeginConnect(this.DestID);
                this.microphoneConnector.BeginConnect(this.DestID);
            });
        }

(5)对于发起方,当收到对方同意的回复后,也立即调用DynamicCameraConnector和MicrophoneConnector的Connect方法,连接到接收方的摄像头、麦克风。

(6)当一方点击挂断的按钮时,就会发送InformationTypes.CloseVideo类型的信息给对方,并调用DynamicCameraConnector和MicrophoneConnector的Disconnect方法断开到对方设备的连接。

(7)另一方接收到InformationTypes.CloseVideo类型的信息时,也会调用DynamicCameraConnector和MicrophoneConnector的Disconnect方法以断开连接。

复制代码
        protected override void OnClosing(CancelEventArgs e)
        {
            base.OnClosing(e);
            this.otherCamera?.Disconnect();
            this.otherCamera?.Dispose();

            this.microphoneConnector?.Disconnect();
            this.microphoneConnector?.Dispose();

            this.selfCamera?.Disconnect();
            this.selfCamera?.Dispose();
        }

(8)如果接收到自己掉线的事件或好友掉线的事件,也采用类似挂断对话的处理。

四.下载

Avalonia 版本即时通讯源码:IM_VideoChat.Avalonia.rar

该源码中包括如下项目:

(1)Oraycn.Demos.VideoChat.LinuxServer : 该Demo的Linux服务端(基于.NetCore)。

(2)Oraycn.Demos.VideoChat.ClientAvalonia : 该Demo的 Avalonia 客户端。

注: Linux客户端内置的是x86/x64非托管so库,若需要其它架构的so,请联系我们免费获取。

相关推荐
向上的车轮19 小时前
信创入门指南:一文掌握信息技术应用创新的核心要点
信创
金仓拾光集1 天前
筑牢风控生命线:金仓数据库替代MongoDB,重构证券融资融券业务的数据基石
数据库·mongodb·信创·1024程序员节·kingbasees·国产化替代
hellojackjiang20116 天前
全面适配iOS 26液态玻璃,基于开源IM即时通讯框架MobileIMSDK:RainbowChat-iOS端v10.2发布
ios·网络编程·即时通讯·im开发·rainbowchat
千桐科技6 天前
qData 数据中台在 ARM 架构与信创环境下的兼容性与适配研究
信创·国产化·大数据平台·arm架构·qdata·开源数据中台·千数平台
未来之窗软件服务17 天前
操作系统应用开发(二十七)网页访问远程桌面—东方仙盟筑基期
远程桌面·rustdesk·仙盟创梦ide·东方仙盟
未来之窗软件服务20 天前
操作系统应用开发(二十三)RustDesk ng反向代理—东方仙盟筑基期
服务器·远程桌面·仙盟创梦ide·东方仙盟·rustdek
未来之窗软件服务20 天前
操作系统应用开发(二十五)RustDesk 502错误—东方仙盟筑基期
网络协议·远程桌面·仙盟创梦ide·东方仙盟·rustdek
未来之窗软件服务20 天前
操作系统应用开发(二十四)RustDesk 404错误—东方仙盟筑基期
服务器·远程桌面·仙盟创梦ide·东方仙盟·rustdek
一粒马豆22 天前
flask_socketio+pyautogui实现的具有加密传输功能的极简远程桌面
python·flask·pyautogui·远程桌面·flask_socketio
未来之窗软件服务22 天前
操作系统应用开发(十四)RustDesk服务器配置——东方仙盟筑基期
运维·服务器·远程桌面·rustdesk·仙盟创梦ide·东方仙盟