• [Unity3d] 网络开发基础【个人复习笔记/有不足之处欢迎斧正/侵删】


    TCP/IP

    TCP/IP协议是一 系列规则(协议)的统称,他们定义了消息在网络间进行传输的规则
    是供已连接互联网的设备进行通信的通信规则

    OSI模型只是一个基本概念,而TCP/IP协议是基于这个概念的具体实现

    TCP和UDP协议

    TCP:传输控制协议,面向连接,更安全,效率较低,一对一
    UDP:用户数据报协议,无连接,不保证可靠性,效率较高,随意组合

    TCP

    是面向连接的协议,也就是说,在收发数据前,必须和对方建立可靠的连接
    并且在消息传送过程中是有顺序的,并且是不会丢包(丢弃消息)的
    如果某一条消息在传送过程中失败了,会重新发送消息,直到成功

    三次握手 四次挥手

    三次握手建立连接
    第一次握手(C->S)
    TCP连接请求
    第二次握手(S->C)
    TCP授予连接
    第三次握手(C->S)
    TCP确认连接

    四次挥手断开连接
    第一次挥手(C->S)
    客户端告诉服务器数据已经发完,如果服务器还有消息就请快发完
    第二次挥手(S->C)
    服务器告诉客户端继续等待服务器的消息
    第三次挥手(S->C)
    服务器告诉客户端消息发送完成,可以正式断开连接
    第四次挥手(C- ->S)
    客户端告诉服务器等一会如果没有收到服务器回复就断开 了

    提供可靠的服务,通过TCP连接传送的数据,做到无差错、不丢失、不重复、且按顺序到达

    UDP

    是一种无需建立连接就可以发送封装的IP数据包的方法,提供了面向事务的简单不可靠信息传送服务

    具有资源消耗小,处理速度快的特点

    UDP协议不像TCP协议需要建立连接有三次握手和四次挥手,当使用UDP协议发送信息时会直接把信息数据扔到网络上,所以也就造成了UDP的不可靠性。信息在这个传递过程中是有可能丢失的虽然UDP是一个不靠谱的协议,但是由于它不需要建立连接。也不会像TCP协议那样携带更多的信息,所以它具有更好的传输效率
     

    网络游戏通信方案

    Socket\HTTP\FTP

    Socket:网络嵌套字

    HTTP:超文本传输协议,主要完成短链接网络游戏需求

    FTP:主要用来完成资源的下载和上传

    1. byte [] ipAddress = new byte[]{118,102,111,11};
    2. IPAddress ip1 =new IPAddress(ipAddress);
    3. //用byte进行初始化
    4. //使用字符串进行初始化
    5. IPAdress ip =IPAddress.Parse("118.102.111.11");
    6. //ipi27.0.0.1代表本机地址
    7. //命名空间System.Net
    8. IPEndPoint ipPoint =new IPEndPoint(0x79666F0B,8080);
    9. IPEndPoint ipPoint2 =new IPEndPoint(IPAddress.Parse("118.102.111.11"),8080);

    域名解析

    也叫域名指向,服务器设置,域名配置

    域名系统是互联网上的一种服务,管理名字与IP的对应关系

    1. //IPHostEntry类
    2. //域名解析后的返回值,可以获取该对象的IP地址主机名等信息
    3. //DNS类
    4. print(Dns.GetHostName());
    5. //获取主机名字
    6. //获取指定域名的ip地址
    7. //可能会阻塞主线程
    8. IPHostEntry entry = Dns.GetHostEntry("www.baidu.com");
    9. for(int i=0;i
    10. print(entry.AddressList[i]);
    11. }

    Socket 套接字

    c#用于提供网络通信的一个类

    类名:Socket          命名空间:System.Net.Socket

    Socket套接字时支持TCP/IP网络通信的基本操作单位

    一个套接字包括:本机地址IP和端口/对方主机的IP地址和端口/双方通信的协议信息

    一个Socket对象表示一个本地或者远程嵌套字信息,可被视为一个数据通道,链接服务器和客户端,可以接受数据的发送和接收

    适合长连接的网络游戏

    AddressFamily 网络寻址 枚举类型,决定寻址方案
            InterNetwork IPv4寻址
            InterNetwork6 IPv6寻址

    SocketType 嵌套字枚举类型 决定使用的套接字类型
            Dgram 支持数据报,最大长度固定,无连接,不可靠的消息(主要用于UDP通信)
            Stream 支持可靠、双向、基于连接的字节流(主要用于TCP通信)

    ProtocolType 协议类型枚举,决定套接字使用的通信协议

            TCP

            UDP

    流套接字

    主要实现TCP通信

    数据报套接字

    主要实现UDP通信

    1. //TCP流套接字
    2. Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    3. //UDP
    4. Socket socketUdp = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

    实现服务端基本逻辑

    1. //创建套接字
    2. Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    3. //用bind方法将套接字与本地地址绑定
    4. //端口号要大于1024,且不能被占用
    5. IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
    6. try
    7. {
    8. socketTcp.Bind(ipPoint);
    9. }
    10. catch (Exception e){
    11. return;
    12. }
    13. //用LISTEN监听
    14. socketTcp.Listen(1024);
    15. //accept等待客户端连入
    16. //建立连接,返回套接字
    17. Socket socketClient =socketTcp.Accept();
    18. //收发数据
    19. socketClient.Send(Encoding.UTF8.GetBytes("HFUT"));
    20. byte[] result=new byte[1024];
    21. int receiveNum=socketClient.Receive(result);
    22. //返回值为接收到的数量
    23. socketClient.Shutdown(SocketShutdown.Both);
    24. socketClient.Close();

    实现客户端基本逻辑

    1. //创建套接字
    2. Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    3. //与服务端相连
    4. IPEndPoint ipPoint =new IPEndPoint(IPAddress.Parse("127.0.0.0"), 8080);
    5. //上述应填写服务端的IP地址
    6. try
    7. {
    8. socket.Connect(ipPoint);
    9. }
    10. catch(SocketException e) {
    11. if(e.ErrorCode ==10061)
    12. {
    13. }
    14. else
    15. {
    16. }
    17. }
    18. //收发数据
    19. byte[] data = new byte[1024];
    20. int num = socket.Receive(data);
    21. //发送数据
    22. socket.Send(Encoding.UTF8.GetBytes("HFUTER"));
    23. socket.Shutdown(SocketShutdown.Both);
    24. socket.Close();

    区分消息类型

    发送自定义类的二进制信息(需要继承BaseData类)

    区分不同消息:给发送的消息添加标识,比如添加消息ID

    例如选择int做消息ID,那么热前四个字节为消息ID,后面为数据类的内容

    实现:

            1.创建一个消息基类,基类继承BaseData,基类添加获取消息的ID的方法或者属性

            2.让想要被发送的消息继承该类,实现序列化反序列化的方法

            3.写客户端和服务端收发处理消息的逻辑

    分包与黏包

    网络通信中由于各种因素造成的消息与消息之间出现的两种状态

    分包:一个消息分成了多个消息进行发送

    黏包:一个消息和另一个消息黏在一起

    两者可能同时发生

    解决办法:

    可以通过消息的长度来判断是否出现分包或者黏包

    为消息添加长度,消息长度记录消息的长度

    心跳信息

    在长连接中,客户端和服务端之间定期发送的一种特殊数据包,通知对方自己还在线,以保证长连接的有效性

    其发送时间间隔是固定的,且持续,因此称之为心跳消息

    心跳消息可以避免非正常关闭客户端时,服务器无法正常收到关闭连接消息,同时避免客户端长期不发消息,防火墙或者路由器会断开连接

    当客户端主动断开时,服务端无法得知客户端已经主动断开

    在客户端中可以使用Disconect方法,看是否因为之前直接Close()从而没有调用Disconect造成服务端无法及时获取状态(仍然无法准确地让服务端得知客户端已经断开连接)

    可以考虑自定义退出消息

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class QuitMsg : BaseMsg
    5. {
    6. //继承自消息基类
    7. //主动发送一条断开连接的消息给服务端
    8. //重写函数
    9. public override int GetBytesNum()
    10. {
    11. return 8;
    12. }
    13. public override int Reading(byte[] bytes, int beginIndex = 0)
    14. {
    15. return 0;
    16. }
    17. public override byte[] Writing()
    18. {
    19. int index = 0;
    20. byte[] bytes = new byte[GetBytesNum()];
    21. WriteInt(bytes, GetID(), ref index);
    22. WriteInt(bytes, 0, ref index);
    23. return bytes;
    24. }
    25. public override int GetID()
    26. {
    27. return 1003;
    28. }
    29. }

    实现心跳消息

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class HeartMsg : BaseMsg
    5. {
    6. //继承消息基类
    7. public override int GetBytesNum()
    8. {
    9. return 8;
    10. }
    11. public override int Reading(byte[] bytes, int beginIndex = 0)
    12. {
    13. //不需要反序列化任何变量
    14. return 0;
    15. }
    16. public override byte[] Writing()
    17. {
    18. int index = 0;
    19. byte[] bytes = new byte[GetBytesNum()];
    20. WriteInt(bytes, GetID(), ref index);
    21. WriteInt(bytes, 0, ref index);
    22. return bytes;
    23. }
    24. public override int GetID()
    25. {
    26. return 999;
    27. }
    28. }

    客户端:定时发送消息

    服务端:收心跳消息 记录收到消息的时间

    异步通信

    方法中的逻辑还没执行完,便继续执行后面的内容

    UNITY中的协同程序中的某系异步方法,有的使用的是多线程有的使用的是迭代器分步执行

    1. //线程回调
    2. public void CountDownAsync(int second, UnityAction callBack)
    3. {
    4. Thread t = new Thread(() =>
    5. {
    6. while (true)
    7. {
    8. print(second);
    9. Thread.Sleep(1000);
    10. --second;
    11. if (second == 0)
    12. break;
    13. }
    14. //?的目的是,如果是空的,则不会执行
    15. callBack?.Invoke();
    16. });
    17. t.Start();
    18. print("开始倒计时");
    19. }
    20. //让函数分布执行
    21. public async void CountDownAsync(int second)
    22. {
    23. print("倒计时开始");
    24. //await->Task通过线程池开启一个线程
    25. //本质上将函数分布执行
    26. await Task.Run(() =>
    27. {
    28. while (true)
    29. {
    30. print(second);
    31. Thread.Sleep(1000);
    32. --second;
    33. if (second == 0)
    34. break;
    35. }
    36. });
    37. print("倒计时结束");
    38. }

    UCP通信

    udp不会对数据包进行合并发送,不会出现黏包问题

    UDP是不可靠连接,消息传递过程中可能出现无序、丢包等情况

    为了避免分包,建议控制消息的大小在MTU(最大传输单元)范围内

    MTU:

    最大传输单元,用来通知对方所能接受数据服务单元的最大尺寸

    局域网:1472字节以内     互联网:548字节以内

    如果想要发送的消息过大,可以进行手动分包,但是手动分包的前提是解决UDP的无序和丢包的问题

    UDP流程更简单,效率高,但是不可靠

    FTP

    是支持因特网文件传输的各种规则的集合,使得文件可以被从一台主机拷贝到另一台主机上。此外,FTP还提供登录、文件查询以及其他绘画控制等功能

    FTP本质上是TCP通信通过FTP,双发至少需要简历两个TCP连接,一个称为控制连接,一个成为数据连接

    FTP的数据连接和控制连接方向一般是相反的

    两种传输方式:1.ASCII传输    2.二进制传输

    FTP关键类

    1.NetworkCredential 通信凭证类 

    命名空间:System.Net

    用于Ftp文件传输,设置账号密码

    NetworkCredrntial n =new NetworkCredrntial("HFUTER","hfuter");


    2.FtpWebRequest Ftp文件传输客户端操作类

    命名空间:System.Net

    用于上传,下载,删除服务器上的文件
    3.FtpWebResponse类

    1. FtpWebRequest req = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/Test.txt")) as FtpWebRequest;
    2. //创建
    3. req.Abort();
    4. //停止
    5. Stream s= req.GetRequestStream();
    6. //获取流对象
    7. FtpWebResponse res= req.GetResponse() as FtpWebResponse;
    8. //返回FTP服务器响应

    HTTP

    HTTP超文本传输协议就是一个在网络中上传下载文件的一套规则

    其本质也是TCP通信,因此不会丢包、不会乱序

    1.以TCP方式工作:

    HTTP/1.1支持持久连接(目前使用版本)

    2.HTTP是无状态的:

    HTTP不会记录客户端请求过的状态

    3.元信息作为标头

    主要数据前添加一段额外信息

    请求类型和相应状态码

    HTTP/1.1:GET\POST\HEAD\PUT\......

    响应状态码:1xx\2xx\3xx\4xx\5xx

    GET:请求获取特定的资源

    POST:请求提交数据进行处理

    HTTP常用状态码:

    200 OK;404 NOT FOUND;405 不支持请求的方法;501 服务器不能识别请求挥着没有实现指定的请求

    关键类

    HttpWebRequest类:

    命名空间:System.Net
    HttpWebRequest是主要用于发送客户端请求的类
    主要用于:发送HTTP客户端请求给服务器,可以进行消息通信、上传、下载等等操作
     

    1. //创建新的WebRequest,用于进行HTTP相关操作
    2. HttpWebRequest req = HttpWebRequest.Create(new Uri("http://192.168.50.109:8000/Http_Server/")) as HttpWebRequest;
    3. //终止传输
    4. req.Abort();
    5. //获取用于上传的流
    6. Stream s = req.GetRequestStream();
    7. //返回HTTP服务器响应
    8. HttpWebResponse res = req.GetResponse() as HttpWebResponse;
    9. //异步获取用于上传的流
    10. req.BeginGetRequestStream()
    11. 异步获取返回的HTTP服务器响应
    12. //req.BeginGetResponse()

    HttpWebResponse类:


    命名空间:System.Net
    主要用于获取服务器反馈信息的类,可以通过HttpWebRequest对象中的GetResponse()方法获取。当使用完毕时,要使用Close释放

    POST

    上传文件到HTTP资源服务器需要遵守的规则:

    1:ContentType = "multipart/form-data; boundary=边界字符串";

    2:上传的数据必须按照格式写入流中

    3:保证服务器允许上传

    4:写入流前需要先设置ContentLength内容长度

    WWW类

    WWW是Unity提供给程序员简单的访问网页的类,可以通过该类下载和上传一些数据,在使用http协议时,默认的请求类型是Get,如果想要Post上传,需要配合WWWFrom类使用(该类在较新Unity版本中会提示过时,但是仍可以使用,新版本将其功能整合进了UnityWebRequest类)

    主要支持的协议:

    1.http://和https:// 超文本传输协议
    2.ftp:// 文件传输协议(但仅限于匿名下载)
    3.file:// 本地文件传输协议,可以使用该协议异步加载本地文件(PC、IOS、Android都支持)

    1. //创建
    2. WWW www = new WWW("");
    3. //从下载数据返回一个音效切片AudioClip对象
    4. www.GetAudioClip();
    5. //用下载数据中的图像来替换现有的一个Texture2D对象
    6. Texture2D tex = new Texture2D(100, 100);
    7. //从缓存加载AB包对象,如果该包不在缓存则自动下载存储到缓存中,以便以后直接从本地缓存中加载
    8. WWW.LoadFromCacheOrDownload("", 1);

    UnityWebRequest

    是一个Unity提供的一个模块化的系统类,用于构成HTTP请求和处理HTTP响应,主要目标是让Unity游戏和Web服务端进行交互,将之前WWW的相关功能都集成在了其中(新版本中都建议使用UnityWebRequest类来代替WWW类)

    注意:
    1.UnityWebRequest和WWW一样,需要配合协同程序使用
    2.UnityWebRequest和WWW一样,支持http、ftp、file协议下载或加载资源
    3.UnityWebRequest能够上传文件到HTTP资源服务器

    UnityWebRequest类的常用操作:

    1. //1.获取文本或2进制
    2. StartCoroutine(LoadText());
    3. //2.获取纹理
    4. StartCoroutine(LoadTexture());
    5. //3.获取AB包
    6. StartCoroutine(LoadAB());
    7. IEnumerator LoadText()
    8. {
    9. UnityWebRequest req = UnityWebRequest.Get("http://192.168.50.109:8000/Http_Server/test.txt");
    10. //就会等待 服务器端响应后 断开连接后 再继续执行后面的内容
    11. yield return req.SendWebRequest();
    12. //如果处理成功 结果就是成功枚举
    13. if(req.result == UnityWebRequest.Result.Success)
    14. {
    15. //独立的处理对象
    16. print(req.downloadHandler.text);
    17. byte[] bytes = req.downloadHandler.data;
    18. print("字节数组长度" + bytes.Length);
    19. }
    20. else
    21. {
    22. print("获取失败:" + req.result + req.error + req.responseCode);
    23. }
    24. }
    25. IEnumerator LoadTexture()
    26. {
    27. //UnityWebRequest req = UnityWebRequestTexture.GetTexture("http://192.168.50.109:8000/Http_Server/HFUTER.jpg");
    28. //UnityWebRequest req = UnityWebRequestTexture.GetTexture("ftp://127.0.0.1/HFUTER.jpg");
    29. UnityWebRequest req = UnityWebRequestTexture.GetTexture("file://" + Application.streamingAssetsPath + "/test.png");
    30. yield return req.SendWebRequest();
    31. if (req.result == UnityWebRequest.Result.Success)
    32. {
    33. //(req.downloadHandler as DownloadHandlerTexture).texture
    34. //DownloadHandlerTexture.GetContent(req)
    35. //image.texture = (req.downloadHandler as DownloadHandlerTexture).texture;
    36. image.texture = DownloadHandlerTexture.GetContent(req);
    37. }
    38. else
    39. print("获取失败" + req.error + req.result + req.responseCode);
    40. }
    41. IEnumerator LoadAB()
    42. {
    43. UnityWebRequest req = UnityWebRequestAssetBundle.GetAssetBundle("http://192.168.50.109:8000/Http_Server/lua");
    44. req.SendWebRequest();
    45. while (!req.isDone)
    46. {
    47. print(req.downloadProgress);
    48. print(req.downloadedBytes);
    49. //每帧执行
    50. yield return null;
    51. }
    52. //yield return req.SendWebRequest();
    53. print(req.downloadProgress);
    54. print(req.downloadedBytes);
    55. if (req.result == UnityWebRequest.Result.Success)
    56. {
    57. //AssetBundle ab = (req.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
    58. AssetBundle ab = DownloadHandlerAssetBundle.GetContent(req);
    59. print(ab.name);
    60. }
    61. else
    62. print("获取失败" + req.error + req.result + req.responseCode);
    63. }
  • 相关阅读:
    一些shell编程技巧
    Excel 每 N 行拼成一行
    C/C++微实践 - 细胞计数
    快速生成力扣链表题的链表,实现快速调试
    Pytorch: Torchvision、torchaudio 和 torch的关系
    2023ES SHOW深圳电子元器件及物料采购展览会!|深圳比创达电子EMC
    Java集合面试题汇总(不定期更新)
    AOSP编译系统演进:从Make到Ninja的技术升级(Android13)
    教师请假条格式范文
    测开领域,三种高性价比测试方法
  • 原文地址:https://blog.csdn.net/m0_73952999/article/details/136357934