上篇文章介绍了Socket传输单元Byte数组的封装。本篇介绍对网络套接字Socket的扩展“传输层”封装。1)连接建立与关闭 ;2)byte数据的发送;3)服务器返回的byte数据的监听与接收【使用队列实现缓存】
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System;
using System.Net;
/*
* Autohr:W
* 网络连接套接字封装【传输层】
* 1)连接建立与关闭
* 2)数据的发送
* 3)服务器返回的数据的监听与接收【队列缓存】
*/
namespace W.NetWork
{
/// <summary>
/// 网络连接状态
/// </summary>
public enum ConnectStateType
{
Success,
Fail,
Close,
Error,
}
public class GameSocket
{
/// <summary>
/// 网络套接字
/// </summary>
private Socket socket;
/// <summary>
/// 当前网络是否处于已连接状态
/// </summary>
public bool IsConnected
{
get
{
if (socket != null)
{
return socket.Connected;
}
else
{
return false;
}
}
}
/// <summary>
/// 当前网络是否正在连接中···
/// </summary>
private bool isConnecting = false;
/// <summary>
/// 错误消息
/// </summary>
private string errorMsg = string.Empty;
/// <summary>
/// 网络连接状态变化回调
/// </summary>
private Action<ConnectStateType> OnConnectStateCallBack = null;
/// <summary>
/// 接收消息的线程
/// </summary>
private Thread receiveThread;
/// <summary>
/// 消息包头Size
/// </summary>
private const int PACK_HEAD_LENGTH = 4;
/// <summary>
/// 消息包头
/// </summary>
private byte[] receiveMsgPackHead = new byte[PACK_HEAD_LENGTH];
/// <summary>
/// 当前要接收的消息包的长度
/// </summary>
private int receiveMsgPackBodyLen = 0;
/// <summary>
/// 接收消息包数据缓冲区
/// </summary>
private MemoryStream readBuffer = new MemoryStream();
/// <summary>
/// 接收到的消息包缓存队列
/// </summary>
private Queue<byte[]> msgPackQueue = new Queue<byte[]>();
/// <summary>
/// 服务器连接
/// </summary>
/// <param name="ip"></param>
/// <param name="port"></param>
public void ConnectToServer(string ip,int port)
{
//重置
CloseServer(false);
IPAddress ipAdr = null;
IPEndPoint ipEndPoint = null;
try
{
//解析IP地址
if (IPAddress.TryParse(ip, out ipAdr))
{
ipEndPoint = new IPEndPoint(ipAdr,port);
}
else
{
IPHostEntry iPHost = Dns.GetHostEntry(ip);
ipEndPoint = new IPEndPoint(iPHost.AddressList[0],port);
}
//创建套接字
socket = new Socket(ipEndPoint.AddressFamily,SocketType.Stream,ProtocolType.Tcp);
StartCheck();
socket.BeginConnect(ipEndPoint,new AsyncCallback(ConnectAsyncCallback),socket);
}
catch (Exception e)
{
if (OnConnectStateCallBack != null)
OnConnectStateCallBack(ConnectStateType.Error);
Debug.LogError("服务器连接失败!error="+e.Message);
CloseServer(false);
}
}
/// <summary>
/// 异步连接回调处理
/// </summary>
/// <param name="ar"></param>
private void ConnectAsyncCallback(IAsyncResult ar)
{
Socket curSocket = (Socket)ar.AsyncState;
try
{
curSocket.EndConnect(ar);
}
catch (Exception e)
{
errorMsg = e.Message;
}
isConnecting = false;
Debug.Log("服务器连接 connected="+IsConnected);
}
/// <summary>
/// 发起连接前需要做的检查处理
/// </summary>
private void StartCheck()
{
isConnecting = true;
errorMsg = string.Empty;
//消息监听检查注册
GameManager.Instance.RegisterFrameCallBack(CheckUpdate);
}
/// <summary>
/// 发起连接结束后的需要做的处理
/// </summary>
private void EndCheck()
{
//消息监听检查注销
GameManager.Instance.UnRegisterFrameCallBack(CheckUpdate);
}
/// <summary>
/// 每帧检查更新,如果没有执行EndCheck();
/// </summary>
private void CheckUpdate()
{
if (!isConnecting)
{
EndCheck();
//没有连接错误
if (string.IsNullOrEmpty(errorMsg))
{
OnConnectCallBack();
}
else
{
if (OnConnectStateCallBack != null)
OnConnectStateCallBack(ConnectStateType.Error);
Debug.LogError("服务器连接失败! 错误信息="+errorMsg);
CloseServer(false);
}
}
}
/// <summary>
/// 发起一次连接的反馈的处理
/// </summary>
private void OnConnectCallBack()
{
//连接成功
if (IsConnected)
{
//接收数据的线程检查
if (receiveThread == null || !receiveThread.IsAlive)
{
receiveThread = new Thread(ReceiveSocketPackage);
receiveThread.IsBackground = true;
receiveThread.Start();
}
if (OnConnectStateCallBack != null)
OnConnectStateCallBack(ConnectStateType.Success);
Debug.Log("服务器连接成功!");
}
else
{
if (OnConnectStateCallBack != null)
OnConnectStateCallBack(ConnectStateType.Error);
Debug.LogError("服务器连接失败!");
}
}
/// <summary>
/// 开启线程,接收数据包
/// </summary>
private void ReceiveSocketPackage()
{
while (IsConnected)
{
//检查当前数据字节数是否大于一个完整消息的包头的大小字节数
if (socket.Available > PACK_HEAD_LENGTH)
{
//获取数据包的包头字节数组
socket.Receive(receiveMsgPackHead, PACK_HEAD_LENGTH, 0);
//根据包头字节数组,解析出整个消息包的长度
receiveMsgPackBodyLen = new ByteArray(receiveMsgPackHead).ReadInt();
//申请需要接收消息数据的缓冲区
byte[] receiveBuff = new byte[receiveMsgPackBodyLen];
int recvSize = 0; //当前接收到数据Size
int allRecvSize = 0; //当前已经接收到的数据总Size
int needSize = 0; //剩余需要接收的数据Size
needSize = receiveMsgPackBodyLen - recvSize;
//循环读取包的数据
while (needSize > 0)
{
//读取Socket缓冲区中包的数据
recvSize = socket.Receive(receiveBuff, needSize, 0);
//并写入到内存流中
readBuffer.Write(receiveBuff, 0, recvSize);
allRecvSize += recvSize;
needSize = receiveMsgPackBodyLen - allRecvSize;
}
//从数据缓冲区中读取数据包
ReadDataPack();
} //如果还没有,则线程休眠30毫秒,再做检查
else
{
Thread.Sleep(30);
}
}
}
/// <summary>
/// 从接收缓存区中读取数据包,放入到消息队列中
/// </summary>
private void ReadDataPack()
{
if (receiveMsgPackBodyLen <= 0)
{
Debug.LogError("收到消息,但是解析出来的消息尺寸错误,为:" + receiveMsgPackBodyLen);
readBuffer.Position = 0;
return;
}
if (readBuffer.Position != receiveMsgPackBodyLen)
{
Debug.LogError("收到消息,但是解析出来的消息尺寸与消息内容大小错误,消息总尺寸为:" + readBuffer.Length + "正常应该为:" + receiveMsgPackBodyLen);
readBuffer.Position = 0;
readBuffer.SetLength(0);
return;
}
byte[] msgPack = new byte[receiveMsgPackBodyLen];
System.Array.Copy(readBuffer.GetBuffer(), msgPack, receiveMsgPackBodyLen);
msgPackQueue.Enqueue(msgPack);
//读取完一个数据包后,读取缓冲区做清空处理
readBuffer.Position = 0;
readBuffer.SetLength(0);
}
/// <summary>
/// 获取消息
/// </summary>
/// <returns></returns>
public byte[] GetReceiveMsg()
{
if (msgPackQueue.Count > 0)
{
byte[] msg = msgPackQueue.Dequeue();
return msg;
}
if (!IsConnected)
{
CloseServer(true);
}
return null;
}
/// <summary>
/// 发送数据
/// </summary>
/// <param name="package"></param>
public void SendPack(byte[] package)
{
if (package == null) return;
if (IsConnected)
{
if (package.Length > 0)
{
try
{
socket.Send(package);
}
catch (SocketException e)
{
Debug.LogError("发送消息错误:"+e.Message);
}
}
}
}
/// <summary>
/// 服务器连接关闭
/// </summary>
/// <param name="isNeedToFire">是否需要下发连接关闭事件</param>
public void CloseServer(bool isNeedToFire)
{
if (socket == null) return;
if (IsConnected)
{
try
{
if (receiveThread != null)
{
receiveThread.Abort();
receiveThread = null;
}
msgPackQueue.Clear();
readBuffer.Position = 0;
socket.Shutdown(SocketShutdown.Both);
socket.Close();
}
catch (Exception e)
{
Debug.LogError("关闭服务器连接出错,错误信息为"+e.Message);
}
}
socket = null;
if (isNeedToFire)
{
if (OnConnectStateCallBack != null)
OnConnectStateCallBack(ConnectStateType.Close);
}
}
}
}