`

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
评论

相关推荐

    代码.zip代码.zip代码数据分析代码.zip代码.zip代码数据分析

    代码.zip代码.zip代码数据分析

    STM32数字示波器+详细注释+上位机程序+硬件

    功能: 1、波形发生器:使用STM32一路DA实现正弦,三角波,方波,白噪声输出。 任意一种波形幅值在0-3.3V任意可调、频率在一定范围任意可调、方波占空比可调。调节选项可以通过触摸屏完成设置。 2、SD卡存储: SD卡波形存储输出,能够对当前屏幕截屏,以JPG格式存储在SD卡上。能够存储1S内的波形数据,可以随时调用查看。 3、数据传输: 用C#编写上位机,通过串口完成对下位机的控制。(1)实现STOP/RUN功能(2)输出波形电压、时间参数(3)控制截屏(4)控制波形发生器(5)控制完成FFT(6)波形的存储和显示 4、图形接口: UCGUI 2、水平扫速: 250 ns*、500ns、1μs、5 μs、10μs、50μs、500 μs、5ms 、50ms 3、垂直电压灵敏度:10mV/div, 20mV/div, 50mV/div, 0.1V/div, 0,2V/div, 0.5V/div, 1V/div, 2V/div, 5V/div 4、被测信号的各种参数屏幕显示,包括频率、电压峰峰值等。

    粗格栅及进水提升泵房计算书.xlsx

    污水处理计算书

    氧化沟工艺在污水处理中的应用.doc

    课程设计污水处理

    node-v11.10.0-linux-arm64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    JSP企业电子投票系统(源代码+设计说明书+调研报告++文献综述).zip

    JSP企业电子投票系统(源代码+设计说明书+调研报告++文献综述).zip

    node-v10.18.1-linux-s390x.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v12.22.7-linux-armv7l.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v10.22.0-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    基于matlab实现的能够计算配电网中的潮流,只需在里面填入参数即可运行 .rar

    基于matlab实现的能够计算配电网中的潮流,只需在里面填入参数即可运行。.rar

    基于matlab实现的随机粗糙表面对微气体轴承内气体压强分布的影响.rar

    基于matlab实现的随机粗糙表面对微气体轴承内气体压强分布的影响.rar

    基于使用microPython的开发单片机设计源码.zip

    我们在单片机开发中常会遇到需要将UTF-8转换为GBK编码的需求。在了解各种编码格式的情况下可知, UFT-8不能直接转成GBK,需中转成unicode再转换为gbk。而unicode和gbk之间没有算法可以直接计算,需要查表方式获取。 网上有一些C语言实现的代码,我这里分享一种microPython的实现代码 接下来就是要考虑表的存储方式了,刚开始我想着把表存到代码里直接通过索引实现编码转换。但是gb2312有七千多个字符全部存储要耗费很大内存,即使是32位的esp32也只有512k的内存,加上其他资源的消耗,剩余的内存不足以存储编码转换表。 于是只能将表保存成一个文件(转化成bin文件会比较好,方法类似),通过读写文件来减少内存开销。 具体的查表就是简单的二分法

    基于matlab实现的实现信号的frft变换

    基于matlab实现的实现信号的frft变换,傅立叶变换是将观看角度从时域转变到频域,分数阶傅立叶变换就是以观看时频面的角度去旋转时频面的坐标轴,然后再从观察频域的角度去分析信息。.rar

    node-v11.10.0-linux-ppc64le.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    杏鲍菇70m3d废水处理工程设计方案.doc

    课程设计污水处理

    node-v10.23.1-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v6.3.0-x64.msi

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    城市小区生活污水处理工艺设计.pptx

    课程设计污水处理

    ASP+ACCESS基于WEB车辆管理系统(源代码+设计说明书).zip

    ASP+ACCESS基于WEB车辆管理系统(源代码+设计说明书).zip

    机械设计自动识别快递分拣系统sw20可编辑非常好的设计图纸100%好用.zip

    机械设计自动识别快递分拣系统sw20可编辑非常好的设计图纸100%好用.zip

Global site tag (gtag.js) - Google Analytics