Unity3D网络通讯--Socket通讯之Tcp通讯

Posted 微卡智享

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity3D网络通讯--Socket通讯之Tcp通讯相关的知识,希望对你有一定的参考价值。



学更好的别人,

做更好的自己。

——《微卡智享》

交易担保 微信圈子 微卡时光




本文长度为2650,预计阅读8分钟




前言


UnityWebRequest通过Restful的通讯我们已经实现了,《 》章中在做Tcp通讯时因为用到了异步处理,解决了Text的最终显示问题,今天这篇我们就来看看Socket中Tcp的通讯。
Unity3D网络通讯(四)--Socket通讯之Tcp通讯


项目整理

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

微卡智享


Socket的服务端本来想用以前自己做Socket测试时写了一个Demo程序做服务端的,结果发现Demo程序不知道什么时候自己删完了,再从实际项目中截出来写个服务端比较麻烦,并且现在网上也不少Socket的测试工具,所以这里就偷个懒,不写服务端的东西了,直接使用sokit-1.3-win32-chs这个程序,下面是网盘的地址:

链接:https://pan.baidu.com/s/18VXIeyQbGKasguHcoQQ5Tg

提取码:vg8n


我们还是同样的项目,在项目中加入了一个TCP的按钮,然后把上一篇的地址输入InputField改为IP地址,另一个改为端口号输入,简单的调整一个布局后,就开始我们的代码处理即可。

项目代码

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

微卡智享

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

在Network目录下新建一个SocketTcp的C#脚本,这次我们直接用封装的方式写完,供外部调用。

01

添加属性


Unity3D网络通讯(四)--Socket通讯之Tcp通讯

定义了SocketTcp的实例,然后内部再定义好TcpClient和NetworkStream,主要是Tcp通讯就是基于这两个来实现的。

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

定义的接收处理类,因为我们这里Tcp接收是用异步进行处理的,在BeginRead的函数里面最后一个参数可以传一个object的对象,所以我们要把相关的东西都传入一个类中进行处理。

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

然后内部再定义一个传入的IP地址和端口号,下面的Instance的获取实例方法同HttpRestful的实例是一样的。

02

连接和发送


Unity3D网络通讯(四)--Socket通讯之Tcp通讯

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

Connect连接和Send发送比较简单,稍微了解一下就可以直接使用了,就算是大数据包,发送也会自动分成多个包发送过去。里面我加了try catch主要就是如果出现异常的话做一次重连再发送,这样就不用单独再写个线程做心跳处理,防止服务端主动断开连接,这块处理也会有更好的写法,我们这里就简单处理即可。


03

异步接收


其实Tcp通讯这里面最麻烦的处理就是接收数据了,像刚才说的我们发送时如果有大数据包时,socket会自动分成多个包进行发送,不用我们考虑怎么分包发,但是在接收这块怎么多包接收后合并再处理,就需要我们自己来实现了。

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

在接收方法中,我们就通过NetworkStream BeginRead来处理异步接收的,参数倒数第二个TcpDataRecvived的方法就是我们写的回调函数,最后一个传入的TransData,就是前面我们说定义这个可以在回调函数中使用传入的参数。

异步实现思路

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

上图中就是异步处理接收数据的一个实现思路,其主要的核心就是判断当前的接收包是否已经接收完,如果接收完后直接执行回调函数,未接收完存入缓存中继续接收。

实现方式

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

上面的代码就实现了我们异步接收流程思路,下面贴出整个SocketTcp的代码。

SocketTcp代码

