当前位置: 首页 > article >正文

unity c# Tcp网络通讯

本篇附带源代码,带有处理拆包分包粘包,也会撰明具体内容。

首先对于tcp就是挥手机制,三次握手四次挥手机制。

一、三次握手

具体过程为简单解释为:

1、客户端请求服务器链接,等待服务器确认。(服务器如果确认请求,就会给这个客户端随机分配一个固定且唯一的ID号码)

2、服务器收到请求后,会向客户端发送应答,进行确认。(服务器会返回给你分配的id编码,代表下一次服务器希望收到以此为地址的固定的客户端序号,如果未来涉及断线重连这里也是非常重要的信息,要进行留存)

3、客户端收到信息后,会再次向服务器进行回复,发出确认。(就此服务器确认链接被确认,客户端服务器建立起链接)

【啰嗦一下:】

【握手为什么是三次?(防止已过期的链接请求报文突然又传输到服务器从而产生错误)、(三次才能确认双方接收和发送能力都正常)、(告知对方自己的初始序号值,并确认双方的序号值是否正确)以及(节省服务器性能【如果长时间服务器分配出id等待联接,会造成资源上的浪费,同时也可能被不法之人利用危害数据安全】)

为啥是三次不是四次?

已经可以确认链接了,无需第四次握手,浪费资源

更多可访问:详解 TCP 三次握手、四次挥手,附带精美图解和超高频面试题 - HYN的技术笔记 - SegmentFault 思否

二、四次挥手

即终止链接,这与tcp的机制有关,详细可看上面的链接。这里我们只做简略描述。

1、客户端发送关闭请求。

2、服务端收到后发回确认请求。

3、这个阶段下,服务器会发送最后的一些数据信息【这里面会有一些你没发完的一些数据信息,包括还未处理的内容信息】,最后会带一个结束的标识,代表服务器将这些没发完的内容发完了。

4、此时该服务器对此客户端的服务已经基本结束了,此时会发送最后的关闭信息。

5、客户端收到确认信息,再次发送确认信息,确认关闭,此时客户端自己就被关闭了,服务器在接受到指令后也就被关闭了。

这是基础内容,然后需要知道的是虽然tcp不会丢包,但是会有沾包的问题。

这个导致的问题有很多,可能是发送方造成的,也可能是接收方造成的。

1、发送方的tcp为提高传输效率,要收集到一定的数据后才可以发送,这就可能导致接受到原本是一大段的内容信息,可能是一段一段的。

2、mss规定,如果数据包过长就会被芬开传输,这样接收方就受到了粘包数据

3、接收方接收数据不及时,卡顿造成的

4、网络传输制式不统一【跨国业务中能遇到,甚至无法链接】

因此值得注意的是一般在unity开发过程中,并不太会使用tcp,一般在小游戏中往往会比较能够见到,相对来说kcp会更多,其次是http web【UnityWebRequest】(udp:往往信息传输的都是请求更新,更新完后的东西都会在本地处理一锤子买卖,干完就不认人【tps:有些境外赌博产品就是这么干的,id号就在本地保存,抓包一抓一个准】)

先上源代码吧,解析放在后面:

客户端 unity ,c#的也可根据这个改改就能用: 下面这个是核心部分

TcpClient
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using UnityEngine;

public class Client
{
     static Client instance=new Client();
     public static Client Instance => instance;
     
     
     
     private TcpClient _client;
     
    public void Start()
    {
        _client = new TcpClient();
        Connect();
    }


    public async void Connect()//连接
    {
        try
        {
            //后端给的ip和服务器端口(这里暂时写的是本机ip)
            await _client.ConnectAsync("127.0.0.1",7788);
            Debug.Log("连接成功!");
            Receive();//连接
        }
        catch (System.Exception e)
        {
            Console.WriteLine(e.Message);//打印错误
        }
    }

    public async void Send(byte[] data)//发送消息
    {
        try
        {
            await _client.GetStream().WriteAsync(data,0,data.Length);
            Debug.Log("发送成功!");
        }
        catch (Exception e)
        {
            Debug.Log(e.Message);
            _client.Close();
        }
    }
    byte[] data = new byte[4096];
    int msgLenth = 0;//记录长度
    public async void Receive()//接收数据
    {
        try
        {
            while (_client.Connected)
            {
                byte[] buff = new byte[4096];
                int lengh=await _client.GetStream().ReadAsync(buff,0,buff.Length);
                if (lengh>0)
                {
                    Debug.Log($"接收到数据了:{lengh}");
                    
                    MassengerHelper.Instance.CopyToData(buff,lengh);
                    
                    Debug.Log(Encoding.UTF8.GetString(buff,0,lengh));
                    
                    Array.Copy(buff, 0, data, msgLenth, lengh);
                    
                  //  JsonHelper.ToObject<JsonHelper.JsonTest>(buff);
                  msgLenth += lengh;
                  Handle();
                }
                else
                {
                    _client.Close();
                }
            }
        }
        catch (Exception e)
        {
            Debug.Log(e.Message);
            _client.Close();
        }
       
    }

