一、网络开发必备知识点

1.线程和线程池

(1)回顾——线程

  • Unity支持多线程
  • Unity中开启的多线程不能使用主线程中的对象
  • Unity中开启多线程后一定要关闭,因为它是在unity编辑器生成一个线程,除非关闭编辑器否则不会关闭线程。
1
2
3
4
5
6
7
8
Thread t = new Thread(()=>{
while(true){
print("123");
Thread.Sleep(1000);
}
});
t.Start();
t.Abort();//关闭线程

(2)线程池

命名空间:System.threading

类名:ThreadPool

作用:避免频繁创建删除线程带来性能消耗,产生内存垃圾,减少GC垃圾回收的触发。

  • ThreadPool中有若干数量的线程,如果有任务需要处理时,会从线程池中获取一个空闲的线程来执行任务。

  • 当任务执行完毕后不会销毁,而是被线程池回收以供后续任务使用。

  • 当线程池数量达到设置的最大值,任务会排队,等待其他任务释放线程后再执行。

相当于一个专门装线程的缓存池

  • 优点:节省开销,减少线程的创建,继而有效减少GC触发。

  • 缺点:不能控制线程池中线程的执行顺序,也不能获取池内线程取消/异常/完成的通知

ThreadPool是一个静态类,里面提供了很多静态成员。

①获取可用的工作线程数I/o线程数(这个数为极限,能开的最大值)

1
2
3
4
5
6
int num1;//工作线程数
int num2;//I/o线程
ThreadPool.GetAvailableThreads(out num1,out num2);
print(num1);
print(num2);
//1600,200

②获取线程池工作线程的最大数目和I/O线程的最大数目

1
ThreadPool.GetMaxThreads(out num1,out num2);

③设置线程池中可用同时处于活动状态的,工作线程的最大数目和I/o线程的最大数目。

大于次数的请求将保持排队状态,直到线程池线程可用

更改成功为true,失败反false

1
ThreadPool.SetMaxThreads(20,20);

④获取线程池中工作线程的最小数目和I/0线程的最小数目(默认数目)

1
ThreadPool.GetMinThreads(out num1,out num2);

⑤ 设置最小线程数

1
ThreadPool.SetMinThreads(55);

⑥将方法排入队列以便执行,当线程池中线程变得可用时执行。从池子中取一个线程执行函数

1
2
3
4
ThreadPool.QueueUserWorkItem((obj)=>{
print("开启了一个线程");
//这个obj其实就是第二个传入的参数
});

2.Task任务类

(1)认识Task

命名空间:System.Threading.Tasks。

类名:Task

它是基于线程池的基础上对线程进行的封装,拥有线程池优点的同时,解决了使用线程池不易控制的弊端

本质:一个Task对象就是一个线程

(2)创建无返回值Task的三种方式

① 通过new一个Task对象传入委托函数并启动

1
2
3
4
5
6
7
8
9
10
Task t1 = new Task(()=>
{
int i=0;
while(true){
print("方式一"+i);
++i;
Thread.Sleep(1000);
}
});
t1.Start();

② 通过Task中的Run静态方法传入委托函数

1
2
3
4
5
6
7
8
Task t2 = Task.Run(() =>{
int i=0;
while(true){
print("方式二"+i);
++i;
Thread.Sleep(1000);
}
});

③ 通过Task.Factory中的StartNew静态方法传入委托函数

1
2
3
4
5
6
7
8
Task.Factory.StartNew(()=>{
int i=0;
while(true){
print("方式二"+i);
++i;
Thread.Sleep(1000);
}
});

(3)创建一个有返回值的Task

① 通过new一个Task对象传入委托函数并启动(加泛型)

1
2
3
4
5
6
7
8
9
10
11
Task<int> t1 = new Task<int>(()=>
{
int i=0;
while(true){
print("方式一"+i);
++i;
Thread.Sleep(1000);
}
return 1;
});
t1.Start();

② 通过Task中的Run静态方法传入委托函数

1
2
3
4
5
6
7
8
9
10
Task<string> t2 = Task.Run<string>(() =>
{
int i=0;
while(true){
print("方式二"+i);
++i;
Thread.Sleep(1000);
}
return "12";
});

③ 通过Task.Factory中的StartNew静态方法传入委托函数

1
2
3
4
5
6
7
8
9
Task<float> t3=Task.Factory.StartNew<float>(()=>{
int i=0;
while(true){
print("方式二"+i);
++i;
Thread.Sleep(1000);
}
return 4.5f
});

获取返回值:Task实例化类.Result

1
t1.Result

注意:

Result获取结果时会阻塞线程

即如果task没有执行完成,会等待task执行完成获取到Result。

然后再执行后边的代码,也就是说 执行到这句代码时,由于我们的Task是死循环

主线程直接卡死

所以我们调用Result时:要保证task线程执行完。

(4)同步执行Task

以上都是多线程异步执行

Task同步进行需要调用Task中的RunSynchronously方法。

注:需要使用new Task对象的方式,因为Run和StartNew在创建时就会启动异步。

