XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEExtension

【DLL】XFE各类拓展是一个C#的DLL库,旨在优化C#代码中常用语句的使用,并提供更简洁的访问方式,同时提供Xunit测试框架,快速搭建服务器/客户端,免费ChatGPTAPI接口,免费通讯服务器,XFE下载器,新增格式等

公开
关注 0 Fork 0 Star 0
UTF-8
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using XFEExtension.ArrayExtension;
using XFEExtension.BufferExtension;
using XFEExtension.DelegateExtension;
using XFEExtension.FormatExtension;
using XFEExtension.TaskExtension;

namespace XFEExtension.CyberComm.XCCNetWork
{
    class XCCNetWorkBase
    {
        /// <summary>
        /// 明文消息接收时触发
        /// </summary>
        public EventHandler<XCCTextMessageReceivedEventArgs> textMessageReceived;
        /// <summary>
        /// 二进制消息接收时触发
        /// </summary>
        public EventHandler<XCCBinaryMessageReceivedEventArgs> binaryMessageReceived;
        /// <summary>
        /// 异常消息接收时触发
        /// </summary>
        public EventHandler<XCCExceptionMessageReceivedEventArgs> exceptionMessageReceived;
        /// <summary>
        /// 连接关闭时触发
        /// </summary>
        public EventHandler<XCCConnectionClosedEventArgs> connectionClosed;
        /// <summary>
        /// 连接成功时触发
        /// </summary>
        public EventHandler<XCCConnectedEventArgs> connected;
    }
    /// <summary>
    /// XCC客户端连接类型
    /// </summary>
    public enum XCCClientType
    {
        /// <summary>
        /// 明文类型
        /// </summary>
        TextMessageClient,
        /// <summary>
        /// 文件类型
        /// </summary>
        FileTransportClient
    }
    /// <summary>
    /// XCC网络通讯
    /// </summary>
    public class XCCNetWork
    {
        private readonly XCCNetWorkBase xCCNetWorkBase;
        /// <summary>
        /// XCC当前群组
        /// </summary>
        public List<XCCGroup> Groups { get; set; }
        /// <summary>
        /// 明文消息接收时触发
        /// </summary>
        public event EventHandler<XCCTextMessageReceivedEventArgs> TextMessageReceived
        {
            add
            {
                xCCNetWorkBase.textMessageReceived += value;
            }
            remove
            {
                xCCNetWorkBase.textMessageReceived -= value;
            }
        }
        /// <summary>
        /// 二进制消息接收时触发
        /// </summary>
        public event EventHandler<XCCBinaryMessageReceivedEventArgs> BinaryMessageReceived
        {
            add
            {
                xCCNetWorkBase.binaryMessageReceived += value;
            }
            remove
            {
                xCCNetWorkBase.binaryMessageReceived -= value;
            }
        }
        /// <summary>
        /// 异常消息接收时触发
        /// </summary>
        public event EventHandler<XCCExceptionMessageReceivedEventArgs> ExceptionMessageReceived
        {
            add
            {
                xCCNetWorkBase.exceptionMessageReceived += value;
            }
            remove
            {
                xCCNetWorkBase.exceptionMessageReceived -= value;
            }
        }
        /// <summary>
        /// 连接关闭时触发
        /// </summary>
        public event EventHandler<XCCConnectionClosedEventArgs> ConnectionClosed
        {
            add
            {
                xCCNetWorkBase.connectionClosed += value;
            }
            remove
            {
                xCCNetWorkBase.connectionClosed -= value;
            }
        }
        /// <summary>
        /// 连接成功时触发
        /// </summary>
        public event EventHandler<XCCConnectedEventArgs> Connected
        {
            add
            {
                xCCNetWorkBase.connected += value;
            }
            remove
            {
                xCCNetWorkBase.connected -= value;
            }
        }
        /// <summary>
        /// 创建XCC群组会话
        /// </summary>
        /// <param name="groupId">群组名称</param>
        /// <param name="sender">发送者</param>
        /// <returns></returns>
        public XCCGroup CreateGroup(string groupId, string sender)
        {
            var group = new XCCGroupImpl(Guid.NewGuid().ToString(), groupId, sender, xCCNetWorkBase);
            Groups.Add(group);
            return group;
        }
        /// <summary>
        /// XCC网络通信会话
        /// </summary>
        public XCCNetWork()
        {
            xCCNetWorkBase = new XCCNetWorkBase();
            Groups = new List<XCCGroup>();
        }
    }
    /// <summary>
    /// XCC群组
    /// </summary>
    public abstract class XCCGroup
    {
        private event EndTaskTrigger<bool> UpdateTaskTrigger;
        private readonly XCCNetWorkBase workBase;
        private int reconnectTimes = -1;
        private bool readyToClose = false;
        #region 公有属性
        /// <summary>
        /// 客户端标识名
        /// </summary>
        public string Signature { get; }
        /// <summary>
        /// 群组ID
        /// </summary>
        public string GroupId { get; }
        /// <summary>
        /// 发送者
        /// </summary>
        public string Sender { get; }
        /// <summary>
        /// 明文传输服务器是否连接
        /// </summary>
        public bool TextMessageClientConnected { get; private set; } = false;
        /// <summary>
        /// 文件传输服务器是否连接
        /// </summary>
        public bool FileTransportClientConnected { get; private set; } = false;
        /// <summary>
        /// WebSocket明文传输客户端
        /// </summary>
        public ClientWebSocket TextMessageClientWebSocket { get; private set; }
        /// <summary>
        /// WebSocket文件传输客户端
        /// </summary>
        public ClientWebSocket FileTransportClientWebSocket { get; private set; }
        #endregion
        #region 公有方法
        /// <summary>
        /// 启动XCC会话
        /// </summary>
        /// <param name="autoReconnect">是否自动重连</param>
        /// <param name="reconnectMaxTimes">最大重连次数,-1则为无限次</param>
        /// <param name="reconnectTryDelay">重连尝试延迟</param>
        /// <returns></returns>
        public async Task StartXCC(bool autoReconnect = true, int reconnectMaxTimes = -1, int reconnectTryDelay = 100)
        {
            var textMessageXCCTask = StartTextMessageXCC(autoReconnect, reconnectMaxTimes, reconnectTryDelay);
            var fileTransportXCCTask = StartFileTransportXCC(autoReconnect, reconnectMaxTimes, reconnectTryDelay);
            await Task.WhenAll(textMessageXCCTask, fileTransportXCCTask);
        }
        /// <summary>
        /// 启动XCC文本会话
        /// </summary>
        /// <param name="autoReconnect">是否自动重连</param>
        /// <param name="reconnectMaxTimes">最大重连次数,-1则为无限次</param>
        /// <param name="reconnectTryDelay">重连尝试延迟</param>
        /// <returns></returns>
        public async Task StartTextMessageXCC(bool autoReconnect = true, int reconnectMaxTimes = -1, int reconnectTryDelay = 100)
        {
        XCCReconnect:
            TextMessageClientWebSocket = new ClientWebSocket();
            Uri serverUri = new Uri("ws://xcc.api.xfegzs.com");
            var base64GroupId = Convert.ToBase64String(Encoding.UTF8.GetBytes(GroupId));
            var base64SenderId = Convert.ToBase64String(Encoding.UTF8.GetBytes(Sender));
            TextMessageClientWebSocket.Options.SetRequestHeader("Group", base64GroupId);
            TextMessageClientWebSocket.Options.SetRequestHeader("Sender", base64SenderId);
            TextMessageClientWebSocket.Options.SetRequestHeader("Type", "Text");
            TextMessageClientWebSocket.Options.SetRequestHeader("Signature", Signature);
            reconnectTimes++;
            try
            {
                if (TextMessageClientWebSocket.State != WebSocketState.Open)
                    await TextMessageClientWebSocket.ConnectAsync(serverUri, CancellationToken.None);
            }
            catch (Exception ex)
            {
                if (readyToClose)
                {
                    return;
                }
                if (TextMessageClientConnected == true)
                {
                    workBase.connectionClosed?.Invoke(this, new XCCConnectionClosedEventArgsImpl(this, XCCClientType.TextMessageClient, TextMessageClientWebSocket, FileTransportClientWebSocket, false));
                }
                TextMessageClientConnected = false;
                if (autoReconnect)
                {
                    if (reconnectTimes <= reconnectMaxTimes || reconnectMaxTimes == -1)
                    {
                        Thread.Sleep(reconnectTryDelay);
                        goto XCCReconnect;
                    }
                }
                else
                {
                    workBase.exceptionMessageReceived?.Invoke(this, new XCCExceptionMessageReceivedEventArgsImpl(this, TextMessageClientWebSocket, FileTransportClientWebSocket, XCCClientType.TextMessageClient, null, null, new XFECyberCommException("与XCC网络通讯明文服务器建立连接时发生异常", ex)));
                    return;
                }
            }
            reconnectTimes = 0;
            TextMessageClientConnected = true;
            workBase.connected?.Invoke(this, new XCCConnectedEventArgsImpl(this, XCCClientType.TextMessageClient, TextMessageClientWebSocket, FileTransportClientWebSocket));
            while (TextMessageClientWebSocket.State == WebSocketState.Open)
            {
                try
                {
                    byte[] receiveBuffer = new byte[1024];
                    WebSocketReceiveResult receiveResult = await TextMessageClientWebSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                    var bufferList = new List<byte>();
                    bufferList.AddRange(receiveBuffer.Take(receiveResult.Count));
                    //ReceiveCompletedMessageByUsingWhile
                    while (!receiveResult.EndOfMessage)
                    {
                        receiveResult = await TextMessageClientWebSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                        bufferList.AddRange(receiveBuffer.Take(receiveResult.Count));
                    }
                    var receivedBinaryBuffer = bufferList.ToArray();
                    if (receiveResult.MessageType == WebSocketMessageType.Text)
                    {
                        try
                        {
                            var receivedMessage = Encoding.UTF8.GetString(receivedBinaryBuffer);
                            var isHistory = receivedMessage.IndexOf("[XCCGetHistory]") == 0;
                            if (isHistory)
                            {
                                receivedMessage = receivedMessage.Substring(15);
                            }
                            var unPackedMessage = receivedMessage.ToXFEArray<string>();
                            var messageId = unPackedMessage[0];
                            var signature = unPackedMessage[1];
                            var message = unPackedMessage[2];
                            var senderName = unPackedMessage[3];
                            var sendTime = DateTime.Parse(unPackedMessage[4]);
                            var messageType = XCCTextMessageType.Text;
                            switch (signature)
                            {
                                case "[XCCTextMessage]":
                                    messageType = XCCTextMessageType.Text;
                                    break;
                                case "[XCCImage]":
                                    messageType = XCCTextMessageType.Image;
                                    break;
                                case "[XCCAudio]":
                                    messageType = XCCTextMessageType.Audio;
                                    break;
                                case "[XCCVideo]":
                                    messageType = XCCTextMessageType.Video;
                                    break;
                                default:
                                    break;
                            }
                            workBase.textMessageReceived?.Invoke(this, new XCCTextMessageReceivedEventArgsImpl(this, TextMessageClientWebSocket, FileTransportClientWebSocket, XCCClientType.TextMessageClient, messageId, messageType, message, senderName, sendTime, isHistory));
                        }
                        catch (Exception ex)
                        {
                            workBase.exceptionMessageReceived?.Invoke(this, new XCCExceptionMessageReceivedEventArgsImpl(this, TextMessageClientWebSocket, FileTransportClientWebSocket, XCCClientType.TextMessageClient, null, null, new XFECyberCommException("接收XCC服务器消息时发生异常", ex)));
                        }
                    }
                    else if (receiveResult.MessageType == WebSocketMessageType.Binary)
                    {
                        try
                        {
                            var xFEBuffer = XFEBuffer.ToXFEBuffer(receivedBinaryBuffer);
                            var signature = Encoding.UTF8.GetString(xFEBuffer["Type"]);
                            var messageId = Encoding.UTF8.GetString(xFEBuffer["ID"]);
                            if (signature == "callback")
                                UpdateTaskTrigger?.Invoke(true, messageId);
                        }
                        catch (Exception ex)
                        {
                            workBase.exceptionMessageReceived?.Invoke(this, new XCCExceptionMessageReceivedEventArgsImpl(this, TextMessageClientWebSocket, FileTransportClientWebSocket, XCCClientType.TextMessageClient, null, null, new XFECyberCommException("接收XCC服务器消息时发生异常", ex)));
                        }
                    }
                }
                catch (Exception ex)
                {
                    try { await TextMessageClientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Close", CancellationToken.None); } catch { }
                    if (TextMessageClientConnected == true)
                    {
                        workBase.connectionClosed?.Invoke(this, new XCCConnectionClosedEventArgsImpl(this, XCCClientType.TextMessageClient, TextMessageClientWebSocket, FileTransportClientWebSocket, false));
                    }
                    TextMessageClientConnected = false;
                    if (autoReconnect)
                    {
                        if (autoReconnect)
                        {
                            Thread.Sleep(reconnectTryDelay);
                            if (reconnectTimes <= reconnectMaxTimes || reconnectMaxTimes == -1)
                                goto XCCReconnect;
                        }
                    }
                    else
                    {
                        workBase.exceptionMessageReceived?.Invoke(this, new XCCExceptionMessageReceivedEventArgsImpl(this, TextMessageClientWebSocket, FileTransportClientWebSocket, XCCClientType.TextMessageClient, null, null, new XFECyberCommException("与XCC网络通讯服务器建立连接时发生异常", ex)));
                        return;
                    }
                }
            }
            workBase.connectionClosed?.Invoke(this, new XCCConnectionClosedEventArgsImpl(this, XCCClientType.TextMessageClient, TextMessageClientWebSocket, FileTransportClientWebSocket, true));
        }
        /// <summary>
        /// 启动XCC文件传输会话
        /// </summary>
        /// <param name="autoReconnect">是否自动重连</param>
        /// <param name="reconnectMaxTimes">最大重连次数,-1则为无限次</param>
        /// <param name="reconnectTryDelay">重连尝试延迟</param>
        /// <returns></returns>
        public async Task StartFileTransportXCC(bool autoReconnect = true, int reconnectMaxTimes = -1, int reconnectTryDelay = 100)
        {
        XCCReconnect:
            FileTransportClientWebSocket = new ClientWebSocket();
            Uri serverUri = new Uri("ws://xcc.api.xfegzs.com");
            var base64GroupId = Convert.ToBase64String(Encoding.UTF8.GetBytes(GroupId));
            var base64SenderId = Convert.ToBase64String(Encoding.UTF8.GetBytes(Sender));
            FileTransportClientWebSocket.Options.SetRequestHeader("Group", base64GroupId);
            FileTransportClientWebSocket.Options.SetRequestHeader("Sender", base64SenderId);
            FileTransportClientWebSocket.Options.SetRequestHeader("Type", "File");
            FileTransportClientWebSocket.Options.SetRequestHeader("Signature", Signature);
            reconnectTimes++;
            try
            {
                if (FileTransportClientWebSocket.State != WebSocketState.Open)
                    await FileTransportClientWebSocket.ConnectAsync(serverUri, CancellationToken.None);
            }
            catch (Exception ex)
            {
                if (readyToClose)
                {
                    return;
                }
                if (FileTransportClientConnected == true)
                {
                    workBase.connectionClosed?.Invoke(this, new XCCConnectionClosedEventArgsImpl(this, XCCClientType.FileTransportClient, TextMessageClientWebSocket, FileTransportClientWebSocket, false));
                }
                FileTransportClientConnected = false;
                if (autoReconnect)
                {
                    if (reconnectTimes <= reconnectMaxTimes || reconnectMaxTimes == -1)
                    {
                        Thread.Sleep(reconnectTryDelay);
                        goto XCCReconnect;
                    }
                }
                else
                {
                    workBase.exceptionMessageReceived?.Invoke(this, new XCCExceptionMessageReceivedEventArgsImpl(this, TextMessageClientWebSocket, FileTransportClientWebSocket, XCCClientType.FileTransportClient, null, null, new XFECyberCommException("与XCC网络通讯明文服务器建立连接时发生异常", ex)));
                    return;
                }
            }
            reconnectTimes = 0;
            FileTransportClientConnected = true;
            workBase.connected?.Invoke(this, new XCCConnectedEventArgsImpl(this, XCCClientType.FileTransportClient, TextMessageClientWebSocket, FileTransportClientWebSocket));
            while (FileTransportClientWebSocket.State == WebSocketState.Open)
            {
                try
                {
                    byte[] receiveBuffer = new byte[1024];
                    WebSocketReceiveResult receiveResult = await FileTransportClientWebSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                    var bufferList = new List<byte>();
                    bufferList.AddRange(receiveBuffer.Take(receiveResult.Count));
                    //ReceiveCompletedMessageByUsingWhile
                    while (!receiveResult.EndOfMessage)
                    {
                        receiveResult = await FileTransportClientWebSocket.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
                        bufferList.AddRange(receiveBuffer.Take(receiveResult.Count));
                    }
                    var receivedBinaryBuffer = bufferList.ToArray();
                    if (receiveResult.MessageType == WebSocketMessageType.Binary)
                    {
                        try
                        {
                            var messageType = XCCBinaryMessageType.Binary;
                            var xFEBuffer = XFEBuffer.ToXFEBuffer(receivedBinaryBuffer);
                            var sender = Encoding.UTF8.GetString(xFEBuffer["Sender"]);
                            var signature = Encoding.UTF8.GetString(xFEBuffer["Type"]);
                            if (signature == "callback")
                                return;
                            var messageId = Encoding.UTF8.GetString(xFEBuffer["ID"]);
                            bool isHistory = Encoding.UTF8.GetString(xFEBuffer["IsHistory"]) == "True";
                            var sendTime = DateTime.Parse(Encoding.UTF8.GetString(xFEBuffer["SendTime"]));
                            byte[] unPackedBuffer = xFEBuffer[sender];
                            switch (signature)
                            {
                                case "text":
                                    messageType = XCCBinaryMessageType.Text;
                                    break;
                                case "image":
                                    messageType = XCCBinaryMessageType.Image;
                                    break;
                                case "audio":
                                    messageType = XCCBinaryMessageType.Audio;
                                    break;
                                case "audio-buffer":
                                    messageType = XCCBinaryMessageType.AudioBuffer;
                                    break;
                                case "video":
                                    messageType = XCCBinaryMessageType.Video;
                                    break;
                                case "callback":
                                    UpdateTaskTrigger?.Invoke(true, messageId);
                                    continue;
                                default:
                                    messageType = XCCBinaryMessageType.Binary;
                                    break;
                            }
                            workBase.binaryMessageReceived?.Invoke(this, new XCCBinaryMessageReceivedEventArgsImpl(this, TextMessageClientWebSocket, FileTransportClientWebSocket, XCCClientType.FileTransportClient, sender, messageId, unPackedBuffer, messageType, signature, sendTime, isHistory));
                        }
                        catch (Exception ex)
                        {
                            workBase.exceptionMessageReceived?.Invoke(this, new XCCExceptionMessageReceivedEventArgsImpl(this, TextMessageClientWebSocket, FileTransportClientWebSocket, XCCClientType.FileTransportClient, null, null, new XFECyberCommException("接收XCC服务器消息时发生异常", ex)));
                        }
                    }
                }
                catch (Exception ex)
                {
                    try { await FileTransportClientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Close", CancellationToken.None); } catch { }
                    if (FileTransportClientConnected == true)
                    {
                        workBase.connectionClosed?.Invoke(this, new XCCConnectionClosedEventArgsImpl(this, XCCClientType.FileTransportClient, TextMessageClientWebSocket, FileTransportClientWebSocket, false));
                    }
                    FileTransportClientConnected = false;
                    if (autoReconnect)
                    {
                        if (autoReconnect)
                        {
                            Thread.Sleep(reconnectTryDelay);
                            if (reconnectTimes <= reconnectMaxTimes || reconnectMaxTimes == -1)
                                goto XCCReconnect;
                        }
                    }
                    else
                    {
                        workBase.exceptionMessageReceived?.Invoke(this, new XCCExceptionMessageReceivedEventArgsImpl(this, TextMessageClientWebSocket, FileTransportClientWebSocket, XCCClientType.FileTransportClient, null, null, new XFECyberCommException("与XCC网络通讯服务器建立连接时发生异常", ex)));
                        return;
                    }
                }
            }
            workBase.connectionClosed?.Invoke(this, new XCCConnectionClosedEventArgsImpl(this, XCCClientType.FileTransportClient, TextMessageClientWebSocket, FileTransportClientWebSocket, true));
        }
        /// <summary>
        /// 等待明文服务器和文件服务器均连接
        /// </summary>
        /// <returns></returns>
        public async Task WaitConnect()
        {
            await Task.Run(() => { while (!TextMessageClientConnected || !FileTransportClientConnected) { } });
        }
        /// <summary>
        /// 发送文本消息
        /// </summary>
        /// <param name="message">待发送的文本</param>
        /// <param name="timeout">最长超时时长</param>
        /// <returns>服务器接收校验是否成功</returns>
        public async Task<bool> SendTextMessage(string message, int timeout = 30000)
        {
            var messageId = Guid.NewGuid().ToString();
            return await SendTextMessage(message, messageId, timeout);
        }
        /// <summary>
        /// 发送文本消息
        /// </summary>
        /// <param name="message">待发送的文本</param>
        /// <param name="messageId">消息ID</param>
        /// <param name="timeout">最长超时时长</param>
        /// <exception cref="XFECyberCommException"></exception>
        /// <returns>服务器接收校验是否成功</returns>
        public async Task<bool> SendTextMessage(string message, string messageId, int timeout)
        {
            try
            {
                byte[] sendBuffer = Encoding.UTF8.GetBytes(new string[] { messageId, "[XCCTextMessage]", message }.ToXFEString());
                await TextMessageClientWebSocket.SendAsync(new ArraySegment<byte>(sendBuffer), WebSocketMessageType.Text, true, CancellationToken.None);
                var endTask = Task.Run(async () =>
                {
                    await Task.Delay(timeout);
                    UpdateTaskTrigger?.Invoke(false, messageId);
                });
                return await new XFEWaitTask<bool>(ref UpdateTaskTrigger, messageId);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端发送文本到服务器时出现异常", ex);
            }
        }
        /// <summary>
        /// 发送标准的文本消息
        /// </summary>
        /// <param name="role">发送者角色</param>
        /// <param name="message">待发送的文本</param>
        /// <exception cref="XFECyberCommException"></exception>
        /// <returns>服务器接收校验是否成功</returns>
        [Obsolete("发送者已统一,请使用SendTextMessage或SendBinaryTextMessage")]
        public async Task<bool> SendStandardTextMessage(string role, string message)
        {
            try
            {
                return await SendTextMessage(message);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端发送文本到服务器时出现异常", ex);
            }
        }
        /// <summary>
        /// 发送签名二进制消息
        /// </summary>
        /// <param name="message">二进制消息</param>
        /// <param name="signature">签名标识</param>
        /// <param name="timeout">最长超时时长</param>
        /// <returns>服务器接收校验是否成功</returns>
        public async Task<bool> SendSignedBinaryMessage(byte[] message, string signature, int timeout = 10000)
        {
            var messageId = Guid.NewGuid().ToString();
            return await SendSignedBinaryMessage(message, messageId, signature, timeout);
        }
        /// <summary>
        /// 发送签名二进制消息
        /// </summary>
        /// <param name="message">二进制消息</param>
        /// <param name="messageId">消息ID</param>
        /// <param name="signature">签名标识</param>
        /// <param name="timeout">最长超时时长</param>
        /// <returns>服务器接收校验是否成功</returns>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task<bool> SendSignedBinaryMessage(byte[] message, string messageId, string signature, int timeout)
        {
            try
            {
                var xFEBuffer = new XFEBuffer(Sender, message, "Type", Encoding.UTF8.GetBytes(signature), "ID", Encoding.UTF8.GetBytes(messageId));
                await FileTransportClientWebSocket.SendAsync(new ArraySegment<byte>(xFEBuffer.ToBuffer()), WebSocketMessageType.Binary, true, CancellationToken.None);
                var endTask = Task.Run(async () =>
                {
                    await Task.Delay(timeout);
                    UpdateTaskTrigger?.Invoke(false, messageId);
                });
                return await new XFEWaitTask<bool>(ref UpdateTaskTrigger, messageId);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端发送二进制数据到服务器时出现异常", ex);
            }
        }
        /// <summary>
        /// 发送二进制文本消息
        /// </summary>
        /// <param name="message">消息</param>
        /// <returns>服务器接收校验是否成功</returns>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task<bool> SendBinaryTextMessage(string message)
        {
            try
            {
                return await SendSignedBinaryMessage(Encoding.UTF8.GetBytes(message), "text");
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端发送文本到服务器时出现异常", ex);
            }
        }
        /// <summary>
        /// 发送默认标准的二进制消息
        /// </summary>
        /// <param name="message">待发送的二进制数据</param>
        /// <param name="timeout">最长超时时长</param>
        /// <exception cref="XFECyberCommException"></exception>
        /// <returns>服务器接收校验是否成功</returns>
        public async Task<bool> SendBinaryMessage(byte[] message, int timeout = 1000)
        {
            try
            {
                return await SendSignedBinaryMessage(message, "binary", timeout);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端发送二进制数据到服务器时出现异常", ex);
            }
        }
        /// <summary>
        /// 发送图片
        /// </summary>
        /// <param name="filePath">图片路径</param>
        /// <returns>服务器接收校验是否成功</returns>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task<bool> SendImage(string filePath)
        {
            try
            {
                return await SendSignedBinaryMessage(File.ReadAllBytes(filePath), "image", 60000);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端发送图片到服务器时出现异常", ex);
            }
        }
        /// <summary>
        /// 发送视频
        /// </summary>
        /// <param name="filePath">视频路径</param>
        /// <returns>服务器接收校验是否成功</returns>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task<bool> SendVideo(string filePath)
        {
            try
            {
                return await SendSignedBinaryMessage(File.ReadAllBytes(filePath), "video", 300000);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端发送视频到服务器时出现异常", ex);
            }
        }
        /// <summary>
        /// 发送音频
        /// </summary>
        /// <param name="filePath">音频路径</param>
        /// <returns>服务器接收校验是否成功</returns>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task<bool> SendAudio(string filePath)
        {
            try
            {
                return await SendSignedBinaryMessage(File.ReadAllBytes(filePath), "audio");
            }
            catch (Exception ex) { throw new XFECyberCommException("客户端发送音频到服务器时出现异常", ex); }
        }
        /// <summary>
        /// 发送音频字节流(服务器不会缓存)
        /// </summary>
        /// <param name="buffer">二进制音频流</param>
        /// <returns>服务器接收校验是否成功</returns>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task<bool> SendAudioBuffer(byte[] buffer)
        {
            try
            {
                return await SendSignedBinaryMessage(buffer, "audio-buffer");
            }
            catch (Exception ex) { throw new XFECyberCommException("客户端发送音频到服务器时出现异常", ex); }
        }
        /// <summary>
        /// 获取历史记录
        /// </summary>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task<bool> GetHistory()
        {
            try
            {
                var messageId = Guid.NewGuid().ToString();
                byte[] sendBuffer = Encoding.UTF8.GetBytes(new string[] { messageId, "[XCCGetHistory]", "[XCCGetHistory]" }.ToXFEString());
                await TextMessageClientWebSocket.SendAsync(new ArraySegment<byte>(sendBuffer), WebSocketMessageType.Text, true, CancellationToken.None);
                var endTask = Task.Run(async () =>
                {
                    await Task.Delay(5000);
                    UpdateTaskTrigger?.Invoke(false, messageId);
                });
                return await new XFEWaitTask<bool>(ref UpdateTaskTrigger, messageId);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端获取历史记录时出现异常", ex);
            }
        }
        /// <summary>
        /// 关闭XCC会话
        /// </summary>
        /// <returns></returns>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task CloseXCC()
        {
            try
            {
                readyToClose = true;
                await TextMessageClientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "客户端主动关闭连接", CancellationToken.None);
                await FileTransportClientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "客户端主动关闭连接", CancellationToken.None);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端关闭连接时出现异常", ex);
            }
        }
        #endregion
        internal XCCGroup(string signature, string groupId, string sender, XCCNetWorkBase xCCNetWorkBase)
        {
            Signature = signature;
            GroupId = groupId;
            Sender = sender;
            workBase = xCCNetWorkBase;
        }
    }
    /// <summary>
    /// XCC文件类型
    /// </summary>
    public enum XCCFileType
    {
        /// <summary>
        /// 图片
        /// </summary>
        Image,
        /// <summary>
        /// 视频
        /// </summary>
        Video,
        /// <summary>
        /// 音频
        /// </summary>
        Audio
    }
    /// <summary>
    /// 消息接收触发器
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="isHistory">是否为历史消息</param>
    /// <param name="message">消息</param>
    public delegate void MessageReceivedHandler<T>(bool isHistory, T message);
    /// <summary>
    /// XCC消息接收器
    /// </summary>
    public class XCCMessageReceiveHelper
    {
        private readonly Dictionary<string, XCCFile> xCCFileDictionary = new Dictionary<string, XCCFile>();
        private readonly Dictionary<string, List<XCCMessage>> xCCMessageDictionary = new Dictionary<string, List<XCCMessage>>();
        private bool loaded = false;
        /// <summary>
        /// 自动保存到本地
        /// </summary>
        public bool AutoSaveInLocal { get; set; }
        /// <summary>
        /// 保存的根目录
        /// </summary>
        public string SavePathRoot { get; set; }
        /// <summary>
        /// 接收到文件事件
        /// </summary>
        public event MessageReceivedHandler<XCCFile> FileReceived;
        /// <summary>
        /// 接收到文本事件
        /// </summary>
        public event MessageReceivedHandler<XCCMessage> TextReceived;
        /// <summary>
        /// 错误发生事件
        /// </summary>
        public event XFEEventHandler<XFECyberCommException> ExceptionOccurred;
        /// <summary>
        /// 接收到实时音频字节流事件
        /// </summary>
        public event XFEEventHandler<byte[]> AudioBufferReceived;
        /// <summary>
        /// 从设置的根目录加载
        /// </summary>
        /// <returns></returns>
        public async Task Load()
        {
            await Task.Run(() =>
            {
                if (Directory.Exists(SavePathRoot))
                {
                    foreach (var groupIdFullPath in Directory.EnumerateDirectories(SavePathRoot))
                    {
                        var groupId = Path.GetFileName(groupIdFullPath);
                        if (File.Exists($"{groupIdFullPath}/XFEMessage/XFEMessage.xfe"))
                        {
                            var xCCMessageList = new List<XCCMessage>();
                            foreach (var entry in new XFEMultiDictionary(File.ReadAllText($"{groupIdFullPath}/XFEMessage/XFEMessage.xfe")))
                            {
                                var xCCMessage = XCCMessage.ConvertToXCCMessage(entry.Content, groupId);
                                xCCMessageList.Add(xCCMessage);
                                if (xCCMessage.MessageType == XCCTextMessageType.Text)
                                    TextReceived?.Invoke(true, xCCMessage);
                                else
                                    FileReceived?.Invoke(true, LoadFile(xCCMessage));
                            }
                            xCCMessageDictionary.Add(groupId, xCCMessageList);
                        }
                    }
                }
                else
                {
                    Directory.CreateDirectory(SavePathRoot);
                }
            });
            loaded = true;
        }
        /// <summary>
        /// 从设置的根目录的指定群组加载
        /// </summary>
        /// <param name="groupId">群组ID</param>
        /// <returns></returns>
        public async Task LoadGroup(string groupId)
        {
            await Task.Run(() =>
            {
                if (File.Exists($"{SavePathRoot}/{groupId}/XFEMessage/XFEMessage.xfe"))
                {
                    var xCCMessageList = new List<XCCMessage>();
                    foreach (var entry in new XFEMultiDictionary(File.ReadAllText($"{SavePathRoot}/{groupId}/XFEMessage/XFEMessage.xfe")))
                    {
                        var xCCMessage = XCCMessage.ConvertToXCCMessage(entry.Content, groupId);
                        xCCMessageList.Add(xCCMessage);
                        if (xCCMessage.MessageType == XCCTextMessageType.Text)
                            TextReceived?.Invoke(true, xCCMessage);
                        else
                            FileReceived?.Invoke(true, LoadFile(xCCMessage));
                    }
                    xCCMessageDictionary.Add(groupId, xCCMessageList);
                }
            });
            loaded = true;
        }
        /// <summary>
        /// 清理无用文件
        /// </summary>
        /// <returns></returns>
        public async Task ClearUselessFile()
        {
            if (!loaded)
                throw new XFEExtensionException("不能在加载完成前调用清理");
            await Task.Run(() =>
            {
                foreach (var groupId in xCCMessageDictionary.Keys)
                {
                    foreach (var file in Directory.EnumerateFiles($"{SavePathRoot}/{groupId}"))
                    {
                        var filePath = $"{SavePathRoot}/{groupId}/{file}";
                        var messageId = Path.GetFileNameWithoutExtension(filePath);
                        if (!xCCMessageDictionary.ContainsKey(groupId) || xCCMessageDictionary[groupId].Find(x => x.MessageId == messageId) is null)
                        {
                            File.Delete(filePath);
                        }
                    }
                }
            });
        }
        private XCCFile LoadFile(XCCMessage xCCMessage)
        {
            var filePath = $"{SavePathRoot}/{xCCMessage.GroupId}/{xCCMessage.MessageId}.xfe";
            byte[] fileBuffer = null;
            if (File.Exists(filePath))
                fileBuffer = File.ReadAllBytes(filePath);
            XCCFile xCCFile;
            if (xCCFileDictionary.ContainsKey(xCCMessage.MessageId))
            {
                if (!xCCFileDictionary[xCCMessage.MessageId].Loaded && fileBuffer != null)
                    xCCFileDictionary[xCCMessage.MessageId].LoadFile(fileBuffer);
                return xCCFileDictionary[xCCMessage.MessageId];
            }
            switch (xCCMessage.MessageType)
            {
                case XCCTextMessageType.Image:
                    xCCFile = new XCCFile(xCCMessage.GroupId, xCCMessage.MessageId, XCCFileType.Image, xCCMessage.Sender, xCCMessage.SendTime, fileBuffer);
                    break;
                case XCCTextMessageType.Audio:
                    xCCFile = new XCCFile(xCCMessage.GroupId, xCCMessage.MessageId, XCCFileType.Audio, xCCMessage.Sender, xCCMessage.SendTime, fileBuffer);
                    break;
                case XCCTextMessageType.Video:
                    xCCFile = new XCCFile(xCCMessage.GroupId, xCCMessage.MessageId, XCCFileType.Video, xCCMessage.Sender, xCCMessage.SendTime, fileBuffer);
                    break;
                default:
                    return null;
            }
            xCCFileDictionary.Add(xCCMessage.MessageId, xCCFile);
            return xCCFile;
        }
        /// <summary>
        /// 获取文件
        /// </summary>
        /// <param name="messageId">消息ID</param>
        /// <returns></returns>
        public XCCFile GetFile(string messageId)
        {
            return xCCFileDictionary.ContainsKey(messageId) ? xCCFileDictionary[messageId] : null;
        }
        /// <summary>
        /// 添加文件
        /// </summary>
        /// <param name="xCCFile">XCC文件实例</param>
        public void AddFile(XCCFile xCCFile)
        {
            xCCFileDictionary.Add(xCCFile.MessageId, xCCFile);
            if (AutoSaveInLocal && xCCFile.FileBuffer != null)
                SaveFile(xCCFile);
        }
        /// <summary>
        /// 保存文件
        /// </summary>
        /// <param name="xCCFile">XCC文件实例</param>
        public void SaveFile(XCCFile xCCFile)
        {
            if (!Directory.Exists($"{SavePathRoot}/{xCCFile.GroupId}"))
            {
                Directory.CreateDirectory($"{SavePathRoot}/{xCCFile.GroupId}");
            }
            File.WriteAllBytes($"{SavePathRoot}/{xCCFile.GroupId}/{xCCFile.MessageId}.xfe", xCCFile.FileBuffer);
        }
        /// <summary>
        /// 保存群组消息
        /// </summary>
        /// <param name="groupId"></param>
        public void SaveMessage(string groupId)
        {
            var filePath = $"{SavePathRoot}/{groupId}/XFEMessage";
            if (!Directory.Exists(filePath))
            {
                Directory.CreateDirectory(filePath);
            }
            var storageDictionary = new XFEDictionary();
            foreach (var xCCMessage in xCCMessageDictionary[groupId])
            {
                storageDictionary.Add(xCCMessage.MessageId, xCCMessage.ToString());
            }
            File.WriteAllText(filePath + "/XFEMessage.xfe", storageDictionary.ToString());
        }
        private void ReceiveFilePlaceHolder(XCCTextMessageReceivedEventArgs e, XCCFileType fileType)
        {
            var xCCFile = new XCCFile(e.GroupId, e.MessageId, fileType, e.Sender, e.SendTime);
            if (!xCCFileDictionary.ContainsKey(e.MessageId))
            {
                xCCFileDictionary.Add(e.MessageId, xCCFile);
            }
            FileReceived?.Invoke(e.IsHistory, xCCFile);
            if (AutoSaveInLocal)
                SaveMessage(e.GroupId);
        }
        private void ReceiveTextMessage(object sender, XCCTextMessageReceivedEventArgs e)
        {
            var message = new XCCMessage(e.MessageId, e.MessageType, e.TextMessage, e.Sender, e.SendTime, e.GroupId);
            if (xCCMessageDictionary.ContainsKey(e.GroupId))
            {
                if (xCCMessageDictionary[e.GroupId].Find(x => x.MessageId == e.MessageId) is null)
                {
                    xCCMessageDictionary[e.GroupId].Add(message);
                }
                else
                {
                    return;
                }
            }
            else
            {
                xCCMessageDictionary.Add(e.GroupId, new List<XCCMessage>() { message });
            }
            switch (e.MessageType)
            {
                case XCCTextMessageType.Text:
                    TextReceived?.Invoke(e.IsHistory, message);
                    if (AutoSaveInLocal)
                        SaveMessage(e.GroupId);
                    break;
                case XCCTextMessageType.Image:
                    ReceiveFilePlaceHolder(e, XCCFileType.Image);
                    break;
                case XCCTextMessageType.Audio:
                    ReceiveFilePlaceHolder(e, XCCFileType.Audio);
                    break;
                case XCCTextMessageType.Video:
                    ReceiveFilePlaceHolder(e, XCCFileType.Video);
                    break;
                default:
                    break;
            }
        }
        private void ReceiveBinaryMessage(object sender, XCCBinaryMessageReceivedEventArgs e)
        {
            if (e.MessageType == XCCBinaryMessageType.AudioBuffer)
            {
                AudioBufferReceived?.Invoke(e.BinaryMessage);
                return;
            }
            if (xCCFileDictionary.ContainsKey(e.MessageId))
            {
                var xCCFile = xCCFileDictionary[e.MessageId];
                if (!xCCFile.Loaded)
                {
                    xCCFileDictionary[e.MessageId].LoadFile(e.BinaryMessage);
                    if (AutoSaveInLocal)
                        SaveFile(xCCFile);
                }
            }
            else
            {
                var fileType = XCCFileType.Image;
                switch (e.MessageType)
                {
                    case XCCBinaryMessageType.Image:
                        fileType = XCCFileType.Image;
                        break;
                    case XCCBinaryMessageType.Audio:
                        fileType = XCCFileType.Audio;
                        break;
                    case XCCBinaryMessageType.AudioBuffer:
                        break;
                    case XCCBinaryMessageType.Video:
                        fileType = XCCFileType.Video;
                        break;
                    default:
                        break;
                }
                var xCCFile = new XCCFile(e.GroupId, e.MessageId, fileType, e.Sender, e.SendTime, e.BinaryMessage);
                xCCFileDictionary.Add(e.MessageId, xCCFile);
                if (!e.IsHistory)
                    FileReceived?.Invoke(e.IsHistory, xCCFile);
            }
        }
        private void XCCNetWork_ExceptionMessageReceived(object sender, XCCExceptionMessageReceivedEventArgs e)
        {
            ExceptionOccurred?.Invoke(e.Exception);
        }
        /// <summary>
        /// XCC消息接收器
        /// </summary>
        /// <param name="savePathRoot">保存根目录</param>
        /// <param name="autoSaveInLocal">自动保存</param>
        public XCCMessageReceiveHelper(string savePathRoot, bool autoSaveInLocal = true)
        {
            AutoSaveInLocal = autoSaveInLocal;
            SavePathRoot = savePathRoot;
        }
        /// <summary>
        /// XCC消息接收器
        /// </summary>
        /// <param name="savePathRoot">保存根目录</param>
        /// <param name="xCCNetWork">XCC网络通讯实例</param>
        /// <param name="autoSaveInLocal">自动保存</param>
        public XCCMessageReceiveHelper(string savePathRoot, XCCNetWork xCCNetWork, bool autoSaveInLocal = true)
        {
            AutoSaveInLocal = autoSaveInLocal;
            SavePathRoot = savePathRoot;
            xCCNetWork.TextMessageReceived += ReceiveTextMessage;
            xCCNetWork.BinaryMessageReceived += ReceiveBinaryMessage;
            xCCNetWork.ExceptionMessageReceived += XCCNetWork_ExceptionMessageReceived;
        }
    }
    /// <summary>
    /// XCC文件
    /// </summary>
    public class XCCFile
    {
        /// <summary>
        /// 文件加载完成时触发
        /// </summary>
        public event XFEEventHandler<XCCFile> FileLoaded;
        /// <summary>
        /// 群组ID
        /// </summary>
        public string GroupId { get; }
        /// <summary>
        /// 消息ID
        /// </summary>
        public string MessageId { get; }
        /// <summary>
        /// 发送者
        /// </summary>
        public string Sender { get; }
        /// <summary>
        /// 发送时间
        /// </summary>
        public DateTime? SendTime { get; }
        /// <summary>
        /// XCC文件类型
        /// </summary>
        public XCCFileType FileType { get; }
        /// <summary>
        /// 是否已加载
        /// </summary>
        public bool Loaded { get; private set; }
        /// <summary>
        /// 文件流
        /// </summary>
        public byte[] FileBuffer { get; set; }
        /// <summary>
        /// 加载文件
        /// </summary>
        /// <param name="fileBuffer">文件流</param>
        public void LoadFile(byte[] fileBuffer)
        {
            FileBuffer = fileBuffer;
            Loaded = true;
            FileLoaded?.Invoke(this);
        }
        /// <summary>
        /// XCC文件
        /// </summary>
        /// <param name="groupId">群组ID</param>
        /// <param name="messageId">文件消息ID</param>
        /// <param name="fileType">文件类型</param>
        /// <param name="sender">发送者</param>
        /// <param name="sendTime">发送时间</param>
        /// <param name="fileBuffer">文件的Buffer</param>
        public XCCFile(string groupId, string messageId, XCCFileType fileType, string sender, DateTime? sendTime, byte[] fileBuffer = null)
        {
            GroupId = groupId;
            MessageId = messageId;
            FileType = fileType;
            Sender = sender;
            SendTime = sendTime;
            FileBuffer = fileBuffer;
            Loaded = fileBuffer != null;
        }
    }
    /// <summary>
    /// XCC消息
    /// </summary>
    public class XCCMessage
    {
        /// <summary>
        /// 消息ID
        /// </summary>
        public string MessageId { get; }
        /// <summary>
        /// 消息类型
        /// </summary>
        public XCCTextMessageType MessageType { get; }
        /// <summary>
        /// 消息内容
        /// </summary>
        public string Message { get; }
        /// <summary>
        /// 发送者
        /// </summary>
        public string Sender { get; }
        /// <summary>
        /// 发送时间
        /// </summary>
        public DateTime SendTime { get; }
        /// <summary>
        /// 群组ID
        /// </summary>
        public string GroupId { get; }
        /// <summary>
        /// 封装为字符串
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return new string[] { MessageId, MessageType.ToString(), Message, Sender, SendTime.ToString() }.ToXFEString();
        }
        /// <summary>
        /// 将封装后的XCC消息字符串转换为XCC消息对象
        /// </summary>
        /// <param name="xCCMessageStringFormat"></param>
        /// <param name="groupId"></param>
        /// <returns></returns>
        public static XCCMessage ConvertToXCCMessage(string xCCMessageStringFormat, string groupId)
        {
            var unPackedMessage = xCCMessageStringFormat.ToXFEArray<string>();
            return new XCCMessage(unPackedMessage[0], (XCCTextMessageType)Enum.Parse(typeof(XCCTextMessageType), unPackedMessage[1]), unPackedMessage[2], unPackedMessage[3], DateTime.Parse(unPackedMessage[4]), groupId);
        }
        /// <summary>
        /// XCC消息
        /// </summary>
        /// <param name="messageId">消息ID</param>
        /// <param name="messageType">消息类型</param>
        /// <param name="message">消息内容</param>
        /// <param name="sender">发送者</param>
        /// <param name="sendTime">发送时间</param>
        /// <param name="groupId">群组ID</param>
        public XCCMessage(string messageId, XCCTextMessageType messageType, string message, string sender, DateTime sendTime, string groupId)
        {
            MessageId = messageId;
            MessageType = messageType;
            Message = message;
            Sender = sender;
            SendTime = sendTime;
            GroupId = groupId;
        }
    }
    /// <summary>
    /// XCC网络通讯事件
    /// </summary>
    public abstract class XCCMessageReceivedEventArgs : EventArgs
    {
        /// <summary>
        /// 触发事件的群组
        /// </summary>
        public XCCGroup Group { get; }
        /// <summary>
        /// WebSocket明文传输客户端
        /// </summary>
        public ClientWebSocket TextMessageClientWebSocket { get; private set; }
        /// <summary>
        /// WebSocket文件传输客户端
        /// </summary>
        public ClientWebSocket FileTransportClientWebSocket { get; private set; }
        /// <summary>
        /// XCC服务器连接类型
        /// </summary>
        public XCCClientType XCCClientType { get; }
        /// <summary>
        /// 消息ID
        /// </summary>
        public string MessageId { get; }
        /// <summary>
        /// 群组ID
        /// </summary>
        public string GroupId
        {
            get
            {
                return Group.GroupId;
            }
        }
        /// <summary>
        /// 发送者
        /// </summary>
        public string Sender { get; }
        /// <summary>
        /// 回复文本消息
        /// </summary>
        /// <param name="message">待发送的文本</param>
        /// <returns>发送进程</returns>
        public async Task ReplyTextMessage(string message)
        {
            try
            {
                byte[] sendBuffer = Encoding.UTF8.GetBytes(message);
                await TextMessageClientWebSocket.SendAsync(new ArraySegment<byte>(sendBuffer), WebSocketMessageType.Text, true, CancellationToken.None);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("收到服务器端数据后客户端回复文本数据时出现异常", ex);
            }
        }
        /// <summary>
        /// 回复二进制消息
        /// </summary>
        /// <param name="message">二进制消息</param>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task ReplyBinaryMessage(byte[] message)
        {
            try
            {
                await FileTransportClientWebSocket.SendAsync(new ArraySegment<byte>(message), WebSocketMessageType.Binary, true, CancellationToken.None);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("收到服务器端数据后客户端回复文本数据时出现异常", ex);
            }
        }
        /// <summary>
        /// 关闭连接
        /// </summary>
        /// <exception cref="XFECyberCommException"></exception>
        public async Task Close()
        {
            try
            {
                if (TextMessageClientWebSocket.State == WebSocketState.Open)
                    await TextMessageClientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "客户端主动关闭连接", CancellationToken.None);
                if (FileTransportClientWebSocket.State == WebSocketState.Open)
                    await FileTransportClientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "客户端主动关闭连接", CancellationToken.None);
            }
            catch (Exception ex)
            {
                throw new XFECyberCommException("客户端关闭连接时出现异常", ex);
            }
        }
        internal XCCMessageReceivedEventArgs(XCCGroup group, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket, XCCClientType xCCClientType, string sender, string messageId)
        {
            Group = group;
            TextMessageClientWebSocket = textMessageClientWebSocket;
            FileTransportClientWebSocket = fileTransportClientWebSocket;
            XCCClientType = xCCClientType;
            Sender = sender;
            MessageId = messageId;
        }
    }
    /// <summary>
    /// XFE网络通信明文返回消息类型
    /// </summary>
    public enum XCCTextMessageType
    {
        /// <summary>
        /// 文本消息
        /// </summary>
        Text,
        /// <summary>
        /// 图片消息
        /// </summary>
        Image,
        /// <summary>
        /// 音频消息
        /// </summary>
        Audio,
        /// <summary>
        /// 视频消息
        /// </summary>
        Video
    }
    /// <summary>
    /// XCC网络通讯接收到明文消息事件
    /// </summary>
    public abstract class XCCTextMessageReceivedEventArgs : XCCMessageReceivedEventArgs
    {
        /// <summary>
        /// 返回文本消息类型
        /// </summary>
        public XCCTextMessageType MessageType { get; }
        /// <summary>
        /// 文本消息
        /// </summary>
        public string TextMessage { get; }
        /// <summary>
        /// 发送时间
        /// </summary>
        public DateTime SendTime { get; }
        /// <summary>
        /// 是否为历史消息
        /// </summary>
        public bool IsHistory { get; }
        internal XCCTextMessageReceivedEventArgs(XCCGroup group, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket, XCCClientType xCCClientType, string messageId, XCCTextMessageType messageType, string message, string sender, DateTime sendTime, bool isHistory) : base(group, textMessageClientWebSocket, fileTransportClientWebSocket, xCCClientType, sender, messageId)
        {
            MessageType = messageType;
            TextMessage = message;
            SendTime = sendTime;
            IsHistory = isHistory;
        }

    }
    /// <summary>
    /// XFE网络通信二进制返回消息类型
    /// </summary>
    public enum XCCBinaryMessageType
    {
        /// <summary>
        /// 文本消息
        /// </summary>
        Text,
        /// <summary>
        /// 二进制消息
        /// </summary>
        Binary,
        /// <summary>
        /// 图片消息
        /// </summary>
        Image,
        /// <summary>
        /// 音频消息
        /// </summary>
        Audio,
        /// <summary>
        /// 实时音频
        /// </summary>
        AudioBuffer,
        /// <summary>
        /// 视频消息
        /// </summary>
        Video
    }
    /// <summary>
    /// XCC网络通讯接收到二进制消息事件
    /// </summary>
    public abstract class XCCBinaryMessageReceivedEventArgs : XCCMessageReceivedEventArgs
    {
        /// <summary>
        /// 返回二进制消息类型
        /// </summary>
        public XCCBinaryMessageType MessageType { get; }
        /// <summary>
        /// 消息签名
        /// </summary>
        public string Signature { get; }
        /// <summary>
        /// 二进制消息
        /// </summary>
        public byte[] BinaryMessage { get; }
        /// <summary>
        /// 发送时间
        /// </summary>
        public DateTime SendTime { get; }
        /// <summary>
        /// 是否为历史消息
        /// </summary>
        public bool IsHistory { get; }
        internal XCCBinaryMessageReceivedEventArgs(XCCGroup group, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket, XCCClientType xCCClientType, string sender, string messageId, byte[] buffer, XCCBinaryMessageType messageType, string signature, DateTime sendTime, bool isHistory) : base(group, textMessageClientWebSocket, fileTransportClientWebSocket, xCCClientType, sender, messageId)
        {
            BinaryMessage = buffer;
            MessageType = messageType;
            Signature = signature;
            SendTime = sendTime;
            IsHistory = isHistory;
        }
    }
    /// <summary>
    /// XCC网络通讯期间异常事件
    /// </summary>
    public abstract class XCCExceptionMessageReceivedEventArgs : XCCMessageReceivedEventArgs
    {
        /// <summary>
        /// 异常信息
        /// </summary>
        public XFECyberCommException Exception { get; }
        internal XCCExceptionMessageReceivedEventArgs(XCCGroup group, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket, XCCClientType xCCClientType, string sender, string messageId, XFECyberCommException exception) : base(group, textMessageClientWebSocket, fileTransportClientWebSocket, xCCClientType, sender, messageId)
        {
            Exception = exception;
        }
    }
    /// <summary>
    /// XCC会话关闭事件
    /// </summary>
    public abstract class XCCConnectionClosedEventArgs : EventArgs
    {
        /// <summary>
        /// 触发事件的群组
        /// </summary>
        public XCCGroup Group { get; }
        /// <summary>
        /// XCC服务器连接类型
        /// </summary>
        public XCCClientType XCCClientType { get; }
        /// <summary>
        /// WebSocket明文传输客户端
        /// </summary>
        public ClientWebSocket TextMessageClientWebSocket { get; private set; }
        /// <summary>
        /// WebSocket文件传输客户端
        /// </summary>
        public ClientWebSocket FileTransportClientWebSocket { get; private set; }
        /// <summary>
        /// 是否正常关闭
        /// </summary>
        public bool ClosedNormally { get; }

        internal XCCConnectionClosedEventArgs(XCCGroup group, XCCClientType xCCClientType, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket, bool closedNormally)
        {
            Group = group;
            XCCClientType = xCCClientType;
            TextMessageClientWebSocket = textMessageClientWebSocket;
            FileTransportClientWebSocket = fileTransportClientWebSocket;
            ClosedNormally = closedNormally;
        }
    }
    /// <summary>
    /// XCC连接事件
    /// </summary>
    public abstract class XCCConnectedEventArgs : EventArgs
    {
        /// <summary>
        /// 触发事件的群组
        /// </summary>
        public XCCGroup Group { get; }
        /// <summary>
        /// XCC服务器连接类型
        /// </summary>
        public XCCClientType XCCClientType { get; }
        /// <summary>
        /// WebSocket明文传输客户端
        /// </summary>
        public ClientWebSocket TextMessageClientWebSocket { get; private set; }
        /// <summary>
        /// WebSocket文件传输客户端
        /// </summary>
        public ClientWebSocket FileTransportClientWebSocket { get; private set; }

        internal XCCConnectedEventArgs(XCCGroup group, XCCClientType xCCClientType, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket)
        {
            Group = group;
            XCCClientType = xCCClientType;
            TextMessageClientWebSocket = textMessageClientWebSocket;
            FileTransportClientWebSocket = fileTransportClientWebSocket;
        }
    }
    class XCCGroupImpl : XCCGroup
    {
        internal XCCGroupImpl(string signature, string groupId, string sender, XCCNetWorkBase xCCNetWorkBase) : base(signature, groupId, sender, xCCNetWorkBase) { }
    }
    class XCCConnectionClosedEventArgsImpl : XCCConnectionClosedEventArgs
    {
        /// <summary>
        /// 使用群组、连接类型、传输客户端及关闭状态创建 XCC 会话关闭事件参数。
        /// </summary>
        /// <param name="group">触发关闭事件的 XCC 群组。</param>
        /// <param name="xCCClientType">关闭连接对应的 XCC 客户端类型。</param>
        /// <param name="textMessageClientWebSocket">用于明文消息传输的 WebSocket 客户端。</param>
        /// <param name="fileTransportClientWebSocket">用于文件传输的 WebSocket 客户端。</param>
        /// <param name="closedNormally">连接是否按正常流程关闭。</param>
        public XCCConnectionClosedEventArgsImpl(XCCGroup group, XCCClientType xCCClientType, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket, bool closedNormally) : base(group, xCCClientType, textMessageClientWebSocket, fileTransportClientWebSocket, closedNormally) { }
    }
    class XCCConnectedEventArgsImpl : XCCConnectedEventArgs
    {
        /// <summary>
        /// 使用群组、连接类型及传输客户端创建 XCC 连接成功事件参数。
        /// </summary>
        /// <param name="group">触发连接事件的 XCC 群组。</param>
        /// <param name="xCCClientType">已建立连接的 XCC 客户端类型。</param>
        /// <param name="textMessageClientWebSocket">用于明文消息传输的 WebSocket 客户端。</param>
        /// <param name="fileTransportClientWebSocket">用于文件传输的 WebSocket 客户端。</param>
        public XCCConnectedEventArgsImpl(XCCGroup group, XCCClientType xCCClientType, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket) : base(group, xCCClientType, textMessageClientWebSocket, fileTransportClientWebSocket) { }
    }
    class XCCTextMessageReceivedEventArgsImpl : XCCTextMessageReceivedEventArgs
    {
        internal XCCTextMessageReceivedEventArgsImpl(XCCGroup group, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket, XCCClientType xCCClientType, string messageId, XCCTextMessageType messageType, string message, string sender, DateTime sendTime, bool isHistory) : base(group, textMessageClientWebSocket, fileTransportClientWebSocket, xCCClientType, messageId, messageType, message, sender, sendTime, isHistory) { }
    }
    class XCCBinaryMessageReceivedEventArgsImpl : XCCBinaryMessageReceivedEventArgs
    {
        internal XCCBinaryMessageReceivedEventArgsImpl(XCCGroup group, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket, XCCClientType xCCClientType, string sender, string messageId, byte[] buffer, XCCBinaryMessageType messageType, string signature, DateTime sendTime, bool isHistory) : base(group, textMessageClientWebSocket, fileTransportClientWebSocket, xCCClientType, sender, messageId, buffer, messageType, signature, sendTime, isHistory) { }
    }
    class XCCExceptionMessageReceivedEventArgsImpl : XCCExceptionMessageReceivedEventArgs
    {
        internal XCCExceptionMessageReceivedEventArgsImpl(XCCGroup group, ClientWebSocket textMessageClientWebSocket, ClientWebSocket fileTransportClientWebSocket, XCCClientType xCCClientType, string sender, string messageId, XFECyberCommException exception) : base(group, textMessageClientWebSocket, fileTransportClientWebSocket, xCCClientType, sender, messageId, exception) { }
    }
}