u3d局域网游戏网络(c# socket select 模型)
之前写了一篇。
发完之后第二天实际应用到游戏之后还是发现了一些小毛病。
比如网络模块有重复使用(多对象)的情况。所以将静态类该成了普通类。
比如安卓下会有些异常出现导致游戏逻辑不正常。所以网络相关的函数有些加了try块。
然后发现写入固定ip的方式根本不适合局域网。于是加了udp做的广播系统,用以服务器和客户端查找ip。
udp广播部分和tcp不一样。因为没有连接,所以socket不需要shutdown。我在这里吃了一亏才知道。
别的没什么修改。贴上修正和扩展之后的代码。
有缘之人自取。唯一要求,如果你发现代码有错,或者有可以提升性能的地方请留言告知。
另:因为这是为局域网设计的,所以网络部分框架以及锁的应用写得很随意,如果需要扩展至千人万人级的承载,请自行修改。
基础类(base)
ClientMsgUnPack.cs 服务器tcp部分用以解包的对象
using UnityEngine;
/*
* 通信协议
* 消息头前2字节保存当前消息长度
* 后面跟4字节表示消息ID
* 再后面是消息实质内容
*/ namespace LanSocket
{
class ClientMsgUnPack : MsgUnPack
{
long m_UserID;
public ClientMsgUnPack()
{
m_UserID = -;
} public ClientMsgUnPack(byte[] mBuff, ushort len, int userID)
{
m_UserID = userID;
UnPack(mBuff, len);
} public ClientMsgUnPack(byte[] mBuff, ushort offset, ushort len, int userID)
{
m_UserID = userID;
UnPack(mBuff, offset, len);
} public long GetUserID()
{
return m_UserID;
} public void SetUserID(long userID)
{
m_UserID = userID;
}
}
}
ClientMsgUnPack.cs
EventDispath.cs 事件分发,有两个类,分别对应服务器和客户端,主要就是参数不同
using UnityEngine;
using System.Collections;
using System.Collections.Generic; delegate void ServerEventDelagate(LanSocket.ClientMsgUnPack msg); class EventNode
{
public int m_EventID;
public LanSocket.ClientMsgUnPack msg;
} class EventDispathBase
{
public static int g_MaxEventNum = ;
} class ServerEventDispath : EventDispathBase
{
List<ServerEventDelagate>[] m_Event;
Queue<EventNode> m_EventQueue;
public ServerEventDispath()
{
m_Event = new List<ServerEventDelagate>[g_MaxEventNum];
m_EventQueue = new Queue<EventNode>();
} public void RegistEvent(int eventID, ServerEventDelagate func)
{
if(null == m_Event[eventID])
{
m_Event[eventID] = new List<ServerEventDelagate>();
}
m_Event[eventID].Add(func);
} public void AddEvent(EventNode eventNode)
{
m_EventQueue.Enqueue(eventNode);
} public void Proccess()
{
if ( != m_EventQueue.Count)
{
EventNode mCur = m_EventQueue.Dequeue();
if (null == m_Event[mCur.m_EventID])
{
MonoBehaviour.print("event ID: "+ mCur.m_EventID+" is null");
}
else
{
List<ServerEventDelagate> curEventDelagate = m_Event[mCur.m_EventID];
for(int i = ; i < curEventDelagate.Count ; ++i)
{
curEventDelagate[i](mCur.msg);
}
}
}
}
} delegate void ClientEventDelagate(LanSocket.MsgUnPack msg);
class ClientEventDispath : EventDispathBase
{
List<ClientEventDelagate>[] m_Event;
Queue<EventNode> m_EventQueue;
public ClientEventDispath()
{
m_Event = new List<ClientEventDelagate>[g_MaxEventNum];
m_EventQueue = new Queue<EventNode>();
} public void RegistEvent(int eventID, ClientEventDelagate func)
{
if (null == m_Event[eventID])
{
m_Event[eventID] = new List<ClientEventDelagate>();
}
m_Event[eventID].Add(func);
} public void AddEvent(EventNode eventNode)
{
m_EventQueue.Enqueue(eventNode);
} public void Proccess()
{
if ( != m_EventQueue.Count)
{
EventNode mCur = m_EventQueue.Dequeue();
if (null == m_Event[mCur.m_EventID])
{
MonoBehaviour.print("event ID: " + mCur.m_EventID + " is null");
}
else
{
List<ClientEventDelagate> curEventDelagate = m_Event[mCur.m_EventID];
for (int i = ; i < curEventDelagate.Count; ++i)
{
curEventDelagate[i](mCur.msg);
}
}
}
}
}
EventDispath.cs
LanSocketBase.cs 没什么实际意义,主要就是定义一些大家都会使用到的变量等
using System.Threading;
using UnityEngine; /*
*轻量级局域网服务器。
* 协议如下
* 消息头前2字节保存当前消息长度
* 后面跟4字节表示消息ID
* 再后面是消息实质内容
*/ namespace LanSocket
{
public class LanSocketBase
{
public static int m_MaxOnePackBuff = * ;
public static int m_MaxAllBuff = * ;
public static int m_HeadSize = ;
protected bool m_HasInit = false;
protected byte[] m_OnePack;
protected int m_OnePackIndex;
private Mutex m_Mutex; public void BaseInit()
{
m_HasInit = true;
m_Mutex = new Mutex();
m_OnePack = new byte[m_MaxOnePackBuff+];
m_OnePackIndex = ;
} public void BaseRelease()
{
m_Mutex.Close();
} protected void Lock()
{
m_Mutex.WaitOne();
//MonoBehaviour.print("Lock:" + Thread.CurrentThread.ManagedThreadId.ToString());
} protected void UnLock()
{
m_Mutex.ReleaseMutex();
//MonoBehaviour.print("Unlock:" + Thread.CurrentThread.ManagedThreadId.ToString());
}
}
}
LanSocketBase.cs
MsgPack.cs 打包类,参数类型不够自行扩展
using UnityEngine;
/*
* 通信协议
* 消息头前2字节保存当前消息长度
* 后面跟4字节表示消息ID
* 再后面是消息实质内容
*/ namespace LanSocket
{
public class MsgPack : PackBase
{
public MsgPack()
{
m_OnePackIndex = LanSocketBase.m_HeadSize;
} public void SetHead(int ID)
{
byte[] mBuff = System.BitConverter.GetBytes(ID);
System.Buffer.BlockCopy(mBuff, , m_OnePack, , );
} public void PackEnd()
{
byte[] mBuff = System.BitConverter.GetBytes(m_OnePackIndex);
System.Buffer.BlockCopy(mBuff, , m_OnePack, , );
} public void Packbool(bool data)
{
ushort curDatalen = ;
if (m_OnePackIndex + curDatalen > m_MaxOnePackBuff)
{
MonoBehaviour.print("Packbool() longer lager than Max buff len");
return;
}
byte[] mBuff = System.BitConverter.GetBytes(data);
Pack(mBuff, curDatalen);
} public void Pack16bit(short data)
{
ushort curDatalen = ;
if (m_OnePackIndex + curDatalen > m_MaxOnePackBuff)
{
MonoBehaviour.print("Pack16bit(short) longer lager than Max buff len");
return;
}
byte[] mBuff = System.BitConverter.GetBytes(data);
Pack(mBuff, curDatalen);
}
public void Pack16bit(ushort data)
{
ushort curDatalen = ;
if (m_OnePackIndex + curDatalen > m_MaxOnePackBuff)
{
MonoBehaviour.print("Pack16bit(ushort) longer lager than Max buff len");
return;
}
byte[] mBuff = System.BitConverter.GetBytes(data);
Pack(mBuff, curDatalen);
}
public void Pack32bit(int data)
{
ushort curDatalen = ;
if (m_OnePackIndex + curDatalen > m_MaxOnePackBuff)
{
MonoBehaviour.print("Pack32bit(int) longer lager than Max buff len");
return;
}
byte[] mBuff = System.BitConverter.GetBytes(data);
Pack(mBuff, curDatalen);
}
public void Pack32bit(uint data)
{
ushort curDatalen = ;
if (m_OnePackIndex + curDatalen > m_MaxOnePackBuff)
{
MonoBehaviour.print("Pack32bit(uint) longer lager than Max buff len");
return;
}
byte[] mBuff = System.BitConverter.GetBytes(data);
Pack(mBuff, curDatalen);
}
public void Pack32bit(float data)
{
ushort curDatalen = ;
if (m_OnePackIndex + curDatalen > m_MaxOnePackBuff)
{
MonoBehaviour.print("Pack32bit(float) longer lager than Max buff len");
return;
}
byte[] mBuff = System.BitConverter.GetBytes(data);
Pack(mBuff, curDatalen);
}
public void Pack64bit(double data)
{
ushort curDatalen = ;
if (m_OnePackIndex + curDatalen > m_MaxOnePackBuff)
{
MonoBehaviour.print("Pack64bit(double) longer lager than Max buff len");
return;
}
byte[] mBuff = System.BitConverter.GetBytes(data);
Pack(mBuff, curDatalen);
}
public void Pack64bit(long data)
{
ushort curDatalen = ;
if (m_OnePackIndex + curDatalen > m_MaxOnePackBuff)
{
MonoBehaviour.print("Pack64bit(long) longer lager than Max buff len");
return;
}
byte[] mBuff = System.BitConverter.GetBytes(data);
Pack(mBuff, curDatalen);
} public void PackString(string data, ushort len)
{
ushort curDatalen = len;
if (m_OnePackIndex + curDatalen > m_MaxOnePackBuff)
{
MonoBehaviour.print("PackString() longer lager than Max buff len");
return;
}
byte[] mBuff = System.Text.Encoding.UTF8.GetBytes(data);
Pack(mBuff, curDatalen);
} void Pack(byte[] data, ushort len)
{
System.Buffer.BlockCopy(data, , m_OnePack, m_OnePackIndex, len);
m_OnePackIndex += len;
} public byte[] GetByte()
{
return m_OnePack;
} public int GetByteLen()
{
return m_OnePackIndex;
}
}
}
MsgPack.cs
MsgUnPack.cs 解包类,返回类型不够自己扩展
using UnityEngine;
/*
* 通信协议
* 消息头前2字节保存当前消息长度
* 后面跟4字节表示消息ID
* 再后面是消息实质内容
*/ namespace LanSocket
{
class MsgUnPack : PackBase
{
ushort m_PackLen;
int m_MsgID;
public MsgUnPack()
{
} void GetHead()
{
m_PackLen = System.BitConverter.ToUInt16(m_OnePack, );
m_MsgID = System.BitConverter.ToUInt16(m_OnePack, );
m_OnePackIndex = ;
} public MsgUnPack(byte[] mBuff, ushort len)
{
UnPack(mBuff, len);
} public MsgUnPack(byte[] mBuff, ushort offset, ushort len)
{
UnPack(mBuff, offset, len);
} public void UnPack(byte[] mBuff, ushort len)
{
System.Buffer.BlockCopy(mBuff, , m_OnePack, , len);
GetHead();
} public void UnPack(byte[] mBuff, ushort offset, ushort len)
{
System.Buffer.BlockCopy(mBuff, offset, m_OnePack, , len);
GetHead();
} public bool Readbool()
{
if (m_OnePackIndex + > m_PackLen)
{
MonoBehaviour.print("Readbool() longer lager than Max buff len");
return false;
}
bool data = System.BitConverter.ToBoolean(m_OnePack, m_OnePackIndex);
++m_OnePackIndex;
return data;
} public short ReadShort()
{
if (m_OnePackIndex + > m_PackLen)
{
MonoBehaviour.print("ReadShort() longer lager than Max buff len");
return ;
}
short data = System.BitConverter.ToInt16(m_OnePack, m_OnePackIndex);
m_OnePackIndex += ;
return data;
} public ushort ReadUShort()
{
if (m_OnePackIndex + > m_PackLen)
{
MonoBehaviour.print("ReadUShortbit() longer lager than Max buff len");
return ;
}
ushort data = System.BitConverter.ToUInt16(m_OnePack, m_OnePackIndex);
m_OnePackIndex += ;
return data;
} public int ReadInt()
{
if (m_OnePackIndex + > m_PackLen)
{
MonoBehaviour.print("ReadInt() longer lager than Max buff len");
return ;
}
int data = System.BitConverter.ToInt32(m_OnePack, m_OnePackIndex);
m_OnePackIndex += ;
return data;
} public uint ReadUInt()
{
if (m_OnePackIndex + > m_PackLen)
{
MonoBehaviour.print("ReadUInt() longer lager than Max buff len");
return ;
}
uint data = System.BitConverter.ToUInt32(m_OnePack, m_OnePackIndex);
m_OnePackIndex += ;
return data;
} public float ReadFloat()
{
if (m_OnePackIndex + > m_PackLen)
{
MonoBehaviour.print("ReadFloat() longer lager than Max buff len");
return 0.0f;
}
float data = System.BitConverter.ToSingle(m_OnePack, m_OnePackIndex);
m_OnePackIndex += ;
return data;
} public double ReadDouble()
{
if (m_OnePackIndex + > m_PackLen)
{
MonoBehaviour.print("ReadDouble() longer lager than Max buff len");
return 0.0f;
}
double data = System.BitConverter.ToDouble(m_OnePack, m_OnePackIndex);
m_OnePackIndex += ;
return data;
} public long ReadLong()
{
if (m_OnePackIndex + > m_PackLen)
{
MonoBehaviour.print("ReadLong() longer lager than Max buff len");
return ;
}
long data = System.BitConverter.ToInt64(m_OnePack, m_OnePackIndex);
m_OnePackIndex += ;
return data;
} public ulong ReadULong()
{
if (m_OnePackIndex + > m_PackLen)
{
MonoBehaviour.print("ReadULong() longer lager than Max buff len");
return ;
}
ulong data = System.BitConverter.ToUInt64(m_OnePack, m_OnePackIndex);
m_OnePackIndex += ;
return data;
} public string ReadString(ushort len)
{
if (m_OnePackIndex + len > m_PackLen)
{
MonoBehaviour.print("ReadString() longer lager than Max buff len");
return "";
}
string data = System.Text.Encoding.UTF8.GetString(m_OnePack, m_OnePackIndex, len);
m_OnePackIndex += len;
return data;
} public int GetMsgID()
{
return m_MsgID;
}
}
}
MsgUnPack.cs
PackBase.cs 没什么实际意义,主要就是打包和解包类都会使用到的一些数据
using System.Threading; /*
*轻量级局域网服务器。
* 协议如下
* 消息头前2字节保存当前消息长度
* 后面跟4字节表示消息ID
* 再后面是消息实质内容
*/ namespace LanSocket
{
public class PackBase
{
protected int m_MaxOnePackBuff;
protected byte[] m_OnePack;
protected int m_OnePackIndex; public PackBase()
{
m_MaxOnePackBuff = LanSocketBase.m_MaxOnePackBuff;
m_OnePack = new byte[m_MaxOnePackBuff];
m_OnePackIndex = ;
}
}
}
PackBase.cs
SocketBase.cs 同上
using System.Net.Sockets;
using System.Threading;
using System.Net; public class SocketBase
{
protected bool m_HasInit = false;
protected Socket m_Socket;
protected Thread m_LinstenThread;
protected IPEndPoint m_IP;
protected Mutex m_Mutex;
}
SocketBase.cs
新增的广播类(broadcast)
特别说下,udp没有握手,所以socket对象在shutdown的时候会出异常,具体知识我也不知道,我是试出来的,有知道的还望不吝赐教。
ReciveBroadcast.cs 广播接收器。服务器段需要长期保持打开,因为只要有新用户加入,服务器就需要下发数据。而客户端不需要,因为客户端一但连上,就不需要广播系统了。所以服务器长期保留,客户端用完销毁
using UnityEngine;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Collections.Generic; class ReciveBroadcast : SocketBase
{
public Queue<string> m_ServerIP;
public void Start(int port)
{
if (m_HasInit)
{
return;
}
try
{
m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
m_IP = new IPEndPoint(IPAddress.Any, port);
m_Socket.Bind(m_IP);
MonoBehaviour.print("广播网络启动监听" + m_Socket.LocalEndPoint.ToString());
m_LinstenThread = new Thread(ListenClientConnect);
m_LinstenThread.Start();
m_ServerIP = new Queue<string>();
m_Mutex = new Mutex();
m_HasInit = true;
}
catch (System.Exception ex)
{
MonoBehaviour.print("Broadcast reciver Start catch:" + ex.Message);
}
} void ListenClientConnect()
{
EndPoint ep = (EndPoint)m_IP;
try
{
while (true)
{
Thread.Sleep();
byte[] data = new byte[];
int recv = m_Socket.ReceiveFrom(data, ref ep);
string stringData = System.Text.Encoding.UTF8.GetString(data, , recv);
m_Mutex.WaitOne();
m_ServerIP.Enqueue(stringData);
m_Mutex.ReleaseMutex();
MonoBehaviour.print("received: " + stringData + " from: " + ep.ToString());
}
}
catch (System.Exception ex)
{
MonoBehaviour.print("Broadcast reciver ListenClientConnect out:" + ex.Message);
}
} public void Destroy()
{
if (!m_HasInit)
{
return;
}
m_Socket.Close();
m_LinstenThread.Abort();
} public string GetIP()
{
if (!m_HasInit)
{
return "";
} try
{
m_Mutex.WaitOne();
if ( != m_ServerIP.Count)
{
m_Mutex.ReleaseMutex();
return m_ServerIP.Dequeue();
}
m_Mutex.ReleaseMutex();
}
catch (System.Exception ex)
{
MonoBehaviour.print("Broadcast GetIP catch:" + ex.Message);
return "";
}
return "";
}
}
ReciveBroadcast.cs
SendBroadcast.cs 广播发射器。同上
using UnityEngine;
using System.Net.Sockets;
using System.Net; class SendBroadcast : SocketBase
{
byte[] m_MyIP;
public void Start(int port)
{
if (m_HasInit)
{
return;
}
try
{
m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
m_IP = new IPEndPoint(IPAddress.Broadcast, port);//255.255.255.255
//m_IP = new IPEndPoint(IPAddress.Parse("192.168.255.255"), 9050); string mLocalIP = "";
string hostname = Dns.GetHostName();
IPHostEntry localHost = Dns.GetHostEntry(hostname);
for (int i = ; i < localHost.AddressList.Length; ++i)
{
if (localHost.AddressList[i].AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
//MonoBehaviour.print(localHost.AddressList[i].ToString());
mLocalIP = localHost.AddressList[i].ToString();
break;
}
} if ("".Equals(m_MyIP))
{
MonoBehaviour.print("网络检测异常。请检查网络设置或接入网络");
m_Socket.Close();
m_Socket = null;
return;
}
m_MyIP = System.Text.Encoding.UTF8.GetBytes(mLocalIP);
m_Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, );
m_HasInit = true;
}
catch (System.Exception ex)
{
MonoBehaviour.print("Broadcast sender Start catch:" + ex.Message);
}
} public void Send()
{
if(null != m_Socket)
{
MonoBehaviour.print("send a broadcast");
m_Socket.SendTo(m_MyIP, m_IP);
}
} public void Destroy()
{
if (!m_HasInit)
{
return;
}
m_Socket.Close();
}
}
SendBroadcast.cs
说明:网络模块也是根据网络上的代码加以修正和自己的经验修改完成。对于c# socket函数不熟,如果有什么地方可以优化或者用错或者传错参数,请指出赐教。
客户端网络(client)
SocketClient.cs 客户端网络模块。包含连接服务器、接受范围、处理返回数据等。
using System.Net.Sockets;
using System.Net;
using System.Threading;
using UnityEngine;
using System.Collections.Generic; /*
*轻量级局域网服务器。
* 协议如下
* 消息头前2字节保存当前消息长度
* 后面跟4字节表示消息ID
* 再后面是消息实质内容
*/ namespace LanSocket
{
class Client : LanSocketBase
{
Thread m_ReciveThread;
Socket m_Connect;
byte[] m_AllData;
int m_AllDataHead;
int m_AllDataEnd;
int m_MsgNum; public void Start(string strIP, int port)
{
if (m_HasInit)
{
return;
}
//设定服务器IP地址
IPAddress ip = IPAddress.Parse(strIP);
Socket temp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
temp.Connect(new IPEndPoint(ip, port)); //配置服务器IP与端口
MonoBehaviour.print("连接服务器成功"); BaseInit();
m_Connect = temp;
m_ReciveThread = new Thread(ReceiveMessage);
m_ReciveThread.Start();
m_AllData = new byte[LanSocketBase.m_MaxAllBuff + ];
m_AllDataHead = ;
m_AllDataEnd = ;
m_MsgNum = ;
}
catch (System.Exception ex)
{
MonoBehaviour.print("连接服务器失败: " + ex.Message);
return;
}
} private void PutDataToBuff(byte[] mClientSendBuff, int mReceiveNumber)
{
if (m_AllDataEnd + mReceiveNumber >= LanSocketBase.m_MaxAllBuff)
{
byte[] mCurAllData = new byte[m_AllDataEnd - m_AllDataHead];
System.Buffer.BlockCopy(m_AllData, m_AllDataHead, mCurAllData, , m_AllDataEnd - m_AllDataHead);
System.Buffer.BlockCopy(mCurAllData, , m_AllData, , m_AllDataEnd - m_AllDataHead);
m_AllDataEnd -= m_AllDataHead;
m_AllDataHead = ;
}
int mOnePackStartPos = ;
while (mReceiveNumber > )
{
if ( == m_OnePackIndex)
{
ushort datalen = System.BitConverter.ToUInt16(mClientSendBuff, mOnePackStartPos);
if (datalen <= mReceiveNumber)
{
System.Buffer.BlockCopy(mClientSendBuff, mOnePackStartPos, m_AllData, m_AllDataEnd, datalen);
m_AllDataEnd += datalen; mOnePackStartPos += datalen; mReceiveNumber -= datalen;
++m_MsgNum;
}
else
{
System.Buffer.BlockCopy(mClientSendBuff, mOnePackStartPos, m_OnePack, m_OnePackIndex, mReceiveNumber);
m_OnePackIndex += mReceiveNumber;
mOnePackStartPos += mReceiveNumber; mReceiveNumber -= mReceiveNumber;
}
}
else
{
ushort datalen = System.BitConverter.ToUInt16(m_OnePack, );
if (m_OnePackIndex + mReceiveNumber >= datalen)
{
int mNeedNum = datalen - m_OnePackIndex;
System.Buffer.BlockCopy(mClientSendBuff, mOnePackStartPos, m_OnePack, m_OnePackIndex, mNeedNum);
mOnePackStartPos += mNeedNum; System.Buffer.BlockCopy(m_OnePack, , m_AllData, m_AllDataEnd, datalen);
m_OnePackIndex = ; mReceiveNumber -= mNeedNum;
}
else
{
System.Buffer.BlockCopy(mClientSendBuff, mOnePackStartPos, m_OnePack, m_OnePackIndex, mReceiveNumber);
m_OnePackIndex += mReceiveNumber;
mOnePackStartPos += mReceiveNumber; mReceiveNumber -= mReceiveNumber;
}
}
}
} public void Destroy()
{
if (!m_HasInit)
{
return;
}
BaseRelease();
ShutDownConnect();
m_MsgNum = ;
} public void GetMsg(ref MsgUnPack msg)
{
if (!m_HasInit)
{
return;
}
try
{
Lock();
if ( != m_MsgNum)
{
ushort datalen = System.BitConverter.ToUInt16(m_AllData, m_AllDataHead);
msg = new MsgUnPack(m_AllData, (ushort)m_AllDataHead, (ushort)datalen);
m_AllDataHead += datalen;
--m_MsgNum;
}
}
finally
{
UnLock();
}
} /// <summary>
/// 接收消息
/// </summary>
public void ReceiveMessage()
{
while (true)
{
Thread.Sleep();
try
{
//通过clientSocket接收数据
byte[] mClientSendBuff = new byte[m_MaxOnePackBuff + ];
int mReceiveNumber = m_Connect.Receive(mClientSendBuff);
if ( == mReceiveNumber)
{
MonoBehaviour.print("disconnect");
ShutDownConnect();
}
else if (mReceiveNumber > )
{
try
{
Lock();
PutDataToBuff(mClientSendBuff, mReceiveNumber);
}
catch (System.Exception ex)
{
MonoBehaviour.print("PutDataToBuff catch: " + ex.Message);
}
finally
{
UnLock();
}
}
else
{
MonoBehaviour.print("one connect recive a error num: " + mReceiveNumber.ToString());
}
}
catch (System.Exception ex)
{
MonoBehaviour.print("ReceiveMessage catch: " + ex.Message);
ShutDownConnect();
}
}
} public void Send(ref MsgPack msg)
{
try
{
Lock();
m_Connect.Send(msg.GetByte(), msg.GetByteLen(), SocketFlags.None);
}
finally
{
UnLock();
}
} public void ShutDownConnect()
{
m_ReciveThread.Abort();
if (m_Connect.Connected)
{
m_Connect.Shutdown(SocketShutdown.Both);
}
m_Connect.Close();
}
}
}
SocketClient.cs
服务器网络(server)
SocketServer.cs 服务器网络模块。同上,额外有玩家池。
using System.Net.Sockets;
using System.Net;
using System.Threading;
using UnityEngine;
using System.Collections.Generic;
/*
*轻量级局域网服务器。
* 协议如下
* 消息头前2字节保存当前消息长度
* 后面跟4字节表示消息ID
* 再后面是消息实质内容
*/ namespace LanSocket
{
class ClientConnect
{
public byte[] m_AllData;
public int m_AllDataHead;
public int m_AllDataEnd;
public int m_MsgCount;
public byte[] m_OnePack;
public int m_OnePackIndex;
public Socket m_Connect;
public long m_UserID; public ClientConnect()
{
m_AllData = new byte[LanSocketBase.m_MaxAllBuff];
m_AllDataHead = ;
m_AllDataEnd = ;
m_MsgCount = ;
m_OnePack = new byte[LanSocketBase.m_MaxOnePackBuff];
m_OnePackIndex = ;
m_Connect = null;
m_UserID = ;
} public void Reset()
{
m_AllDataHead = ;
m_AllDataEnd = ;
m_MsgCount = ;
m_OnePackIndex = ;
m_Connect = null;
m_UserID = ;
}
}
class Server : LanSocketBase
{
Queue<int> m_MsgOrder; Socket m_ServerSocket;
Thread m_LinstenThread;
Thread m_ReciveThread;
System.Collections.ArrayList m_ServerSocketList;
System.Collections.ArrayList m_listenSocketList;
System.Collections.ArrayList m_DeleteSocketList;
int m_MaxClientConnect = ;
ClientConnect[] m_ConnectPool;
Queue<int> m_EmptyConnect;
public void Start(int port)
{
if (m_HasInit)
{
return;
}
string mLocalIP = ""; string mHostName = Dns.GetHostName();
IPHostEntry localHost = Dns.GetHostEntry(mHostName);
for (int i = ; i < localHost.AddressList.Length; ++i)
{
if (localHost.AddressList[i].AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
//MonoBehaviour.print(localHost.AddressList[i].ToString());
mLocalIP = localHost.AddressList[i].ToString();
break;
}
} if ("".Equals(mLocalIP))
{
MonoBehaviour.print("网络检测异常。请检查网络设置或接入网络");
return;
}
BaseInit();
m_MsgOrder = new Queue<int>(); //服务器IP地址
IPAddress ip = IPAddress.Parse(mLocalIP);
m_ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
m_ServerSocket.Bind(new IPEndPoint(ip, port)); //绑定IP地址:端口
m_ServerSocket.Listen(); //设定最多10个排队连接请求
MonoBehaviour.print("游戏网络启动监听" + m_ServerSocket.LocalEndPoint.ToString()); m_ServerSocketList = new System.Collections.ArrayList();
m_listenSocketList = new System.Collections.ArrayList();
m_DeleteSocketList = new System.Collections.ArrayList(); m_ConnectPool = new ClientConnect[m_MaxClientConnect];
m_EmptyConnect = new Queue<int>();
for (int i = ; i < m_MaxClientConnect; ++i)
{
m_ConnectPool[i] = new ClientConnect();
m_EmptyConnect.Enqueue(i);
}
//通过Clientsoket发送数据
m_ReciveThread = new Thread(ReceiveMessage);
m_ReciveThread.Start();
m_LinstenThread = new Thread(ListenClientConnect);
m_LinstenThread.Start();
} /// <summary>
/// 监听客户端连接
/// </summary>
public void ListenClientConnect()
{
while (true)
{
Thread.Sleep();
m_ServerSocketList.Add(m_ServerSocket);
Socket.Select(m_ServerSocketList, null, null, );
for (int i = ; i < m_ServerSocketList.Count; ++i)
{
Socket clientSocket = ((Socket)m_ServerSocketList[i]).Accept();
if (null != clientSocket)
{
try
{
Lock();
if ( == m_EmptyConnect.Count)
{
MonoBehaviour.print("链接已经达到最大上线,丢弃当前连接");
clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
}
else
{
//m_listenSocketList.Add(clientSocket);
int mSlot = m_EmptyConnect.Dequeue();
m_ConnectPool[mSlot].m_Connect = clientSocket;
m_ConnectPool[mSlot].m_UserID = System.DateTime.Now.ToFileTime();
MonoBehaviour.print("成功连接一个客户端,编号:" + mSlot.ToString());
}
}
finally
{
UnLock();
}
}
}
m_ServerSocketList.Clear();
}
} private bool PutDataToBuff(byte[] mClientSendBuff, int mReceiveNumber, Socket client)
{
ClientConnect curPlayer = null;
int mSlot = -;
for (int i = ; i < m_MaxClientConnect; ++i)
{
if (client == m_ConnectPool[i].m_Connect)
{
curPlayer = m_ConnectPool[i];
mSlot = i;
break;
}
}
if (null == curPlayer)
{
return false;
}
if (curPlayer.m_AllDataEnd + mReceiveNumber >= LanSocketBase.m_MaxAllBuff)
{
byte[] mCurAllData = new byte[curPlayer.m_AllDataEnd - curPlayer.m_AllDataHead];
System.Buffer.BlockCopy(curPlayer.m_AllData, curPlayer.m_AllDataHead, mCurAllData, , curPlayer.m_AllDataEnd - curPlayer.m_AllDataHead);
System.Buffer.BlockCopy(mCurAllData, , curPlayer.m_AllData, , curPlayer.m_AllDataEnd - curPlayer.m_AllDataHead);
curPlayer.m_AllDataEnd -= curPlayer.m_AllDataHead;
curPlayer.m_AllDataHead = ;
}
int mOnePackStartPos = ;
while (mReceiveNumber > )
{
if ( == m_OnePackIndex)
{
ushort datalen = System.BitConverter.ToUInt16(mClientSendBuff, mOnePackStartPos);
if (datalen > LanSocketBase.m_MaxOnePackBuff || datalen < LanSocketBase.m_HeadSize)
{
return false;
}
if (datalen <= mReceiveNumber)
{
System.Buffer.BlockCopy(mClientSendBuff, mOnePackStartPos, curPlayer.m_AllData, curPlayer.m_AllDataEnd, datalen);
curPlayer.m_AllDataEnd += datalen;
mOnePackStartPos += datalen; mReceiveNumber -= datalen; m_MsgOrder.Enqueue(mSlot);
}
else
{
System.Buffer.BlockCopy(mClientSendBuff, mOnePackStartPos, m_OnePack, m_OnePackIndex, mReceiveNumber);
m_OnePackIndex += mReceiveNumber;
mOnePackStartPos += mReceiveNumber; mReceiveNumber -= mReceiveNumber;
}
}
else
{
ushort datalen = System.BitConverter.ToUInt16(m_OnePack, );
if (datalen > LanSocketBase.m_MaxOnePackBuff || datalen < LanSocketBase.m_HeadSize)
{
return false;
}
if (m_OnePackIndex + mReceiveNumber >= datalen)
{
int mNeedNum = datalen - m_OnePackIndex;
System.Buffer.BlockCopy(mClientSendBuff, mOnePackStartPos, m_OnePack, m_OnePackIndex, mNeedNum);
mOnePackStartPos += mNeedNum; System.Buffer.BlockCopy(m_OnePack, , curPlayer.m_AllData, curPlayer.m_AllDataEnd, datalen);
m_OnePackIndex = ; mReceiveNumber -= mNeedNum; m_MsgOrder.Enqueue(mSlot);
}
else
{
System.Buffer.BlockCopy(mClientSendBuff, mOnePackStartPos, m_OnePack, m_OnePackIndex, mReceiveNumber);
m_OnePackIndex += mReceiveNumber;
mOnePackStartPos += mReceiveNumber; mReceiveNumber -= mReceiveNumber;
}
}
} return true;
} /// <summary>
/// 接收消息
/// </summary>
public void ReceiveMessage()
{
try
{
while (true)
{
Thread.Sleep();
for (int i = ; i < m_MaxClientConnect; ++i)
{
if (null != m_ConnectPool[i].m_Connect)
{
m_listenSocketList.Add(m_ConnectPool[i].m_Connect);
}
}
if ( == m_listenSocketList.Count)
{
continue;
}
Socket.Select(m_listenSocketList, null, null, );
for (int i = ; i < m_listenSocketList.Count; ++i)
{
Socket mClient = (Socket)m_listenSocketList[i];
//try
//{
//通过clientSocket接收数据
byte[] mClientSendBuff = new byte[m_MaxOnePackBuff];
int mReceiveNumber = mClient.Receive(mClientSendBuff);
if ( == mReceiveNumber)
{
m_DeleteSocketList.Add(mClient);
}
else if (mReceiveNumber > )
{
try
{
Lock();
bool rt = PutDataToBuff(mClientSendBuff, mReceiveNumber, mClient);
if (!rt)
{
m_DeleteSocketList.Add(mClient);
}
}
catch (System.Exception ex)
{
MonoBehaviour.print("PutDataToBuff catch: " + ex.Message);
}
finally
{
UnLock();
}
}
else
{
MonoBehaviour.print("one connect recive a error num: " + mReceiveNumber.ToString());
}
//}
//catch (System.Exception ex)
//{
// MonoBehaviour.print("ReceiveMessage catch: " + ex.Message);
// m_DeleteSocketList.Add(mClient);
//}
}
m_listenSocketList.Clear();
if ( != m_DeleteSocketList.Count)
{
ShutDownConnect();
}
} }
catch (System.Exception ex)
{
MonoBehaviour.print("ReceiveMessage out:" + ex.Message);
} } /// <summary>
/// 程序退出销毁
/// </summary>
public void Destroy()
{
if (!m_HasInit)
{
return;
}
m_LinstenThread.Abort();
m_ReciveThread.Abort();
m_listenSocketList.Clear(); for (int i = ; i < m_ServerSocketList.Count; ++i)
{
Socket mServer = (Socket)m_ServerSocketList[i];
if (mServer.Connected)
{
mServer.Shutdown(SocketShutdown.Both);
}
mServer.Close();
}
m_ServerSocketList.Clear(); for (int i = ; i < m_MaxClientConnect; ++i)
{
if (null != m_ConnectPool[i].m_Connect)
{
if (m_ConnectPool[i].m_Connect.Connected)
{
m_ConnectPool[i].m_Connect.Shutdown(SocketShutdown.Both);
}
m_ConnectPool[i].m_Connect.Close();
m_ConnectPool[i].m_Connect = null;
}
}
m_EmptyConnect.Clear();
BaseRelease();
} /// <summary>
/// 销毁一个连接
/// </summary>
void ShutDownConnect()
{
try
{
Lock();
for (int j = ; j < m_DeleteSocketList.Count; ++j)
{
Socket connect = (Socket)m_DeleteSocketList[j];
for (int i = ; i < m_MaxClientConnect; ++i)
{
if (connect == m_ConnectPool[i].m_Connect)
{
connect.Shutdown(SocketShutdown.Both);
connect.Close();
m_ConnectPool[i].Reset();
m_EmptyConnect.Enqueue(i);
MonoBehaviour.print("关闭一个连接,编号:" + i.ToString());
break;
}
}
}
}
catch (System.Exception ex)
{
MonoBehaviour.print("ShutDownConnect catch: " + ex.Message);
}
finally
{
m_DeleteSocketList.Clear();
UnLock();
}
} /// <summary>
/// 获取一个数据
/// </summary>
public void GetMsg(ref ClientMsgUnPack msg)
{
if(!m_HasInit)
{
return;
}
try
{
Lock();
if ( != m_MsgOrder.Count)
{
int mSlot = m_MsgOrder.Dequeue();
ClientConnect curPlayer = m_ConnectPool[mSlot];
ushort mOnePackLen = System.BitConverter.ToUInt16(curPlayer.m_AllData, curPlayer.m_AllDataHead);
msg = new ClientMsgUnPack(curPlayer.m_AllData, (ushort)curPlayer.m_AllDataHead, (ushort)mOnePackLen, mSlot);
msg.SetUserID(curPlayer.m_UserID);
curPlayer.m_AllDataHead += mOnePackLen;
}
}
finally
{
UnLock();
}
} public void SendTo(ref MsgPack msg, long userID)
{
try
{
Lock();
for(int i = ; i < m_MaxClientConnect ; ++i)
{
ClientConnect curPlayer = m_ConnectPool[i];
if (null != curPlayer.m_Connect && curPlayer.m_UserID == userID)
{
curPlayer.m_Connect.Send(msg.GetByte(), msg.GetByteLen(), SocketFlags.None);
break;
}
}
}
finally
{
UnLock();
}
} public void SendToAll(ref MsgPack msg)
{
try
{
Lock();
for (int i = ; i < m_MaxClientConnect; ++i)
{
ClientConnect curPlayer = m_ConnectPool[i];
if (null != curPlayer.m_Connect)
{
curPlayer.m_Connect.Send(msg.GetByte(), msg.GetByteLen(), SocketFlags.None);
break;
}
}
}
finally
{
UnLock();
}
}
}
}
SocketServer.cs
外部配套模块
ClientMain.cs u3d下的一个启动客户端网络模块的控件
using UnityEngine;
using System.Collections; public class ClientMain : MonoBehaviour { // Use this for initialization
ClientEventDispath m_Msg;
SendBroadcast m_Sender;
ReciveBroadcast m_Reciver;
LanSocket.Client m_GameNet;
string m_GameServerIP;
bool m_bReady;
float m_BroadTime; void Start ()
{
m_Sender = new SendBroadcast();
m_Sender.Start();
m_Reciver = new ReciveBroadcast();
m_Reciver.Start(); m_GameNet = new LanSocket.Client(); m_GameServerIP = ""; m_bReady = false;
m_BroadTime = 0.0f; EventDispathBase.g_MaxEventNum = (int)NetMsgID.NET_MSG_END;
m_Msg = new ClientEventDispath();
m_Msg.RegistEvent((int)NetMsgID.S2C_SEND_ANIMAL_DATA, Action_S2C_SEND_ANIMAL_DATA);
} // Update is called once per frame
void Update ()
{
if (m_bReady)
{
LanSocket.MsgUnPack msg = null;
m_GameNet.GetMsg(ref msg);
if (null != msg)
{
print("here have one msg on client");
} if (Input.GetKeyUp(KeyCode.Space))
{
LanSocket.MsgPack sendMsg = new LanSocket.MsgPack();
sendMsg.SetHead((int)NetMsgID.C2S_SELECT_ANIMAL);
sendMsg.Pack16bit();
sendMsg.PackEnd();
m_GameNet.Send(ref sendMsg);
print("send 1");
}
}
else
{
m_GameServerIP = m_Reciver.GetIP();
if ("".Equals(m_GameServerIP))
{
m_BroadTime -= Time.deltaTime;
if(m_BroadTime - Time.deltaTime < 0.0f)
{
m_BroadTime = 5.0f;
m_Sender.Send();
}
}
else
{
print("get broadcast ip:" + m_GameServerIP);
GameStart();
}
}
}
void OnDestroy()
{
m_GameNet.Destroy();
if(null != m_Reciver)
{
m_Reciver.Destroy();
}
if (null != m_Sender)
{
m_Sender.Destroy();
}
} void GameStart()
{
m_bReady = true;
m_GameNet.Start(m_GameServerIP, );
try
{
m_Reciver.Destroy();
m_Sender.Destroy();
}
catch (System.Exception ex)
{
MonoBehaviour.print("GameStart catch:" + ex.Message);
}
m_Reciver = null;
m_Reciver = null;
} void Action_S2C_SEND_ANIMAL_DATA(LanSocket.MsgUnPack msg)
{
}
}
ClientMain.cs
Common.cs 定义的网络通信的消息ID
using UnityEngine;
using System.Collections; enum NetMsgID
{
NET_MSG_START = ,
S2C_SEND_ANIMAL_DATA,
C2S_SELECT_ANIMAL, NET_MSG_END,
}
Common.cs
ServerMain.cs u3d下的一个启动服务器网络模块的控件
using UnityEngine;
using System.Collections; public class ServerMain : MonoBehaviour
{
bool m_Destroy;
ServerEventDispath m_ClientMsg;
ReciveBroadcast m_Reciver;
SendBroadcast m_Sender;
LanSocket.Server m_GameNet;
void Start ()
{
m_Destroy = false;
//广播
m_Reciver = new ReciveBroadcast();
m_Reciver.Start();
m_Sender = new SendBroadcast();
m_Sender.Start(); //游戏网络
m_GameNet = new LanSocket.Server();
m_GameNet.Start(); m_ClientMsg = new ServerEventDispath();
m_ClientMsg.RegistEvent(, Action_123);
} // Update is called once per frame
void Update ()
{
if(!m_Destroy)
{
LanSocket.ClientMsgUnPack clientMsg = null;
m_GameNet.GetMsg(ref clientMsg);
if (null != clientMsg)
{
print("Msg:" + clientMsg.GetMsgID() + " from: " + clientMsg.GetUserID()); EventNode mNode = new EventNode();
mNode.m_EventID = clientMsg.GetMsgID(); ;
mNode.msg = clientMsg;
m_ClientMsg.AddEvent(mNode);
} if(!"".Equals(m_Reciver.GetIP()))
{
m_Sender.Send();
} m_ClientMsg.Proccess();
}
} void OnDestroy()
{
m_Destroy = true;
m_GameNet.Destroy();
m_Reciver.Destroy();
m_Sender.Destroy();
} void Action_123(LanSocket.ClientMsgUnPack msg)
{
long userID = msg.GetUserID();
ushort accountLen = msg.ReadUShort();
string account = msg.ReadString(accountLen);
ushort passLen = msg.ReadUShort();
string pass = msg.ReadString(passLen); print("Action_123 account: " + account + " pass word: " + pass+" from user: " + userID); LanSocket.MsgPack sendMsg = new LanSocket.MsgPack();
sendMsg.SetHead();
string strAccount = "test account";
sendMsg.Pack16bit((ushort)strAccount.Length);
sendMsg.PackString(strAccount, (ushort)strAccount.Length);
string strPass = "test pass word";
sendMsg.Pack16bit((ushort)strPass.Length);
sendMsg.PackString(strPass, (ushort)strPass.Length);
sendMsg.PackEnd();
m_GameNet.SendTo(ref sendMsg, msg.GetUserID());
}
}
ServerMain.cs
伙计们,有代码分享出来。利人利己。
你问为什么会利己?
等你换了工作找不到原本的代码的时候你就知道传到网上是一件幸福的事。
u3d局域网游戏网络(c# socket select 模型)的更多相关文章
- u3d局域网游戏网络(c# socket select 模型)——续
原文:http://www.cnblogs.com/saucerman/p/5555793.html 因为项目要加语音.语音数据都非常大.所以顺带就把之前写的网络模块一起测试了. 然后发现了一些bug ...
- c# socket select 模型代码(u3d)
其实写过多次网络链接.但是因为换了工作,又没电脑在身边,所以以前的代码都没办法翻出来用. 所以从今天起,一些常用的代码只好放到网上. 公司有一个局域网的游戏.本来想用u3d的rpc就可以完成.但是后来 ...
- socket select模型
由于socket recv()方法是堵塞式的,当多个客户端连接服务器时,其中一个socket的recv调用时,会产生堵塞,使其他连接不能继续. 如果想改变这种一直等下去的焦急状态,可以多线程来实现(不 ...
- socket select()模型
转载:http://www.cnblogs.com/xiangshancuizhu/archive/2012/10/05/2711882.html 由于socket recv()方法是阻塞式的,当有多 ...
- C# Socket select模型
http://www.cnblogs.com/Clingingboy/archive/2011/07/04/2097806.html http://www.cnblogs.com/RascallySn ...
- socket之 select模型
前段时间一直想学习网络编程的select模型,看了<windows网络编程>的介绍,参考了别人的博客. 这里的资料主要来自http://www.cnblogs.com/RascallySn ...
- 网络编程第六讲Select模型
网络模型第六讲Select模型 一丶Select模型是什么 以前我们讲过一个迭代模型.就是只服务一个客户端连接.但是实际网络编程中.复杂的很多. 比如一个 C/S架构程序 (客户端/服务端) 客户端很 ...
- windows socket编程select模型使用
int select( int nfds, //忽略 fd_ser* readfds, //指向一个套接字集合,用来检测其可读性 ...
- socket编程的select模型
在掌握了socket相关的一些函数后,套接字编程还是比较简单的,日常工作中碰到很多的问题就是客户端/服务器模型中,如何让服务端在同一时间高效的处理多个客户端的连接,我们的处理办法可能会是在服务端不停的 ...
随机推荐
- Python 服务器端表单验证插件
Python格式验证库 Cerberus 作者 MrStranger 关注 2016.08.02 14:44 字数 2140 阅读 79评论 0喜欢 1 Cerberus是一个验证Python对象.M ...
- windows批处理学习(for和字符串)---03
[1]for命令简介 先把for循环与for命令类比一下,这样学习理解快. for 循环语句,一般格式如下: 1 for (表达式1;表达式2;表达式3) 2 { 3 循环体; 4 } 1. 表达式1 ...
- 第二部分shell编程2正则(grepegrepsedawk)
一.grep/egrep 1. 语法+选项语法: grep [-cinvABC] 'word' filename -c :打印符合要求的行数-n :在输出符合要求的行的同时连同行号一起输出 -v :打 ...
- c++ new 堆 栈
根据32位的Windows系统默认有2GB的用户空间,则不能new超过2GB的,执行下列代码: ***]; 会出现下面的错误 error C2148: 数组的总大小不得超过 0x7fffffff 字节 ...
- K-means聚类算法与EM算法
K-means聚类算法 K-means聚类算法也是聚类算法中最简单的一种了,但是里面包含的思想却不一般. 聚类属于无监督学习.在聚类问题中,给我们的训练样本是,每个,没有了y. K-means算法是将 ...
- iOS-系统 图片、视频 管理控制器UIImagePickerController
UIImagePickerController 是一个管理系统多媒体文件库(相册)中的图片.视频文件的视图控制器,诞生于iOS4之前,虽然功能不是很完善,我们仍可以用这个视图控制器做一些有创造 ...
- hdu5575 Discover Water Tank
题意: 给出个水箱,水箱两侧有无限高的隔板,水箱内有整数高度的隔板将水箱分成n-1份,现在给出m个限制,每个限制表示某个位置的某个高度有水或没水,问最多能同时满足多少个限制.n,m<=2*10^ ...
- 【bzoj4491】我也不知道题目名字是什么 离线扫描线+线段树
题目描述 给定一个序列A[i],每次询问l,r,求[l,r]内最长子串,使得该子串为不上升子串或不下降子串 输入 第一行n,表示A数组有多少元素接下来一行为n个整数A[i]接下来一个整数Q,表示询问数 ...
- Java入门之:对象和类
Java对象和类 Java作为一种面向对象语言,支持以下基本概念: 多态 继承 封装 抽象 类 对象 实例 方法 重载 本节我们重点研究对象和类的概念: 对象: 对象是类的一个实例,有状态和行为.例如 ...
- Django 2.0 学习(11):Django setuptools
应用打包 当前状态的Python包与各种工具有点儿混乱,本结我们将学习使用setuptools来构建应用包.该工具是强烈推荐使用的打包工具,之后我们也会使用pip去安装和卸载它. Python打包指的 ...