Socket框架
来源:互联网 发布:嵌入式软件开发技术 编辑:程序博客网 时间:2024/06/05 23:51
1.构建消息包类 MessagePackage
<1> 定义包头
根据业务需求,这里有5种:
// 构造包头用的数据(包头10个字节里的内容) public int bufferSize = 0;// 消息总长度 public int protocolId = 0;// 消息ID // 网络标记包用来标记消息是否压缩、加密等信息 比如:0表示无压缩 无加密 public ushort nFlags = 0; public ushort nModuleId = 0;// 发送的模块Id 客户端和服务器之间通讯设置为0 public uint bodyLength = 0;// 包体长度
<2> 接受数据缓冲区 和 当前接受包接受字节多少的索引
public byte[] buffer;public int bufferPoint;// 用来指示当前已经接收了多少字节
<3>自定义构造方法(用来初始化buffer区和重置bufferPoint)
上代码:
public MessagePackage(int size) { CreatBuffer(size); } public void CreatBuffer(int size) { buffer = new byte[size]; bufferPoint = 0; }
<4>将数据包头信息转换为实际信息
#region 将数据包头信息转换为实际信息 // 将字节数组(长度为2)转换成数字 private ushort BytesToInt(byte[] bytes) { int value = 0; value |= (bytes[1] & 0xFF) << 8; value |= (bytes[0] & 0xFF); return (ushort)value; } // 重置字节数组依次取出n个字节出来 private byte[] GetBytes(byte[] byteArr, int length, int offset) { byte[] bytes = new byte[length]; for (int i = 0; i < length; i++) { bytes[i] = byteArr[offset + i]; } return bytes; } // 读取包头信息 public void ReadPacketHead(MessagePackage msg) { bodyLength = BytesToInt(GetBytes(msg.buffer, 2, 0)); bufferSize = BytesToInt(GetBytes(msg.buffer, 2, 2)); mouduleId = BytesToInt(GetBytes(msg.buffer, 2, 4)); flags = BytesToInt(GetBytes(msg.buffer, 2, 6)); protocolId = BytesToInt(GetBytes(msg.buffer, 2, 8)); } #endregion
<5>将要发送的数据包转换为字节数组
#region 将要发送的数据包MessagePackage转换为字节数组 // 将数字转换成两个字节长度的数组 private byte[] IntToBytes(ushort value) { byte[] bytes = new byte[2]; bytes[1] = (byte)((value >> 8) & 0xFF); bytes[0] = (byte)(value & 0xFF); return buffer; } // 将包头信息转换为字节数组之后放到buffer public void PackPacket() { List<byte> bytesList = new List<byte>(); bytesList.AddRange(IntToBytes((ushort)bodyLength)); bytesList.AddRange(IntToBytes((ushort)bufferSize)); bytesList.AddRange(IntToBytes(mouduleId)); bytesList.AddRange(IntToBytes(flags)); bytesList.AddRange(IntToBytes((ushort)protocolId)); // 此时buffer已经是我们通信的对象进行序列化(protobuf)的字节数组 bytesList.AddRange(buffer); buffer = bytesList.ToArray(); } #endregion
2.构建自己的Socket类M_Socket
<1>代理服务器 游戏服务器 地址和端口号
<2>包头长度 和 当前接受的长度
<3>构造链接
<4>开始链接 (区分代理服务器和游戏服务器)
<5>链接成功的回调(挂起异步链接)
<6>开始接收包头
<7>开始接受包体
<8>将数据添加到数据接收序列 (丢回主线程)
<9>发送数据
<10>关闭链接
上代码:
// 登录服务器IP地址 端口 string loginServerIP = ""; int loginPort = 8000; // 游戏服务器地址 端口 string gameServerIP = ""; int gamePort = 9000; // 包头大小 public int headSize = 10; int headPoint = 0; public void BuildSocket() { // 构造一个Socket 对象 长连接的方式 建立长连接socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } // 开始链接 public void StartConnect(bool isConnectionLoginServer = false) { if (isConnectionLoginServer) { socket.BeginConnect(loginServerIP, loginPort, new AsyncCallback(ConnectCallBack), socket); } else { socket.BeginConnect(gameServerIP, gamePort, new AsyncCallback(ConnectCallBack), socket); } } // 链接完成的回调 public void ConnectCallBack(IAsyncResult ar) { try { Socket tempSocket = (Socket)ar.AsyncState; // 挂起异步socket链接 tempSocket.EndConnect(ar); } catch (Exception ec) { Debug.Log("连接出错了" + ec.ToString()); return; // TODO 界面处理 //NetMgr中定义的链接成功的标志位 NetMgr.Inst.isConnectSuccess = true; // 连接成功 开始监听是否有包头发送过来 ReceivePackageHead(); } // 开始接收包头 public void ReceivePackageHead() { MessagePackage msg = new MessagePackage(headSize); socket.BeginReceive(tmsg.buffer, 0, headSize, 0, new AsyncCallback(ReceivePackageHeadHandle), msg); } // 接收到包头的回调 public void ReceivePackageHeadHandle(IAsyncResult ar) { // 获取本次接收数据的长度 int length = socket.EndReceive(ar); if (length > 0) { MessagePackage tempTmsg = ( MessagePackage)ar.AsyncState; headPoint += length; if (headPoint < headSize) { // 没接收完 继续接收 socket.BeginReceive(tempTmsg.buffer, headPoint, headSize, 0, new AsyncCallback(ReceivePackageHeadHandle), tempTmsg); } else { // 接收完毕 开始接收包体 headPoint = 0; // 开始接收包体 解析包头 得到bodyLength tempTmsg.ReadPacketHead(tempTmsg); // 根据bodyLength 重新开始初始化buffer区 tempTmsg.CreatBuffer(tempTmsg.bodyLength); // 开始接收包体 ReceivePackageBody(tempTmsg); } } else { // TODO 界面处理 没收到 Debug.Log("服务器断开"); } } public void ReceivePackageBody(MessagePackage msg) { // 继续开始接收bodyLength长度的包体 socket.BeginReceive(msg.buffer, headPoint, msg.bodyLength, 0, new AsyncCallback(ReceivePackageBodyHandle), msg); }public void ReceivePackageBodyHandle(IAsyncResult ar) { int length = socket.EndReceive(ar); if (length > 0) { // 收到包体 MessagePackage tempMsg = (MessagePackage)ar.AsyncState; tempMsg.bufferPoint += length; if (tempMsg.bufferPoint == length) { // 将数据添加到数据接收序列 (丢回主线程) // 一会构建这个类 NetMgr.Inst.receiveQueue.Enqueue(tempMsg); // 接收下一个数据包 ReceivePackageHead(); } else { // 没接收完 继续接收 socket.BeginReceive(tempMsg.buffer, tempMsg.bufferPoint, length - tempMsg.bufferPoint, 0, new AsyncCallback(ReceivePackageBodyHandle), tempMsg); } } else { // TODO 没有收到包体 Debug.Log(" "); } } // 异步发送数据的方法 public void SendMsg(MessagePackage msg) { socket.BeginSend(msg.buffer, 0, msg.buffer.Length, 0, new AsyncCallback(SendMsgHandle), socket); } // 发送数据包结束的回调 public void SendMsgHandle(IAsyncResult ar) { try { Socket tempSocket = (Socket)ar.AsyncState; tempSocket.EndSend(ar); } catch (Exception ec) { Debug.Log(ec.ToString()); } // 关闭Socket连接 public void CloseConnection() { socket.Close();// 关闭连接 Debug.Log("客户端 关闭连接"); }
3.构造NetMgr
<1>单例类
<2>2个队列 接收包队列和发送包队列
<3>代理服务器和游戏服务器的socket
<4>定义连接状态的标志位
<5>定义代理服务器链接的标志位
<6>允许重连次数和等待时间
<7>序列化反序列化
<8>重连方法实现
上代码:
private static NetMgr inst; public static NetMgr Inst { get { return inst; } } private void Awake() { inst = this; } // 是否链接成功 public bool isConnectSuccess = false; public bool isLoginServer = true; TaoSocket loginSocket; TaoSocket gameSocket; public Queue<MessagePackaget> receiveQueue = new Queue<MessagePackaget>(); public Queue<MessagePackaget> sendQueue = new Queue<MessagePackaget>(); // 链接登陆服务器 public void StartConnectLoginServer() { isConnectSuccess = false;// 断线重新置位(保障) isLoginServer = true; loginSocket = new M_Socke(); loginSocket.BuildSocket(); loginSocket.StartConnect(isLoginServer); } // 链接游戏服务器 public void StartConnectGameServer() { isConnectSuccess = false; isLoginServer = false;// 链接登陆服务器 gameSocket = new M_Socke(); gameSocket.BuildSocket(); gameSocket.StartConnect(isLoginServer); } // 发送数据 private void SendMsg(MessagePacket msg) { if (isConnectSuccess) { if (isLoginServer) { loginSocket.SendMsg(msg); } else { gameSocket.SendMsg(msg); } } } private void Update() { if (sendQueue.Count > 0) { SendMsg(sendQueue.Dequeue()); } if (receiveQueue.Count > 0) { MsgProcessing(receiveQueue.Dequeue()); } } private void MsgProcessing(MessagePacket msg) { // 开始解析数据 switch (msg.protocolId) { // 根据消息ID 反序列化字节数组 case (int)MySpace.Package.PacketTypeS2C.S2C_GetSelfItem_Response: var pack = Serialize_Deserialize.Deserialize<GetSelfItem_Response>(msg.buffer); break; // 等等.. } }// 处理重连 float waitTime = 12f; int waitCount = 0; float timer = 0; public IEnumerator WaitSocketBack() { while (!isConnectSuccess) { yield return 0; timer += Time.deltaTime; if (timer > waitTime) { break; } } if (isConnectSuccess) { // TODO 连接成功 Debug.Log("Connect is ok!"); waitCount = 0;// 重置重连次数 } else { // 超时 Debug.Log("Connect Failed! 超时!"); // 链接失败 if (waitCount < 3) { waitCount++; ContinueConnect(); } else { // 弹出tips 告知玩家 网络状况较差 请检查网络连接后重试 if (isLoginServer) { loginSocket.CloseConnection(); } else { gameSocket.CloseConnection(); } StopCoroutine("WaitSocketBack");// 停止协程 } } } // 重连 private void ContinueConnect() { if (isLoginServer) { // 重连代理服务器 loginSocket.CloseConnection();// 关闭之前连接 loginSocket.BuildSocket(); loginSocket.StartConnect(); // 调用协程 StartCoroutine("WaitSocketBack"); } else { // 重连游戏服务器 gameSocket.CloseConnection();// 关闭之前连接 gameSocket.BuildSocket(); gameSocket.StartConnect(); // 调用协程 StartCoroutine("WaitSocketBack"); } }
好了,大致就是这样,有时间再补充。
阅读全文
0 0
- Socket框架
- 可控SOCKET框架
- socket编程基本框架
- socket的服务端框架
- socket的服务端框架
- Socket网络框架C#
- flash socket 框架
- Socket 开发框架 SuperSocket
- Java 开源 SOCKET 框架
- linux socket服务器框架
- linux socket服务器框架
- SOCKET简单框架
- linux socket服务器框架
- linux socket服务器框架
- socket.io框架学习
- VB Socket编程 框架
- linux socket服务器框架
- socket编程学习1----socket编程框架
- HTML初涉
- mysql添加表注释、字段注释、查看与修改注释
- RMQ问题 在线算法-ST算法
- hibernate创建数据库
- HTML的基础框架
- Socket框架
- Cannot read property 'glob' of null
- Linux SD/MMC/SDIO驱动分析
- [Freemarker] 初识FreeMarker
- Linux设备模型(总线、设备、驱动程序和类)
- HDU5701(技巧暴力)
- Mybatis的动态sql----where,trim,set,foreach
- UVa101(模拟+基础数据结构vector)
- eclipse Alt+/提示老弹错误框