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