Unity3D网络通讯(四)--Socket通讯之Tcp通讯
前言
UnityWebRequest通过Restful的通讯我们已经实现了,《笔记|Unity异步处理与UI Text显示的问题》章中在做Tcp通讯时因为用到了异步处理,解决了Text的最终显示问题,今天这篇我们就来看看Socket中Tcp的通讯。
项目整理
微卡智享
Socket的服务端本来想用以前自己做Socket测试时写了一个Demo程序做服务端的,结果发现Demo程序不知道什么时候自己删完了,再从实际项目中截出来写个服务端比较麻烦,并且现在网上也不少Socket的测试工具,所以这里就偷个懒,不写服务端的东西了,直接使用sokit-1.3-win32-chs这个程序,下面是网盘的地址:
链接:https://pan.baidu.com/s/18VXIeyQbGKasguHcoQQ5Tg
提取码:vg8n
我们还是同样的项目,在项目中加入了一个TCP的按钮,然后把上一篇的地址输入InputField改为IP地址,另一个改为端口号输入,简单的调整一个布局后,就开始我们的代码处理即可。
项目代码
微卡智享
在Network目录下新建一个SocketTcp的C#脚本,这次我们直接用封装的方式写完,供外部调用。
01
添加属性
定义了SocketTcp的实例,然后内部再定义好TcpClient和NetworkStream,主要是Tcp通讯就是基于这两个来实现的。
定义的接收处理类,因为我们这里Tcp接收是用异步进行处理的,在BeginRead的函数里面最后一个参数可以传一个object的对象,所以我们要把相关的东西都传入一个类中进行处理。
然后内部再定义一个传入的IP地址和端口号,下面的Instance的获取实例方法同HttpRestful的实例是一样的。
02
连接和发送
Connect连接和Send发送比较简单,稍微了解一下就可以直接使用了,就算是大数据包,发送也会自动分成多个包发送过去。里面我加了try catch主要就是如果出现异常的话做一次重连再发送,这样就不用单独再写个线程做心跳处理,防止服务端主动断开连接,这块处理也会有更好的写法,我们这里就简单处理即可。
03
异步接收
其实Tcp通讯这里面最麻烦的处理就是接收数据了,像刚才说的我们发送时如果有大数据包时,socket会自动分成多个包进行发送,不用我们考虑怎么分包发,但是在接收这块怎么多包接收后合并再处理,就需要我们自己来实现了。
在接收方法中,我们就通过NetworkStream BeginRead来处理异步接收的,参数倒数第二个TcpDataRecvived的方法就是我们写的回调函数,最后一个传入的TransData,就是前面我们说定义这个可以在回调函数中使用传入的参数。
异步实现思路
上图中就是异步处理接收数据的一个实现思路,其主要的核心就是判断当前的接收包是否已经接收完,如果接收完后直接执行回调函数,未接收完存入缓存中继续接收。
实现方式
上面的代码就实现了我们异步接收流程思路,下面贴出整个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(' ');
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);
}
}
}
调用方法
实现效果
- 重磅:如何玩转kylin
- 体验SQL Server 2008 Express
- oracle、mysql 分页查询比较
- RGW性能监控及源码实现
- JSP中文乱码的产生原因及解决方案
- 比较Swing中三种注册事件的方法
- Kafka单节点至集群的安装部署及注意事项
- JavaScript onchange 事件:表单文本域或选择域发生改变
- 利用反射比较两个对象字段值是否相等
- 更新Silverlight ctp到Silverlight beta 1.0
- JSON.stringify(), JSON.parse(), toJSON()方法使用
- compression功能测试
- 源码阅读再来一发:解读RGW中request的处理流程
- 绘制Crushmap分布图-Python
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Android实现实时搜索框功能
- 浅谈Android轻量级的数据缓存框架RxCache
- Android开发实现消除屏幕锁的方法
- Android中js和原生交互的示例代码
- 浅谈android获取设备唯一标识完美解决方案
- Android开发实现长按返回键弹出关机框功能
- Android View 完美实现EditText 在软键盘上边的示例
- Android蓝牙通信之搜索蓝牙设备
- Android listview定位到上次显示的位置的实现方法
- Android开发实现生成excel的方法详解
- Android ToolBar 修改边距的实现方法
- Android 自定义view实现TopBar效果
- Android 客户端RSA加密的实现方法
- Android圆形头像拍照后“无法加载此图片”的问题解决方法(适配Android7.0)
- Android 中Failed to read key from keystore解决办法