#define Test
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using System.Linq;
using Newtonsoft.Json;
using System.IO;
using System.Reflection;
namespace NCFramework
{
public class NetService : SingleTon<NetService>, IService,IDisposable
{
/*
随笔
客户端的数据与服务端的数据相差3帧左右,对于客户端来说,所有的数据全部放在队列里面,在处理网络抖动的时候,驱动队列里面的数据来平滑相关的操作,
数据链的格式以链表的形式存在,因为UDP是不可靠的,数据的到来不一定按照顺序的,所以会进行部分的增删插入的操作
*/
#region Init
private NetService() { }
public string Name => ServiceInfoCfg.NetName;
public byte ID => ServiceInfoCfg.NetID;
public void Init()
{
Debug.Log("当前服务ID:" + ID + "====" + "当前服务Name:" + Name);
ProtoInit();
UDP();
LogicInit();
}
#endregion
private UInt32 frameCount = 0;
private AddressFamily IPProtocol = AddressFamily.InterNetwork;//目前暂定为IPV4
private Socket tcpClient;
private Socket udpClient;
private long realtimeSinceStartup = DateTimeUtil.DateTimeToLongTimeStamp(DateTime.Now);
private long m_LastUpdateShowTime = 0; //上一次更新帧率的时间;
private Socket server;
private EndPoint ClientPoint;
private Queue<IMessage> receiveQueue = new Queue<IMessage>();
private Queue<IMessage> sendQueue = new Queue<IMessage>();
private byte[] data = new byte[ConfigInfo.BufferSize];
//业务逻辑库
private Dictionary<int, Func<byte[], int>> allMethodsDic = new Dictionary<int, Func<byte[], int>>();
#region TCP
public void TcpInit()
{
tcpClient = new Socket(IPProtocol, SocketType.Stream, ProtocolType.Tcp);
IPAddress ip = IPAddress.Parse(ConfigInfo.ServerIP);
IPEndPoint point = new IPEndPoint(ip, ConfigInfo.ServerPort);
try
{
tcpClient.Connect(point);
Thread th = new Thread(TcpReceiveMsg);
th.IsBackground = true;
th.Start();
}
catch (Exception ex)
{
//打印异常信息,后续添加到日志中
}
finally
{
//可能会有超时重连
}
}
void TcpReceiveMsg()
{
while (true)
{
try
{
byte[] buffer = new byte[ConfigInfo.BufferSize];
int n = tcpClient.Receive(buffer);
string s = Encoding.UTF8.GetString(buffer, 0, n);
}
catch (Exception ex)
{
//添加异常信息
}
}
}
#endregion
#region UDP
Thread Udpthread;
private void UDP()
{
//构建UCP 服务器
Debug.Log("This is a Client, host name is "+ Dns.GetHostName());
//设置服务IP,设置UDP端口号
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(ConfigInfo.ServerIP), ConfigInfo.ServerPort);
//定义网络类型,数据连接类型和网络协议UDP
server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
server.Bind(new IPEndPoint(IPAddress.Parse(ConfigInfo.LocalIP), ConfigInfo.LocalPort));
string welcome = "Hello! ";
data = Encoding.ASCII.GetBytes(welcome);
server.SendTo(data, data.Length, SocketFlags.None, ipep);
//这个只是作为临时的发送对象,真正的主角是socket绑定的
IPEndPoint sender = new IPEndPoint(IPAddress.Any,0);
ClientPoint = (EndPoint)sender;
//server.Bind(ClientPoint);
data = new byte[1024];
//对于不存在的IP地址,加入此行代码后,可以在指定时间内解除阻塞模式限制
//server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 100);
int recv = server.ReceiveFrom(data, ref ClientPoint);
//使用Net自身还是Unity,根据后期测试情况来确定
#warning 目前不确定是依靠Unity驱动还是线程驱动
Udpthread = new Thread(()=>{
while (true)
{
ReceiveData();
}
});
Udpthread.IsBackground = true;
Udpthread.Start();
}
#endregion
public void SendToQueue(byte[] buffer)
{
try
{
server.SendTo(buffer, ClientPoint);
}
catch
{
Debug.LogError(DateTime.Now.ToString()+" 发送队列失败");
//添加相关的IMessage
}
}
/// <summary>
/// 数据接收以及处理流程
/// </summary>
/// <param name="buffer"></param>
private void ReceiveData()
{
// try
{
int recv = server.ReceiveFrom(data, ref ClientPoint);
byte[] buffer = new byte[recv];
Array.Copy(data, buffer, recv);
ReceiveData(buffer);
}
//catch (Exception ex)
{
// Debug.LogError("远程主机连接失败,开启重连机制"+ex.ToString());
}
}
/// <summary>
/// 后期将拆分所有的子功能
/// </summary>
/// <param name="data"></param>
public void ReceiveData(byte[] data)
{
//记录其Socket,记录所有的客户端
// Head + 地址码 + 功能码 + 数据 + 校验码 + tail
// 数据 {remote信息}
byte addressCode = data[1];
byte funCode = data[2];
byte[] buffer = new byte[data.Length - 6];
Array.Copy(data, 3, buffer, 0, buffer.Length);
int key = (addressCode << 8) + funCode;
allMethodsDic[key].Invoke(buffer);
}
/// <summary>
/// 需要主意的是缓冲的大小必须大于创建的长度
/// </summary>
/// <returns></returns>
public byte[] CreateBuffers(byte addressCode,byte funCode,byte[] data)
{
List<byte> buffer = new List<byte>();
buffer.Add(ProtocalDataConfig.Head);
buffer.Add(addressCode);
buffer.Add(funCode);
buffer.AddRange(data);
byte[] crc = ByteConvertHelper.CRC16(buffer);
buffer.AddRange(crc);
buffer.Add(ProtocalDataConfig.Tail);
return buffer.ToArray();
}
public byte[] CreateBattleBuffers(byte addressCode, byte funCode,Player player,List<IProtoBufBaseData> protoBufBaseDatas)
{
List<byte> data = new List<byte>();
data.Add(player.RoomID);
data.AddRange(ByteConvertHelper.UInt32ToByteArray(frameCount));
data.Add(player.PlayerID);
data.Add(Convert.ToByte(protoBufBaseDatas.Count));
// Head + 地址码 + 功能码 +{ roomid + id + 帧号 + 子协议数量 + 子协议名称集 + 各个子协议的长度 + 子协议数据集 + 校验码 } + tail
List<DataItem> list = new List<DataItem>();
for (int i = 0; i < protoBufBaseDatas.Count; i++)
{
byte[] buffer = ProrocolTool.Serialize(protoBufBaseDatas[i]);
string name = protoBufBaseDatas[i].GetType().Name;
list.Add(new DataItem() { Name = ModelDataConfig.ModelDataDic[name], Data = buffer });
}
//数据ID
for (int i = 0; i < list.Count; i++)
{
data.Add(list[i].Name);
}
//该数据长度
for (int i = 0; i < list.Count; i++)
{
data.Add(Convert.ToByte(list[i].Data.Length));
}
//数据
for (int i = 0; i < list.Count; i++)
{
data.AddRange(list[i].Data);
}
return CreateBuffers(addressCode,funCode, data.ToArray());
}
public Dictionary<byte, IProtoBufBaseData> ProtosDic = new Dictionary<byte, IProtoBufBaseData>();
public void ProtoInit()
{
ProtosDic.Add(ProtocalDataConfig.MoveID,new MoveData());
ProtosDic.Add(ProtocalDataConfig.SkillID, new SkillData());
ProtosDic.Add(ProtocalDataConfig.ChatID, new ChatData());
}
Thread battleTh;
long currentTime;
public void CacheData()
{
byte[] battleData = NetService.Instance.CreateBattleBuffers
(
ProtocalDataConfig.Battle_ID,
ProtocalDataConfig.Battling_ID,
GlobalStaticData.Player,
SenceMiddleWareData.BattleDataList.Values.ToList()
);
Debug.Log("发送数据");
SendToQueue(battleData);
}
//定时发送数据
public void StartBattle()
{
battleTh = new Thread(()=> {
while (true)
{
currentTime = DateTimeUtil.DateTimeToLongTimeStamp(DateTime.Now);
if (currentTime- m_LastUpdateShowTime>ConfigInfo.Interval)
{
CacheData();
m_LastUpdateShowTime = DateTimeUtil.DateTimeToLongTimeStamp(DateTime.Now);
}
}
});
battleTh.Start();
}
/// <summary>
/// 接收到的完整的数据
/// </summary>
/// <param name="data"></param>
public void DeSerlizeBattleData(byte[] data)
{
/*
*Head + 地址码 + 功能码 +
roomid + 帧号
data[0] data[1] data[2] data[3] data[4]
[
{ 该数据的长度 + id + 子协议数量 + 子协议名称集 + 各个子协议的长度 + 子协议数据集} +
{ 该数据的长度 + id + 子协议数量 + 子协议名称集 + 各个子协议的长度 + 子协议数据集} +
{ 该数据的长度 + id + 子协议数量 + 子协议名称集 + 各个子协议的长度 + 子协议数据集} +
校验码
]
+ tail
*/
#if Test && false
return;
#else
// byte head = data[0];//head
//byte addressCode = data[1];//地址码
//byte funCode = data[2];//功能码
byte roomID = data[0]; //房间ID
byte[] frameBuffer = new byte[4];
UInt32 tempframeCount = 0;//发送的帧索引
Array.Copy(data, 1, frameBuffer, 0, 4);
tempframeCount = ByteConvertHelper.BytesToUInt32(frameBuffer);
//对该房间的所有的人数进行数据划分
int offset = 0;
for (int i = 0; i < ConfigInfo.MaxPlayers; i++)
{
byte[] subData = new byte[data[5 + offset]];
Array.Copy(data, 5 + offset, subData, 0, data[5 + offset]);
offset += data[8 + offset];
//获取到各个的同步数据
//{ 该数据的长度 + id + 子协议数量 + 子协议名称集 + 各个子协议的长度 + 子协议数据集}
byte length = subData[0];
byte playerId = subData[1];
byte subCount = subData[2];
byte[] subNames = new byte[subCount];
Array.Copy(subData, 3, subNames, 0, subCount);
byte[] subLengths = new byte[subCount];
Array.Copy(subData, 3 + subCount, subLengths, 0, subCount);
int subOffset = 1 + subCount * 2;
for (int j = 0; j < subCount; j++)
{
byte[] subProData = new byte[subLengths[j]];
Array.Copy(subData, subOffset, subProData, 0, subLengths[j]);
switch (subNames[j])
{
case ProtocalDataConfig.MoveID:
Debug.Log(subData[1] + ":" + ProtocalDataConfig.MoveID);
break;
case ProtocalDataConfig.SkillID:
Debug.Log(subData[1] + ":" + ProtocalDataConfig.SkillID);
break;
}
subOffset += subLengths[j];
}
}
frameCount++;
Debug.Log($"当前是第{frameCount}帧");
#endif
}
/// <summary>
/// 业务逻辑注册
/// </summary>
private void LogicInit()
{
BaseNetLogic netLogic = new BaseNetLogic();
BindingFlags flag = BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
var infos = netLogic.GetType().GetMethods(flag);
foreach (var item in infos)
{
object[] parameters = new object[] { new byte[] { 0x00, 0x11 } };
object target = netLogic;
Func<byte[], int> del = Delegate.CreateDelegate(typeof(Func<byte[], int>), target, item) as Func<byte[], int>; // 创建一个EventHandler类型的委托
var obj = item.Invoke(netLogic, parameters);
allMethodsDic.Add(int.Parse(obj.ToString()), del);
}
GlobalStaticData.NetLogicFlag = true;
}
public void Dispose()
{
Debug.Log("程序线程退出");
if (Udpthread!=null)
{
if (Udpthread.IsAlive)
{
Udpthread.Abort();
}
}
if (battleTh != null)
{
if (battleTh.IsAlive)
{
battleTh.Abort();
}
}
}
}
}