    private void Handle()
    {
        //注意:这里只是举个例子,一般不会这么小,空间大小一般遵循二进制准则为1024
        //然后在这里我们对消息体进行封装,减少了传输,即:客户端和服务端都拿了一个密码本
        //双方约定传输内容,比如1,为登录,2为退出。。。
        //包体大小(4) 协议ID(4) 包体(byte[])
        if (msgLenth >= 8)
        {
            byte[] _size = new byte[4];
            Array.Copy(data, 0, _size, 0, 4);
            int size = BitConverter.ToInt32(_size, 0);

            //本次要拿的长度
            var _length = 8 + size;

            if (msgLenth >= _length)
            {
                //拿出id
                byte[] _id = new byte[4];
                Array.Copy(data, 4, _id, 0, 4);
                int id = BitConverter.ToInt32(_id, 0);

                //包体
                byte[] body = new byte[size];
                Array.Copy(data, 8, body, 0, size);

                if (msgLenth > _length)
                {
                    for (int i = 0; i < msgLenth - _length; i++)
                    {
                        data[i] = data[_length + i];
                    }
                }

                msgLenth -= _length;
                Debug.Log($"收到客户端请求:{id}");
                
            }
        }
    }
   

}

然后这里有个关联代码:

public class MassengerHelper
{
    private static MassengerHelper instance=new MassengerHelper();
    public static MassengerHelper Instance => instance;
    
    byte[] data = new byte[4096];
    int msgLenth = 0;

    public void CopyToData(byte[] buffer,int length)
    {
        Array.Copy(buffer,0,data,msgLenth,length);
        msgLenth += length;
        Handle();
    }

    private void Handle()
    {
        //包体大小(4bit)协议(4)包体(byte[])
        if (msgLenth>=8) 
        {
            byte[] _size = new byte[4];
            Array.Copy(data,0,_size,0,4);
            int size=BitConverter.ToInt32(_size,0);//获知包体具体大小
            //本次要拿的长度
            var _length = 8 + size;
            if (msgLenth>=_length) 
            {
                //拿出id
                byte[] _id = new byte[4];
                Array.Copy(data,4,_size,0,4);
                int id = BitConverter.ToInt32(_id,0);

                //包体
                byte[] body = new byte[size];
                Array.Copy(data,8,body,0,size);

                if (msgLenth>_length) 
                {
                    for (int i = 0; i < msgLenth-_length; i++)
                    {
                        data[i] = data[_length];
                    }
                }

                msgLenth -= _length;
                Debug.Log($"收到客户请求:{id}");
                //添加监听
                MessanagerCenter.Instance.Dispatch(id, body);
            }
        }
    }
    
    public void SendToSever(int id,string str)
    {
        Debug.Log("ID"+id);
        var body = Encoding.UTF8.GetBytes(str);
        byte[] send_buff = new byte[body.Length+8];

        int size = body.Length;
        var _size = BitConverter.GetBytes(size);
        var _id = BitConverter.GetBytes(id);
        
        Array.Copy(_size,0,send_buff,0,4);
        Array.Copy(_id,0,send_buff,4,4);
        Array.Copy(body,0,send_buff,8,body.Length);
        
        Client.Instance.Send(send_buff);
    }
}

具体详细等我哪天有时间再更新吧,如果有错误欢迎大佬提出意见


http://www.kler.cn/a/378549.html

相关文章:

  • Java之包,抽象类,接口
  • CODESYS可视化星三角降压启动程序控制电气动画图
  • 【Vue项目1】第一篇
  • 【系统架构设计师】2023年真题论文: 论面向对象分析的应用与实现(包括解题思路和素材)
  • P11233 [CSP-S 2024] 染色 题解
  • 03:选择语句的练习
  • C++ 函数调用时的参数传递方法
  • 线性数据结构之队列
  • 【读书笔记/深入理解K8S】集群控制器
  • 《GBDT 算法的原理推导》 11-15更新决策树的叶子节点值 公式解析
  • mac 系统下载 vscode
  • 如何设置使PPT的画的图片导出变清晰
  • 自动驾驶-端到端大模型
  • 三层交换实现不同VLAN之间设备的互通
  • SQL 常用语句
  • 【系统架构设计师】2024年上半年真题论文: 论云上自动化运维级其应用(包括解题思路和素材)
  • 项目模块十四:HttpRequest模块
  • 六西格玛项目助力,手术机器人零部件国产化稳中求胜——张驰咨询
  • LLaMA系列一直在假装开源...
  • 基于YOLO11/v10/v8/v5深度学习的危险驾驶行为检测识别系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】
  • 【p2p、分布式,区块链笔记 Torrent】通过网络编程库net集成bittorrent-protocol协议
  • ps技巧,来源于网络
  • Linux -- 信号的常见产生方式
  • MySQL日志——针对实习面试
  • 聚观早报 | 苹果推出新款iMac;华为Mate 70系列将上市
  • 并发编程中的CAS思想