不Start 而是 RunSynchronously

1
2
3
4
5
Task t = new Task(()=>{
Thread.Sleep(1000);
print("assad");
});
t.RunSynchronously();

(5)Task中线程阻塞的方式

① Wait方法:等待任务执行完毕,再执行后面的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
Task t1 = new Task(()=>{
for(int i= 0;i<5;i++){
print("t1:"+i);
}
});
Task t2 = new Task(()=>{
for(int i= 0;i<5;i++){
print("t2:"+i);
}
});
t2.Wait();
print("主线程执行");
//因为task的主线程不会阻塞,如果没有wait函数,print会第一个打印
image-20250701161733945

② WaitAny静态方法:传入任务中任意一个任务结束就继续执行。

1
Task.WaitAny(t1,t2);

③ WaitAll静态方法:任务列表中所有任务执行结束就继续执行

1
Task.WaitAll(t1,t2);

(6)Task延续

① WhenAll静态方法 + ContinueWith方法:

传入任务全部完毕后再执行某任务

1
2
3
4
Task.WhenAll(t1,t2).ContinueWith((t)=>{
print("一个新的任务");
});
//t1执行完再去执行这个新的任务

通过延续函数的参数(即前一个任务对象),可以访问以下关键信息:

属性/方法 说明
t.Status 任务状态(如 RanToCompletion, Faulted, Canceled 等)
t.Result 如果前一个任务是Task<TResult>,获取其返回值(需确保任务成功完成)
t.Exception 获取任务抛出的异常(如果任务失败)
t.IsCompleted 任务是否已完成(无论成功、失败或取消)
t.Wait() 阻塞当前线程,直到任务完成(通常不推荐在延续任务中使用)
1
2
3
4
//第二种写法
Task.Factory.ContinueWhenAll(new Task[]{t1,t2},(t)=>{

});

②WhenAny静态方法+ContinueWith方法:传入任务只要有一个执行完毕后再执行某任务

1
同上

(7)取消Task执行

主要运用到死循环里面。

方法一:加bool变量

