前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Unity3D网络通讯(四)--Socket通讯之Tcp通讯

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

作者头像
Vaccae
发布2020-09-18 15:41:00
3.2K0
发布2020-09-18 15:41:00
举报
文章被收录于专栏:微卡智享

前言

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代码

代码语言:javascript
复制
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);
        }
    }
}

调用方法

实现效果

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-09-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 异步实现思路
  • SocketTcp代码
  • 调用方法
相关产品与服务
大数据
全栈大数据产品,面向海量数据场景,帮助您 “智理无数,心中有数”!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档