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,请联系我们免费获取。
Avalonia 实现跨平台的IM即时通讯、语音视频通话(源码,支持信创国产OS,统信、银河麒麟)的更多相关文章
- iOS即时通讯之CocoaAsyncSocket源码解析五
接上篇:iOS即时通讯之CocoaAsyncSocket源码解析四 原文 前言: 本文为CocoaAsyncSocket Read篇终,将重点涉及该框架是如何利用缓冲区对数据进行读取. ...
- iOS即时通讯之CocoaAsyncSocket源码解析四
原文 前言: 本文为CocoaAsyncSocket源码系列中第二篇:Read篇,将重点涉及该框架是如何利用缓冲区对数据进行读取.以及各种情况下的数据包处理,其中还包括普通的.和基于TLS的不同读取操 ...
- iOS即时通讯之CocoaAsyncSocket源码解析二
原文 前言 本文承接上文:iOS即时通讯之CocoaAsyncSocket源码解析一 上文我们提到了GCDAsyncSocket的初始化,以及最终connect之前的准备工作,包括一些错误检查:本机地 ...
- iOS即时通讯之CocoaAsyncSocket源码解析一
申明:本文内容属于转载整理,原文连接 前言: CocoaAsyncSocket是谷歌的开发者,基于BSD-Socket写的一个IM框架,它给Mac和iOS提供了易于使用的.强大的异步套接字库,向上封装 ...
- 视酷即时通讯系统应用源码 V1.0
视酷即时通讯系统(原创),成熟稳定,拥有和微信一样强大的功能不再是梦,节省几个月研发时间迅速融合进项目中: 1.首家支持聊天室群聊 2.支持和微信一样的语音聊天,可以显示时长.未读状态,自动轮播未读语 ...
- MVC中使用SignalR打造酷炫实用的即时通讯功能附源码
前言,现在这世道写篇帖子没个前言真不好意思发出来.本贴的主要内容来自于本人在之前项目中所开发的一个小功能,用于OA中的即时通讯.由于当时走的太急,忘记把代码拿出来.想想这已经是大半年前的事情了,时间过 ...
- iOS即时通讯之CocoaAsyncSocket源码解析三
原文 前言 本文实例Github地址:即时通讯的数据粘包.断包处理实例. 本文旨以实例的方式,使用CocoaAsyncSocket这个框架进行数据封包和拆包.来解决频繁的数据发送下,导致的数据粘包.以 ...
- (转)即时通讯IM OpenFire源码学习之三:在Eclipse中构建源码
转:http://blog.csdn.net/huwenfeng_2011/article/details/43412617 源码搭建 下载地址: 地址:http://www.igniterealti ...
- C#中的WinFrom技术实现串口通讯助手(附源码)
C#中的WinFrom技术实现串口通讯助手(附源码) 实现的功能: 1.实现自动加载可用串口. 2.打开串口,并且使用C#状态栏显示串口的状态. 3.实现了串口的接收数据和发送数据功能. 4.串口 ...
- java Activiti6 工作流引擎 websocket 即时聊天 SSM源码 支持手机即时通讯聊天
即时通讯:支持好友,群组,发图片.文件,消息声音提醒,离线消息,保留聊天记录 (即时聊天功能支持手机端,详情下面有截图) 工作流模块---------------------------------- ...
随机推荐
- Angular: Error: NG0100: ExpressionChangedAfterItHasBeenChecked
错误原因 当变更检测完成后又更改了表达式的值时,Angular就会抛出ExpressionChangedAfterItHasBeenCheckedError 错误,Angular只会在开发模式下抛出此 ...
- 采用PCA算法&KMeans算法来实现用户对物品类别的喜好细分(菜篮子分析)(附带数据集下载)
实现该项目的流程如下 """ 项目:用户对物品类别的喜好细分(菜篮子分析) 主算法:PCA降维算法 KMeans算法 总思路 1.导包 2.获取数据 3.数据处理 5.特 ...
- 随风迎 jmeter下TPS插件的安装(转)
1.下载插件http://pan.baidu.com/s/1mioVJni 2.解压下载的安装包: 将 jpgc-graphs-basic-2.0.zip 解压缩后只有一个 lib 目录,该目录下有一 ...
- Wow: 基于 DDD、EventSourcing 的现代响应式 CQRS 架构微服务开发框架
领域驱动 | 事件驱动 | 测试驱动 | 声明式设计 | 响应式编程 | 命令查询职责分离 | 事件溯源 架构图 事件源 可观测性 OpenAPI (Spring WebFlux 集成) 自动注册 命 ...
- 微服务集成seata完成分布式事务,解决数据不一致问题
细心的盆友可能已经发现了,我们的跨行转账并没有保证数据一致性,比如小明扣除了100,但是因为各种问题小红在添加100金额的时候遇到了异常,这个时候数据就出现不一致性 我们可以选择seata来进行分布式 ...
- ELK环境部署-基础环境安装(一)
ELK简介 ElasticSearch工作原理以及专用名词 ELK是Elasticsearch(ES) , Logstash, Kibana的结合,是一个开源日志收集软件. Elasticsearch ...
- Go之流程控制大全: 细节、示例与最佳实践
本文深入探讨Go语言中的流程控制语法,包括基本的if-else条件分支.for循环.switch-case多条件分支,以及与特定数据类型相关的流程控制,如for-range循环和type-switch ...
- 树莓派4b装系统到运行 Blazor Linux 本地程序全记录
在Linux下运行gui程序,咱也是第一次做,属于是瞎子过河乱摸一通,写得有什么不对和可以优化的地方,希望各位看官斧正斧正. 1. 下载烧录器 https://www.raspberrypi.com/ ...
- Java 21 新特性:Record Patterns
Record Patterns 第一次发布预览是在JDK 19.随后又在JDK 20中进行了完善.现在,Java 21开始正式推出该特性优化.下面我们通过一个例子来理解这个新特性. record Po ...
- vue中watch侦听器,deep和immediate的用法
1.deep深度监听的用法 当监听一个对象时,可能想监听整个对象的变化,而不仅仅是某个属性.但在默认情况下,如果你正在监听formData对象并且修改了formData.username,对应的侦听器 ...