最近公司需要用unity里的项目与某硬件相连,写了一个简单的通信功能,不足之处望不吝赐教!
int BUFFER_SIZE=51200; //定义接受Buffer长度
public Socket clientSocket; //存储客户端实例
public Socket serverSocket; //服务端实例
public Queue recQueue; //接受信息队列
byte[] receiveBuffer=new byte[51200]; //创建一块缓冲
public List<Socket> clientSocketList=new List<Socket>(); //存储所有连接客户端对象(未做标识处理)
byte[] cacheBuffer=null; //保存一条完整数据
//开启异步监听
public void BeginSocket(int port)
{
string ip="";
IPHostEntry ipEntry=Dns.GetHostEntry(Dns.GetHostName());
foreach(IPAddress _ip in ipEntry.AddressList)
{
if(_ip.AddressFamily==AddressFamily.InterNetwork)
{
ip=_ip.ToString(); //获取本地IP
}
}
IPEndPoint ipendpoint=new IPEndPoint(IPAddress.Parse(ip),port); //定义节点
serverSocket =new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocoType.Tcp);
serverSocket.Bind(ipendpoint); //绑定节点
serverSocket.Listen(20); //开启监听队列
serverSocket.BeginAccept(AcceptCallback,serverSocket); //开启异步监听回调
}
//连接客户端回调函数
public void AcceptCallback(IAsyncResult re)
{
Socket ser=re.AsyncState as Socket;
try
{
CliSocket=ser.EndAccept(re); //结束accept接受客户端clientsocket
clientSocketList.Add(CliSocket); //将客户端socket存到链表
cliSocket.BeginReceive(receiveBuffer,0,receiveBuffer.Length,SocketFlags.None,ReceiveCallBack,cliSocket); //开启接受信息回调函数
ser.BeginAccept(AcceptCallbacck,ser); //再次开启回调
}
catch(SocketException se)
{
Debug.Log(se.Message);
}
catch(Exception ce)
{
Debug.Log(ce);
throw;
}
}
//接受信息回调函数
public void ReceiceCallBack(IAsyncResult re)
{
Socket socketClient=null;
try
{
socketClient=re.IAsyncState as Socket;
clientSocket=socketClient;
int receiveLen=clientsocket.EndReceive(re); //接受客户端发送信息,返回值为字节数据长度
if(receiveLen<=0) //接受<=0字节,客户端请求断开服务端
{
Debug.Log("客户端已正常断开");
socketClient.Close();
return;
}
//缓存为空则新建字节数组 (此处处理粘包分包问题)
if(cacheBuffer==null)
{
cacheBuffer=new Byte[receiveLen];
Array.Copy(receiveBuffer,cacheBuffer,receiveLen); //将缓冲区内容复制给cache字节数组
}
else
{
//缓存不为空,进行拼接
byte[] t=new byte[cacheBuffer.Length+receiveLen];
Array.Copy(cacheBuffer,t,cacheBuffer.Length);
Array.Copy(receiveBuffer,0,t,cacheBuffer.Length,receiveLen);
cacheBuffer=t; //此处将cache字节数组内容与缓冲区字节数组进行拼接
}
//数据长度小于等于一个int字节长度则继续接受
if(cacheBuffer.Length<=4)
{
socketClient.BeginReceive(receiveBuffer,0,receiveBuffer.Length,SocketFlags.None,ReceiveCallBack,socketClient); //继续接受缓冲数据
return;
}
int msg=BitConverter.ToInt32(cacheBUffer,0); //取出包头数据
while(cacheBuffer!=null&&msgl+4<=cacheBuffer.Length) //判断缓存包内至少含有一个完整的数据
{
byte[] msgbyte=new byte[msg];
Array.Copy(cacheBuffer,4,msgbyte,0,msg);
//拿到完整数据执行操作
recQueue.Enqueue(msgbyte); //将一条完整数据放入队列中
if(msg+4==cacheBuffer.Length) //包头数据正好与缓存包大小一致,再无粘包分包
{
cacheBuffer=null; //将chache数组归为null
}
else
{
//缓存cacheBuffer中仍有数据
byte[] tmpbyte=new byte[cacheBuffer.Length-msg-4];
Array.Copy(cacheBuffer,msg+4,tmpbyte,0,cacheBuffer.Length-msg-4);
cacheBuffer=tmpbyte; //chache数组保留取出一整条数据后剩下的数据
}
}
socketClent.BeginReceive(receiveBuffer,0,receiveBuffer.Length,SocketFlags.None,ReceiveCallBack,socketClient);
}
catch(Exception ce){
Debug.Log(ce.message);
throw;
}
}
这是unity中通讯部分用到的几个主要接口,通过BeginSocket开启服务端监听模式,使用的异步通信方式,处理了粘包分包问题,只考虑了客户端正常断线(服务端接受0字节内容),暂时未考虑物理断线(发送心跳包)。
粘包分包处理的思想是首先对网络数据进行约束,--->(包头)int[包体长度]+(包体)byte[具体数据],缓冲区接受到信息后,使用一个cacheBuffer字节数组来进行判断,具体内容看上述代码,基本上可以满足粘包分包的问题。