游戏实时网络通信实现【Socket与FlatBuffer协议】(三)

本文详细介绍了网络套接字Socket的传输层封装方法,包括连接建立与关闭、数据发送及服务器返回数据的监听与接收等核心功能,并通过队列实现缓存机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上篇文章介绍了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);
			}
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Data菌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值