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

被折叠的 条评论
为什么被折叠?