方法二:通过CancellationTokenSource对象取消标识源类 来控制.还可以延时取消

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
CancellationTokenSource c= new CancellationTokenSource();
Task.Run(()=>{
int i=0;
while(!c.IsCancellationRequested){
print("计时"+i);
++i;
Thread.Sleep(1000);
c.CancwelAfter(5000);//延时取消
c.Token.Register(()=>{
//取消回调
});
});
Update(){
if(Input.GetKeyDown(keyCode.Space))
{
c.Cancel();
}
}

总结:

  • Task是Thread的封装
  • Task可有返回值,Thread没有返回值
  • Task可用执行后续操作,比如执行完task还可以追加task,thread没有这个功能
  • Task可用更方便取消任务,包括延迟取消、取消回调。Thread更单一
  • Task具有线程池的优点,同时又克服了线程池的缺点。可以控制池中执行顺序。

3.async和await

(1)什么是同步和异步

  • 同步和异步主要修饰方法

  • 同步方法:

    • 当方法被调用是,调用者需要等待该方法执行完才能继续执行
  • 异步方法:

    • 当方法被调用时立即返回,并获取一个线程执行该方法内部的逻辑,调用者不用等该方法执行完毕
  • 我们会把一些不需要立即获得结果且耗时的逻辑设置为异步执行。避免由复杂逻辑带来的线程阻塞。

(2)什么时候需要异步编程

  • 需要处理的逻辑严重影响主线程执行的流畅性时,我们需要使用异步编程
  • 比如:
    • 复杂逻辑计算
    • 网络下载、网络通讯
    • 资源加载

(3)异步方法async和await

  • async和await一般需要配合Task进行使用
  • async用于修饰函数、lambda表达式、匿名函数
  • await用于在函数中和async配对使用
  • 主要作用:是等待某个逻辑结束,此时逻辑会返回函数外部继续执行,直到等待的内容执行结束后,再继续执行异步函数内部逻辑

使用async修饰异步方法:

  • 在异步方法中使用await关键字,不使用则会以同步方式进行
  • 异步方法名建议以Async结尾
  • 异步方法的返回值只能是void、Task、Task<>
  • 异步方法中不能声明使用ref和out关键字变量

使用await等待异步内容执行完毕(配合task使用)

遇到await关键字时:

  • 异步方法被挂起
  • 将控制器返回给调用者(主线程)
  • 当await修饰内容异步执行结束后,继续通过调用者线程执行后面 的内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public async void TestAsync()
{
//1
print("进入异步方法");
//2
await Task.Run(()=>{
// 这个线程执行完了才会执行3,在此期间会去执行主线程,然后子线程,然后后面
});
//3
print("异步后面的")
}
TestAsync();
print("主线程逻辑执行");

image-20250701194752981

注:Unity中大部分异步方法是不支持异步关键字async和await的,我们只有在协同程序中进行使用

,但是可以装插件让这些异步方法支持。

但是,当我们用到.Net库中提供的API时可以使用

二、网络开发必备理论

1.网络基本概念

网络的作用:让设备之间可用互相通信

网络的概念:

  • 网络是由若干设备和连接这些设备的链路构成。
  • 各种设备 间接或直接通过介质相连
  • 数据信息通过编码成2进制数值进行传输,以电脉冲的形式
  • 1是通过一个正电压传输。
  • 0是通过一个负电压传输。

(1)局域网

image-20250702092906799 image-20250702092940255

(2)以太网

image-20250702093144984

说人话:局域网就是一种设备连接规范,一种网络连接协议

(3)以太网 网络拓扑结构

用传输媒体把计算机等各种设备互相连接起来的物理布局。连接过程中构成的几何形状

  • 各个拓扑结构相互配合使用
image-20250702093455380 image-20250702093611019

(4)城域网

一个城市范围内建立的网络,通常覆盖一个城市

image-20250702094130969

(5)广域网

image-20250702094318963 image-20250702094348753

(6)互联网

image-20250702094702908 image-20250702094752057 image-20250702094841464 image-20250702094914619

2.IP 端口 Mac地址

(1)IP地址

  • 基本概念:
    • 由IP协议提供的一种统一的地址格式,是设备在网络中的具体地址。
  • 简单理解:
    • 类似于在网络上的设备电话号码。

(2)IP地址的分类

image-20250702095413267 image-20250702095831785

(3)端口号

通过IP地址我们可以在网络上找到一台设备

但是我们想要和设备通信,本质上是设运行在设备上的应用程序通信。

而一台设备可能运行多个应用设备。

端口号就是帮助我们区分和哪一个程序通信的。

基本概念:

  • IP地址决定设备在网络中的地址
  • 而端口是不同应用程序在该设备上的门牌号
  • 一台设备上不同的应用程序想要通信,就必须对应一个唯一的端口号。
image-20250702100703626

信息传输的必须:IP地址+端口号

image-20250702101043492

(4)Mac地址

物理设备(网卡)的唯一标识。

image-20250702101338699 image-20250702101401767 image-20250702101436843

MAC地址就像身份证号,IP地址就好像你的住址。

MAC地址是物理层面上的通信基础,IP地址是逻辑层面上的通信基础。

image-20250702101855876

3.客户端和服务端

(1)客户端

image-20250702102316302 image-20250702102354834

(2)服务端

image-20250702102539454

(3)游戏开发中的客户端和服务端

image-20250702102704578 image-20250702102936517 image-20250702103335911

4.数据通信模型

即数据在通信时是如何管理的

一般分为:分散式、集中式、分布式

  • 分散式
image-20250702103804907
  • 集中式
image-20250702103930489
  • 分布式
image-20250702104211064 image-20250702104245333

(1)C/S模型

image-20250702104513734

(2)B/S模型

image-20250702104606719

(3)P/P模型

image-20250702104730537 image-20250702104955973

5.OSI模型

(1)OSI模型是什么

  • 简单来说:
    • 就是人为定义的一个标准
    • 它制定了设备之间互相连接、相互通信的标准
    • 各公司按照这个标准设计的规则,就可以让不同设备利用互联网进行互联通信。

(2)OSI模型的规则

image-20250702135607236

OSI模型把互联通信的过程分成了七个层级

image-20250702135717881

(3)OSI模型每层的职能

image-20250702140358330
  • 物理层
    • 负责传送0和1的电信号,将2进制数据利用电脉冲实现比特流传输
    • 主要功能:定义传输模式、定义传输速率等等。
  • 数据链路层
    • image-20250702140738312
  • 网络层
    • image-20250702141034751
  • 传输层
    • image-20250702141303758
  • 应用层
    • image-20250702141620092
  • 表示层
    • image-20250702141721740
  • 会话层
    • image-20250702141815005

总结

image-20250702141910837 image-20250702142038727

6.TCP/IP协议

(1)TCP/IP是什么

  • 网络传输或通讯协议
  • 就是一系列规则(协议)的统称
  • 不仅仅指TCP和IP两个协议,还包括FTP,SMTP,UDP等。

(2)TCP/IP协议的规则

image-20250702142746252
image-20250702193214728

(3)TCP/IP每层职能

image-20250702193552274
  • 对比:
image-20250702193636435 image-20250702193841518

7.TCP与UDP

(1)什么是TCP与UDP

  • TCP:传输控制协议
  • UDP:用户数据报协议

区别:

TCP UDP
连接方面 面向连接(比如:打电话要先拨号连接) 无连接。发数据前不需要建立连接
安全方面 无差错、不丢失、不重复、按序到达 不保证可靠,尽力交付
传输效率 相对较低 相对较高
连接对象 一对一 一对一,一对多,多对一,多对多

(2)TCP协议

image-20250702194841034
  • 三次握手(建立连接)

    • image-20250702195015451
  • 四次挥手(断开连接)

    • image-20250702195239501

TCP协议有了这三次握手四次挥手,就可以做到传输数据可靠、不丢失、不重复、按顺序到达。

(3)UDP协议

image-20250702195713499
image-20250702195754330