`

RudpSocket

 
阅读更多

RudpSocket:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace RGame.Rudp
{
    /// <summary>
    /// 可靠UDP的封装
    /// 1.连接:客户端会持续发送包给服务器,服务器收到后发送一个conv过来,conv为客户端在服务器的唯一标识,5s内没有收到触发超时
    /// 2.接收:数据报文会先放到一个双缓冲队列里面,
    /// </summary>
    public class RudpSocket
    {
        private const Int32 SWITCH_QUEUE_COUNT = 256;    // 双向队列的大小
        private const UInt32 CONNECT_TIMEOUT = 5000;     // 连接请求超时时间:5秒内没连接上算超时
        private const UInt32 RESEND_CONNECT = 500;       // 连接请求的间隔时间 500ms 发送一次

        private Action<enNetResult, byte[], string> mOnMsgCallback; // 回调函数,外部处理入口
        private UdpClient mUdpClient;
        private IPEndPoint mSvrEndPoint;              // 服务器地址

        private SwitchQueue<byte[]> mRecvQueue;       // 双缓冲队列:用于接收数据报
        private KCP mRudp;                           // 可靠UDP处理

        // 接收消息
        private bool mNeedUpdateFlag;                // 接收消息处理开关
        private UInt32 mNextUpdateTime;              // 下次更新时间

        // 连接相关
        private bool mInConnectStage;                 // 进入连接阶段
        private bool mConnectSucceed;                 // 是否连接成功
        private UInt32 mConnectStartTime;             // 连接开始时间
        private UInt32 mLastSendConnectTime;          // 最后一次连接时间

        public RudpSocket(Action<enNetResult, byte[], string> callback)
        {
            mOnMsgCallback = callback;
            mRecvQueue = new SwitchQueue<byte[]>(SWITCH_QUEUE_COUNT);
        }

        private void Reset()
        {
            mNeedUpdateFlag = false;
            mNextUpdateTime = 0;

            mInConnectStage = false;
            mConnectSucceed = false;
            mConnectStartTime = 0;
            mLastSendConnectTime = 0;
            mRecvQueue.Clear();
            mRudp = null;
        }

        #region 接收数据
        /// <summary>
        /// 建立连接,接收数据
        /// </summary>
        /// <param name="host">IP地址</param>
        /// <param name="port">端口</param>
        public void Connect(string host, UInt16 port)
        {
            if (string.IsNullOrEmpty(host) || port <= 0) return;

            Disconnect();

            mSvrEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
            mUdpClient = new UdpClient(12000);
            mUdpClient.Connect(mSvrEndPoint);
            mUdpClient.BeginReceive(ReceiveCallback, this);

            Reset();

            //mInConnectStage = true;
            mConnectSucceed = true;    // Test
            InitRudp(100);

            mConnectStartTime = RudpUtil.iclock();
        }

        private void ReceiveCallback(IAsyncResult ar)
        {
            // 1.异步接收指定端口的数据
            Byte[] data = mUdpClient.EndReceive(ar, ref mSvrEndPoint);  

            if (null != data)
            {
                OnData(data); // 数据处理
                //DebugHelper.LogError(StringUtil.BytesToString(data));
            }

            if (mUdpClient != null)
            {
                mUdpClient.BeginReceive(ReceiveCallback, this);   // 重新接收
            }
        }

        private void OnData(byte[] buf)
        {
            mRecvQueue.Push(buf);
        }
        #endregion

        #region 发送数据
        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="buf"></param>
        public void Send(byte[] buf)
        {
            if (buf == null || mRudp == null) return;
            mRudp.Send(buf);
            mNeedUpdateFlag = true;
        }

        public void Send(string temp)
        {
            if (string.IsNullOrEmpty(temp)) return;

            Send(System.Text.ASCIIEncoding.ASCII.GetBytes(temp));
        }
        #endregion

        public void Update()
        {
            OnUpdate(RudpUtil.iclock());
        }

        private void OnUpdate(UInt32 curTime)
        {
            // 1.处理连接相关
            ProcessConnect(curTime);

            // 2.处理收消息
            ProcessRecv(curTime);
        }

        #region 连接状态处理
        private void ProcessConnect(UInt32 curTime)
        {
            // 1.处理连接中
            if (mInConnectStage)
            {
                // 1.1处理超时
                if (IsConnectTimeout(curTime))
                {
                    mOnMsgCallback(enNetResult.ConnectFailed, null, "Timeout");
                    mInConnectStage = false;
                    return;
                }

                // 1.2 发送连接请求包
                if (IsSendConnectPacket(curTime))
                {
                    mLastSendConnectTime = curTime;
                    mUdpClient.Send(new byte[4] { 0, 0, 0, 0 }, 4);
                }

                // 1.3 判断连接是否建立
                ProcessConnectPacket();

                return;
            }
        }

        // 初始化 Rudp
        private void InitRudp(UInt32 conv)  
        {
            DebugHelper.LogError("------  181  ------:  conv = " + conv);
            mRudp = new KCP(conv, (byte[] buf, int size) =>
            {
                // 发送报文
                mUdpClient.Send(buf, size);
            });

            mRudp.NoDelay(1, 10, 2, 1);
        }

        private void ProcessConnectPacket()
        {
            mRecvQueue.Switch();

            if (!mRecvQueue.Empty())
            {
                byte[] buf = mRecvQueue.Pop();

                UInt32 conv = 0;
                RudpUtil.Decode32u(buf, 0, ref conv);    // conv

                // 1.连接状态要反馈一个大于零的值
                if (conv <= 0)
                {
                    throw new Exception("inlvaid connect back packet");
                }

                // 2.连接成功,初始化Rudp
                InitRudp(conv);

                mInConnectStage = false;
                mConnectSucceed = true;

                mOnMsgCallback(enNetResult.Success, null, null);
            }
        }

        private bool IsConnectTimeout(UInt32 curTime)
        {
            return curTime - mConnectStartTime > CONNECT_TIMEOUT;
        }

        private bool IsSendConnectPacket(UInt32 curTime)
        {
            return curTime - mLastSendConnectTime > RESEND_CONNECT;
        }
        #endregion

        #region 处理接收数据
        private void ProcessRecv(UInt32 curTime)
        {
            if (mConnectSucceed)
            {
                // 2.1 处理接收队列
                ProcessRecvQueue();

                // 2.2 处理消息:发送和接收的都在这里处理   
                if (mNeedUpdateFlag || curTime >= mNextUpdateTime) // 满足条件才开始处理
                {
                    DebugHelper.LogError("curTime = " + curTime + "    mNextUpdateTime = " + mNextUpdateTime);
                    mRudp.Update(curTime); 
                    mNextUpdateTime = mRudp.Check(curTime);  // 一个机制,当空闲时开启刷新
                    mNeedUpdateFlag = false;
                }
            }
        }

        private void ProcessRecvQueue()  // 处理接收到的消息:接收到的消息首先会压入队列,然后在update里面处理
        {
            // 1.双缓冲交换位置
            mRecvQueue.Switch();

            while (!mRecvQueue.Empty())
            {
                // 2.处理单个报文
                byte[] buf = mRecvQueue.Pop();
                mRudp.Input(buf);
                mNeedUpdateFlag = true;

                // 3.处理收到的,封装完整的消息
                for (int size = mRudp.PeekSize(); size > 0; size = mRudp.PeekSize())
                {
                    byte[] buffer = new byte[size];
                    if (mRudp.Recv(buffer) > 0)
                    {
                        mOnMsgCallback(enNetResult.RcvMsg, buffer, null);   // 返回上层可用的数据
                    }
                }
            }
        }
        #endregion

        public void Disconnect()
        {
            if (mUdpClient != null)
            {
                mUdpClient.Close();
                mUdpClient = null;
            }
        }
    }
}

 

 

分享到:
Kcp
评论

相关推荐

    单片机C语言Proteus仿真实例可演奏的电子琴

    单片机C语言Proteus仿真实例可演奏的电子琴提取方式是百度网盘分享地址

    电力概预算软件.zip

    电力概预算软件

    setuptools-64.0.0.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    爱你老妈(HTML文件)母亲节快乐

    母亲节祝福html源码 很简单的代码,随机生成背景

    Python源码-三门问题的验证.py

    Python源码-三门问题的验证

    setuptools-62.6.0.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    Gomoku.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    【财务管理】财务费用报销流程(含流程图).pdf

    【财务管理】财务费用报销流程(含流程图).pdf

    setuptools-46.3.0.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    C语言课作业-五子棋GomokuZero.zip

    五子棋游戏想必大家都非常熟悉,游戏规则十分简单。游戏开始后,玩家在游戏设置中选择人机对战,则系统执黑棋,玩家自己执白棋。双方轮流下一棋,先将横、竖或斜线的5个或5个以上同色棋子连成不间断的一排者为胜。 【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。 【技术】 Java、Python、Node.js、Spring Boot、Django、Express、MySQL、PostgreSQL、MongoDB、React、Angular、Vue、Bootstrap、Material-UI、Redis、Docker、Kubernetes

    setuptools-66.1.1.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    setuptools-65.6.3.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    setuptools-46.1.0.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    setuptools-0.9.5.zip

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    setuptools-3.0.1.tar.gz

    Python库是一组预先编写的代码模块,旨在帮助开发者实现特定的编程任务,无需从零开始编写代码。这些库可以包括各种功能,如数学运算、文件操作、数据分析和网络编程等。Python社区提供了大量的第三方库,如NumPy、Pandas和Requests,极大地丰富了Python的应用领域,从数据科学到Web开发。Python库的丰富性是Python成为最受欢迎的编程语言之一的关键原因之一。这些库不仅为初学者提供了快速入门的途径,而且为经验丰富的开发者提供了强大的工具,以高效率、高质量地完成复杂任务。例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    电力架空线路辅助设计软件.zip

    电力架空线路辅助设计软件

    红外辐射测温枪的测温原理2020年中国计算机设计大赛获奖作品展视频

    红外辐射测温枪的测温原理2020年中国计算机设计大赛获奖作品展视频提取方式是百度网盘分享地址

    实用五金手册计算软件.zip

    实用五金手册计算软件

    住宅小区配变电容量计算软件.zip

    住宅小区配变电容量计算软件

    中国大学生计算机设计大赛信息可视化国家二等奖计算机设计大赛

    中国大学生计算机设计大赛 信息可视化国家二等奖计算机设计大赛提取方式是百度网盘分享地址

Global site tag (gtag.js) - Google Analytics