【Unity&C#】运行在主线程的Socket
来源:互联网 发布:react.js阮一峰 编辑:程序博客网 时间:2024/06/05 01:07
通常的Socket都是通过多线程的方式来实现的,多线程需要确保线程安全,而且代码量也会相对多一些,由于之前已经实现了Unity的协程功能,现在就可以通过协程来实现单线程的Socket了。
首先,封装一下C#的Socket。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net;using System.Net.Sockets;using FirBase;namespace NxNetwork{ public class NxSocket { private Socket _socket; public NxSocket() { _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); } public bool Connect(string ip, int port) { return Connect(new IPEndPoint(IPAddress.Parse(ip), port)); } public bool Connect(IPEndPoint ipPoint) { bool connected = false; try { FirLog.v(this, "连接服务器:" + ipPoint.Address.ToString()); _socket.NoDelay = true; _socket.Connect(ipPoint); _socket.Blocking = false; connected = true; } catch(Exception ex) { FirLog.v(this, "连接出现异常:" + ex.Message); } return connected; } public bool Disconnect() { bool success = false; try { _socket.Shutdown(SocketShutdown.Both); _socket.Close(); success = true; } catch (Exception ex) { FirLog.v(this, "断开连接出现异常:" + ex.Message); } return success; } public bool Send(byte[] buf, int lenght) { bool success = false; try { _socket.Send(buf, lenght, SocketFlags.None); success = true; } catch(SocketException ex) { FirLog.v(this, "发送出现异常:" + ex.Message); } return success; } public int Receive(byte[] buf, int offset, int size) { return _socket.Receive(buf, offset, size, SocketFlags.None); } public int Receive(ref byte[] buf) { int lenght = 0; try { lenght = _socket.Receive(buf); } catch(Exception ex) { FirLog.v(this, "接受出现异常:" + ex.Message); } return lenght; } public int Available { get { try { if (_socket != null) { lock (_socket) { return _socket.Available; } } } catch(Exception ex) { FirLog.v(this, "Available出现异常:" + ex.Message); } return 0; } } public bool Connected { get { if(_socket != null && _socket.Connected) { return true; } return false; } } public void Dispose() { if(_socket != null) { lock(_socket) { if (_socket.Connected) { _socket.Shutdown(SocketShutdown.Both); } _socket.Close(); _socket = null; } } FirLog.v(this, "析构Socket"); } }}
这只是包装了一些Socket的接口,可以看到,我们包装的这个Socket是通过NoBlock的方式来接受数据的,所以接收数据的方法Receive不会阻塞线程,这就给我们的协程提供了可能。
下面是通过协程实现的NetworkManager
using FirBase;using NxNetwork.MSG;using System;using System.Collections;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace NxNetwork{ public class NetworkNoThreadManager : FirSingleton<NetworkNoThreadManager> { private NxSocket _socket = new NxSocket(); private byte[] _recvData = new byte[GlobalVar.BUFFER_LENGTH]; //消息接受缓存 private byte[] _recvDataTmp = new byte[GlobalVar.BUFFER_LENGTH]; private UInt32 _recvOffset = 0; Dispatcher _commDispatcher = new Dispatcher(); public NetworkNoThreadManager() { } public void RegisterMsg<T>(CallBack<T> handle) { _commDispatcher.Reg(handle); } public void Init() { FirTask.Instance.AddTask(Recv()); } public void ConnectServer(string ip, int port) { _socket.Connect(ip, port); } public void SendMsg<T>(T data) { var bData = SerializationHelper.Serialization(data); ProtoMsg msg = new ProtoMsg(bData.Length, DateTime.Now.ToBinary(), data.GetType().Name, bData); var msgData = msg.ToBinary(); SendMsg(msgData); } public void SendMsg(byte[] data) { _socket.Send(data, data.Length); if (_socket == null || !_socket.Connected) { //TODO 重连 } } private IEnumerator Recv() { while (true) { if (_socket != null && _socket.Connected) { if (_socket.Available > 0) { int receiveLength = _socket.Receive(ref _recvData); if (receiveLength != 0) { Array.Copy(_recvData, 0, _recvDataTmp, _recvOffset, receiveLength); _recvOffset += (UInt32)receiveLength; if (_recvOffset > GlobalVar.HEAD_SIZE) { byte[] receiveData = new byte[_recvOffset]; Array.Copy(_recvDataTmp, receiveData, _recvOffset); Array.Clear(_recvDataTmp, 0, _recvDataTmp.Length); _recvOffset = 0; RecvDataHandle(receiveData); } } } } yield return new WaitForEndOfFrame(); } } private void RecvDataHandle(byte[] receiveData) { ProtoMsg msg = SerializationHelper.BinaryDeSerialization<ProtoMsg>(receiveData); byte[] data = new byte[msg.Lenght]; Array.Copy(msg.MsgBuffer, data, msg.Lenght); _commDispatcher.MsgDispatcher(msg.MsgName, data); } }}
在main函数需要这样启动
NetworkNoThreadManager.Instance.Init(); NetworkNoThreadManager.Instance.ConnectServer("127.0.0.1", 8885);
之后我们可以像上一篇博客那样注册消息,首先建立一个消息处理类:
class LoginHandle { public LoginHandle() { NetworkNoThreadManager.Instance.RegisterMsg(new CallBack<UserInfo>(onUserInfo)); } public void onUserInfo(UserInfo info) { FirLog.v(this, "接受到用户信息:" + info.accid + "密码:" + info.pwd); } }
然后同样在main函数中创建这个类。
LoginHandle handle = new LoginHandle();
然后就可以接受消息了
源码以及测试工程
http://download.csdn.net/detail/baijiajie2012/9481989
1 0
- 【Unity&C#】运行在主线程的Socket
- android在主线程运行
- Service 运行在主线程
- 关于“运行在主线程的ContentProvider为什么不会影响主线程”的记录
- 关于“运行在主线程的ContentProvider为什么不会影响主线程”的记录
- [iOS diary]iOS在非主线程运行主线程的方法
- socket用在主线程中
- 在非主线程运行NSURLConnection
- android Toast运行在主线程中
- android中network的连接不能在直接在主线程运行
- Android4.0版本以上不能在主线程中访问socket的解决办法
- 接sdk的时候,在主线程下运行的方法
- 在主线程的handler里开子线程 主线程会卡主
- iOS 通知观察者的被调函数不一定运行在主线程
- iOS 通知观察者的被调函数不一定运行在主线程
- android 判断app是否运行在主线程中
- android之postDelayed是否运行在主线程中解答
- Android运行在主线程和子线程浅释
- Mix-in 类
- OpenGL基础图形编程(二)OpenGL概念建立
- 第六周项目四-静态成员应用
- 【HEVC学习与研究】38、HEVC编码过程中的块分割结构
- 数据挖掘的基本任务与要解决的问题(数据挖掘入门)
- 【Unity&C#】运行在主线程的Socket
- 用c语言读一张图片
- 同步和互斥
- 笔试携程经验
- Tsinsen A1127 方格取数
- hadoop启动后,长久不用,无法关闭
- Mysql 不同版本 说明
- SQL语言基础入门总结
- javaEE