Unity3d自定义TCP消息替代UNet实现网络连接

以前使用UNet实现网络连接,Unity2018以后被弃用了。要将以前的老程序升到高版本,最开始打算使用Mirro,结果发现并不好用。那就只能自己写连接了。

1.TCP消息结构

(1). TCP消息是按流传输的,会发生粘包。那么在发射和接收消息时就需要对消息进行打包和解包。如果接收的消息长度不足,先不处理,继续接收。

(2).当TCP客户端断开时,服务端是收不到通知的。解决的方法是通过是否收到自定义消息来判断客户端是否在线。

这里用的消息结构如下,

第1部分为4个字节,表示消息长度,包括消息ID和消息体;

第2部分为2个字节,表示消息ID;

第3部分为n个字节,表示消息体;

2.辅助类和插件

(1). UnityThread:接收消息时在子线程中进行,处理消息后更新界面则只能在主线程中进行,这个类就是为了把实子线程中的有些操作放到主线程中。

(2). Newtonsoft.Json:这个插件可以实现Json字符串与Json对象之间的转换。

3.注意事项

(1). 将服务端和客户端设置为可后台运行Application.runInBackground = true

(2). UnityThread使用之前一定要初始化UnityThread.initUnityThread();

4.服务端代码

TcpServerScript .cs

using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.UI;

public class TcpServerScript : MonoBehaviour
{
    public Image imageTcpServerStatus;
    public Text textConnectCount;
    public Text textPrompt;

    //上一次接收到消息时间,客户端是否在线以本数组为准
    Dictionary<int, TcpClientInfo> dictConnectId2Client;
    public Dictionary<int, float> dictConnectId2Time;
    private Dictionary<int, ClientRemainData> dictConnectId2RemainData;

    private int port = 6001;
    /// <summary>

    private TcpListener tcpListener;
    private bool running = false;
    /// <summary>
    /// Background thread for TcpServer workload.  
    /// </summary>  
    private Thread tcpListenerThread;

    int globalConnectId = 1;

    void Awake()
    {
        UnityThread.initUnityThread();
    }

    void Start()
    {
        dictConnectId2Time = new Dictionary<int, float>();
        dictConnectId2Client = new Dictionary<int, TcpClientInfo>();
        dictConnectId2RemainData = new Dictionary<int, ClientRemainData>();

        StartCoroutine(delayStartTcpServer());
    }

    IEnumerator delayStartTcpServer()
    {
        yield return new WaitForSeconds(0.5f);
        try
        {
            tcpListener = new TcpListener(IPAddress.Any, 6001);
            tcpListener.Start();
            imageTcpServerStatus.color = Color.green;
        }
        catch (Exception ex)
        {
            imageTcpServerStatus.color = Color.gray;
            Debug.Log(ex.Message);
            yield break;
        }

        tcpListenerThread = new Thread(new ThreadStart(ListenForIncommingRequests));
        tcpListenerThread.IsBackground = true;
        tcpListenerThread.Start();
    }

    private void Update()
    {
        if (Time.frameCount % 10 == 0)
        {
            List<int> keys = dictConnectId2Time.Keys.ToList();
            for (int i = 0; i < keys.Count; i++)
            {
                int connectId = keys[i];
                if (Time.time - dictConnectId2Time[connectId] > 3.0f)
                {
                    OnServerDisconnected(connectId);
                }
            }

            //StringMsg msg = new StringMsg();
            //msg.str = "hello";
            //keys = dictConnectId2Client.Keys.ToList();
            //for (int i = 0; i < keys.Count; i++)
            //{
            //    int key = keys[i];
            //    ServerSendOne(dictConnectId2Client[key].client,MessageId.MsgId_StringMsg, msg);
            //}
        }

        textConnectCount.text = string.Format("连接数:{0}", dictConnectId2Client.Count);
    }

    private void ListenForIncommingRequests()
    {
        running = true;
        ThreadPool.QueueUserWorkItem(this.ListenerWorker, null);
    }
    private void ListenerWorker(object token)
    {
        while (running)
        {
            TcpClient connectedTcpClient = tcpListener.AcceptTcpClient();
            TcpClientInfo clientInfo = new TcpClientInfo(connectedTcpClient, globalConnectId);
            UnityThread.executeInUpdate(() =>
            {
                if (!dictConnectId2Client.ContainsKey(clientInfo.connectionId))
                {
                    dictConnectId2Client.Add(clientInfo.connectionId, clientInfo);           

                    IPEndPoint endPoint = connectedTcpClient.Client.RemoteEndPoint as IPEndPoint;
                    string clientIp = endPoint.Address.ToString();
                    Debug.Log(clientIp);
                }
            });

            Debug.Log("连接:" + dictConnectId2Client.Count);
            ThreadPool.QueueUserWorkItem(this.HandleClientWorker, clientInfo);
        }
        tcpListener.Stop();
    }

    private void HandleClientWorker(object token)
    {
        var clientInfo = token as TcpClientInfo;
        int connId = clientInfo.connectionId;
        if (!dictConnectId2RemainData.ContainsKey(clientInfo.connectionId))
        {
            dictConnectId2RemainData.Add(clientInfo.connectionId, new ClientRemainData(clientInfo));
        }

        using (var client = clientInfo.client as TcpClient)
        using (var nwStream = client.GetStream())
        {
            while (running)
            {
                byte[] bufNumber = new byte[1000];
                int byReadNumber = nwStream.Read(bufNumber, 0, 1000);
                if (byReadNumber < 1)
                {
                    dictConnectId2Client.Remove(clientInfo.connectionId);
                    Debug.Log("断开1:" + clientInfo.connectionId);
                    break;
                }

                byte[] btsAdd = new byte[dictConnectId2RemainData[connId].lenRemain + byReadNumber];

                if (dictConnectId2RemainData[connId].lenRemain > 0)
                {
                    //拼接上次处理的字节
                    Array.Copy(dictConnectId2RemainData[connId].btsRemain, 0, btsAdd, 0, dictConnectId2RemainData[connId].lenRemain);
                }
                Array.Copy(bufNumber, 0, btsAdd, dictConnectId2RemainData[connId].lenRemain, byReadNumber);

                List<byte> listRemain = new List<byte>();
                dealwithData(clientInfo, btsAdd, listRemain);
                if (listRemain.Count > 0)
                {
                    byte[] btsTemp = listRemain.ToArray();
                    Array.Copy(btsTemp, 0, dictConnectId2RemainData[connId].btsRemain, 0, btsTemp.Length);
                    dictConnectId2RemainData[connId].lenRemain = btsTemp.Length;
                }
                else
                {
                    Array.Clear(dictConnec
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值