using System;using System.Collections;using System.Collections.Generic;using System.Net.Sockets;using System.Text;using System.Threading;using UnityEngine;
public class SocketTcp : MonoBehaviour{ private static SocketTcp _instance;
//TCPClient private TcpClient _tcpClient; private NetworkStream _netStream;//得到网络流
//接收处理类 public class TransData { public TransData(Action<bool, string> action, int buffsize = 8192) { recvbuff = new byte[1000000]; tmpbuff = new byte[buffsize]; istrans = false; actionResult = action; recvsize = 0; }
public byte[] tmpbuff; public byte[] recvbuff; public int recvsize; public bool istrans; public Action<bool, string> actionResult; }
private string _ipadr; private int _port;
public static SocketTcp Instance { get { if (_instance == null) { GameObject goRestful = new GameObject("SocketTcp"); _instance = goRestful.AddComponent<SocketTcp>(); } return _instance; } }
public SocketTcp Connect(string ipadr, int port) { _ipadr = ipadr; _port = port; try { if (_tcpClient == null || !_tcpClient.Connected) { _tcpClient = new TcpClient(_ipadr, _port); _tcpClient.ReceiveBufferSize = 8192; _tcpClient.SendBufferSize = 8192; _netStream = _tcpClient.GetStream(); } } catch (System.Exception) { _tcpClient.Close(); _tcpClient.Dispose(); Connect(ipadr, port); } return Instance; }

public SocketTcp Send(string msg = "") { try { if (_netStream.CanWrite) { Byte[] sendBytes = Encoding.UTF8.GetBytes(msg); _netStream.Write(sendBytes, 0, sendBytes.Length); _netStream.Flush(); } } catch (System.Exception) { _tcpClient.Close(); _tcpClient.Dispose(); Connect(_ipadr, _port).Send(msg); } return Instance; }
public SocketTcp Recv(Action<bool, string> action = null) { TransData trans = new TransData(action, _tcpClient.ReceiveBufferSize); try { _netStream.BeginRead(trans.tmpbuff, 0, trans.tmpbuff.Length, TcpDataReceived, trans); } catch (System.Exception) { _tcpClient.Close(); _tcpClient.Dispose(); Connect(_ipadr, _port).Recv(action); } return Instance; }

private void TcpDataReceived(IAsyncResult ar) { int recv = 0; TransData transData = (TransData)ar.AsyncState; try { recv = _netStream.EndRead(ar); } catch { recv = 0; }
//判断接收数为0,并且未在接收中 if (recv == 0 && !transData.istrans) { transData = new TransData(transData.actionResult, _tcpClient.ReceiveBufferSize); _netStream.BeginRead(transData.tmpbuff, 0, transData.tmpbuff.Length, TcpDataReceived, transData); } //已在接收了,再次接收为0,说明接收完了,直接调用 else if (recv == 0) { //执行回调函数 string resstr = Encoding.UTF8.GetString(transData.recvbuff); transData.actionResult(transData.istrans, resstr); } //如果recv大于0,说明接收到了数据,修改接收值 else { transData.istrans = true;
byte[] buff = new byte[recv]; Buffer.BlockCopy(transData.tmpbuff, 0, buff, 0, recv); Buffer.BlockCopy(buff, 0, transData.recvbuff, transData.recvsize, recv); transData.recvsize += recv;
if (recv < transData.tmpbuff.Length) { //执行回调函数 string resstr = Encoding.UTF8.GetString(transData.recvbuff); int strlen = resstr.IndexOf('\0'); if (strlen > 0) { resstr = resstr.Substring(0, strlen); } transData.actionResult(transData.istrans, resstr); //执行完回调后重新初始化接收参数 transData = new TransData(transData.actionResult, _tcpClient.ReceiveBufferSize); }
_netStream.BeginRead(transData.tmpbuff, 0, transData.tmpbuff.Length, TcpDataReceived, transData); } }}

调用方法

Unity3D网络通讯(四)--Socket通讯之Tcp通讯

Unity3D网络通讯(四)--Socket通讯之Tcp通讯





实现效果




Unity3D网络通讯(四)--Socket通讯之Tcp通讯



Unity3D网络通讯(四)--Socket通讯之Tcp通讯



扫描二维码

获取更多精彩

微卡智享


交易担保 微信圈子 微卡时光


「 往期文章 」








以上是关于Unity3D网络通讯--Socket通讯之Tcp通讯的主要内容,如果未能解决你的问题,请参考以下文章

UDP之socket通讯

网络编程基础之TCP编程学习

GJM: Unity3D基于Socket通讯例子

网络编程-Socket编程(异步通讯)(Tcp,Udp)

网络编程-Socket编程(异步通讯)(Tcp,Udp)

SOCKET 封包和拆包