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;
            }
        }
    }
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值