UnityWebRequest 的上传功能
UnityWebRequest 用来发送网络请求,其中既包括下载(从服务器获取数据)也包括上传(向服务器发送数据)。++早期的操作方式(++ 比如使++用 WWW++ 类)通常把++数据处理和网络传输混在++ 一起,而 ++UnityWebRequest 把它们分开++了。
分离上传和数据处理
上传部分:负责把数据发送给服务器。
数据处理部分:负责把数据打包、序列化、格式化(比如把对象转换成字节数组)。
这种++UnityWebRequest高级++分离的好处是:
如果你++和后端已经约定好数据格式(比如"我发送的所有消息都是一个字节数组,数组中按照特定规则排列"),你就可以直接用这种约定好的格式来上传数据++。
++不需要额外封装数据(比如构造一个 List 或其他容器)++,直接把数据按照规定打包成字节数组传过去就可以了。
UnityWebRequest 相比早期的 WWW 类,一个非常重要的改进就在于将"下载操作"与"数据处理"解耦开来了。这种设计带来的好处主要有:
下载部分 :负责与服务器进行数据传输,发送请求、接收响应。
数据处理部分 :则由各种 DownloadHandler(如 DownloadHandlerBuffer、DownloadHandlerTexture、DownloadHandlerAssetBundle 等)来完成,它们负责将下载的数据转换成我们需要的格式或对象。
这种分离使得代码结构更清晰,也更容易扩展和维护。
由于数据处理是一个独立的模块 ,你可以通过继承 DownloadHandler 或 DownloadHandlerScript 来自定义自己的数据解析逻辑。
例如,你可++以编写一个自定义的下载处理器来解析服务器返回的 JSON 数据++ ,并++直接转换成业务对象,而不必在主流程中手动解析++。
UnityWebRequest 提供了多种内置的 DownloadHandler 类,分别针对不同类型的数据进行处理(例如图像、音频、文件等)。
这使得开发者可以直接利用这些现成的类,快速实现对常见数据类型的获取,而不需要重复造轮子。
分离后的设计允许你 在同一个网络请求中更灵活地控制数据下载、超时设置、错误处理、证书验证等各个环节。
同时,如果需要对数据做更多自定义操作 ,也可以只专注于 DownloadHandler 的实现,而不干扰整个请求的流程。
- GET 用于"获取"资源,表示客户端向服务器请求数据,而不会引起服务器状态改变。
- POST 用于"提交"数据,通常用于创建或修改服务器上的资源,可能会引起服务器状态的变化。
这种语义区分使得开发者、服务器以及中间设备(例如代理服务器或缓存系统)都能基于请求方法做出合理的行为判断。
++如果需要从服务器获取数据且数据量较小,使用 GET 请求。++
++如果需要向服务器提交数据,尤其是敏感信息或大量数据,使用 POST 请求。++
GET
请求:
不同浏览器对 URL 长度的支持有所差异,但通常限制在 2,000 到 8,000 个字符之间
将数据附加在 ++URL 的查询字符串++ 中,++格式为 ?key1=value1&key2=value2
++
是幂等的,即多次请求的结果相同,不会改变服务器状态
可以被浏览器缓存,并且 URL 可以被收藏为书签
由于数据暴露在 URL中,可能被浏览器历史记录、服务器日志等记录,不适合传输敏感信息
POST
请求:
没有固定的长度限制,适合传输大量数据
将数据包含在++请求的主体(body)中++,而不是 URL 中
不是幂等的,多次请求可能导致不同的结果,如多次提交表单可能导致多次记录创建
通常不会被缓存,且 URL 不适合作为书签
没有固定的长度限制,适合传输大量数据
数据包含在请求主体中,相对更安全,但仍需通过 HTTPS 等加密协议保护
DownLoadHandlerMsg msgHandler = new DownLoadHandlerMsg();
DownLoadHandlerMsg:这是一个自定义的下载处理类,通常会继承 DownloadHandler 或 DownloadHandlerScript。
其内部可能实现了对服务器返回数据的解析(例如 JSON、XML 或自定义格式),并提供了 GetMsg<T>() 方法来获取解析后的对象。
该行代码的作用是 实例化一个自定义的下载处理器
req.downloadHandler = msgHandler;
req.downloadHandler:UnityWebRequest 提供的属性 ,用来指定下载处理器(DownloadHandler)。
UnityWebRequest req = new UnityWebRequest (++"web服务器地址"++ , ++UnityWebRequest.kHttpVerbPOST++);
"web服务器地址":这是目标服务器的 URL(字符串),告诉 UnityWebRequest 要向哪个服务器地址发送请求。
UnityWebRequest.kHttpVerbPOST:这是 Unity 提供的一个常量字符串,值为 "POST",表示使用 HTTP POST 方法。
如果不指定方法,默认是 GET 请求;这里明确指定为 POST,可以在后续向服务器提交数据、表单等。
HTTP 请求是一条完整的消息,通常由请求行、请求头和消息体构成。服务器在处理请求时通常会先解析请求行和请求头,再决定如何处理后续的消息体。
-
头部的作用与解析顺序
服务器在收到请求时,会首先读取并解析请求头。请求头携带了很多元数据,比如认证信息、内容类型、客户端环境等 。++服务器会根据这些信息做出判断,例如验证客户端身份、确定数据格式、检查缓存策略等++ 。如果++在解析请求头阶段发现问题(例如缺少必要的认证信息或格式错误),服务器可以选择立即拒绝请求,返回错误响应,而不会继续处理消息体++。
-
消息体的处理
如果请求头中的信息被++验证通过,服务器才会进一步解析和处理消息体中的主数据++。例如,当客户端上传文件时,服务器首先检查请求头(比如 Content-Type、Content-Length 等),确认无误后才会读取和处理文件数据。
-
并非"等待头处理完成"
这里的"先处理头"并不是说服务器会暂停等待头部处理结束再决定是否读取消息体,而是整个请求消息是按照协议结构组织的。服务器在解析请求时会按照先头后体的顺序进行,如果头部验证失败,服务器可能根本不会去解析或使用消息体的数据。所以,从应用逻辑上看,只有当请求头中的关键信息被接受并确认合理后,服务器才会继续处理客户端发送的主数据。
cs
public class HttpClientExample
{
public async Task SendRequestAsync(string url)
{
using (HttpClient client = new HttpClient())
{
// 设置默认的请求头
client.DefaultRequestHeaders.Add("User-Agent", "MyCustomAgent/1.0");
client.DefaultRequestHeaders.Add("X-Custom-Header", "CustomValue");
HttpResponseMessage response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
Console.WriteLine("请求成功");
}
通过 SetRequestHeader
方法设置了两个请求头,一个是标准的授权头(Authorization),另一个是自定义头(X-Custom-Header)。这些头部信息会在请求发送时附加到 HTTP 请求中。
request.SetRequestHeader ("Authorization", "Bearer YOUR_TOKEN_HERE"); request.SetRequestHeader("X-Custom-Header", "CustomValue");
cs
public class HeaderExample : MonoBehaviour
{
void Start()
{
StartCoroutine(SendRequest("http://example.com"));
}
IEnumerator SendRequest(string url)
{
UnityWebRequest request = UnityWebRequest.Get(url);
// 设置自定义请求头,例如添加身份验证信息
request.SetRequestHeader("Authorization", "Bearer YOUR_TOKEN_HERE");
request.SetRequestHeader("X-Custom-Header", "CustomValue");
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
Debug.Log("请求成功");
}
可以通过调用 SetRequestHeader
方法添加或修改请求头
请求头保存在请求对象内部
UnityWebRequest 来发起 HTTP 请求
Unity 或 .NET 中,HTTP 头部其实并不是独立的"类",而是以键值对的形式存在于请求对象里,你需要手动设置
你可以在一个 HTTP 请求或响应中同时包含多个标准头和自定义头,以满足不同的需求。
请求头(Request Headers) 用于在客户端和服务器之间传递附加信息 ,描述请求的细节和客户端的环境。 这些头部信息由键值对组成 ,键和值之间用冒号分隔 。 例如,User-Agent
头部用于标识发起请求的客户端软件类型和版本。
UnityWebRequest 类提供了非常丰富的高级操作功能,从自定义请求头、数据上传与下载,到证书处理、超时控制、进度监控等,都可以根据项目需求进行灵活配置













好像,,懂了,,但不是很愿意懂,或者说还没有做好准备迎接懂了之后的情况

用这个服务器的FTP的时候,需要把代理置空





//在实际商业项目开发当中,如果需要用FTP来进行文件传输
//那么FTP服务器的解决方案 都是由后端程序员来完成的
//不管它使用哪种方式来搭建FTP服务器
//只要能正常上传下载内容并且保证安全性即可










异步通信,客户端则使用async实现,



先写了客户端的,客户端比较简单,只要Connect了服务端,然后就对应的写好receive和Send的异步方法就可以了


--------------有必要存一些截图,直接可以把唐老师的lesson代码学下来,不对后期也有修改的可能



socket中也有别的异步方法,不是begin和end配合,而是async起作用

offset对于分包黏包的处理是比较重要的

SocketFlags
是一个枚举类型,用来指定在执行 Socket 接收(或发送)操作时对数据进行何种特殊处理。在调用像 BeginReceive
这样的异步方法时,socketFlags
参数可以用来调整操作的行为,具体包括以下几种常见用途:
-
SocketFlags.None
- 含义:没有额外的特殊行为,就是按照默认方式接收数据。
- 使用场景 :++绝大多数情况下,直接传入
SocketFlags.None
即可++。
-
SocketFlags.++Peek++
- 含义 :接收数据时只"窥视"数据,也就是说数据会++从接收缓冲区中被读取出来,但不会从队列中移除,后续的接收操作仍然能够读取到相同的数据++。
- 使用场景:如果你只需要查看数据而不想消耗它,就可以使用此选项。
-
SocketFlags.OutOfBand
- 含义 :用于接收++带外数据(Out-of-Band Data),这种数据通常用于传递紧急信息++。
- 使用场景:仅在你需要处理带外数据时使用,普通的数据传输一般不使用此标志。
-
其他标志
- 还有一些其他的标志(如 SocketFlags.Partial 等),用于处理特定场景或数据的一部分。



上为客户端
这都是服务端的异步处理accept,
通过endaccept得到连入的那个客户端socket
客户端连入的时候,就会执行这里的回调函数

在 .NET 中,Socket 提供了一组以 Begin 开头、以 End 结尾的异步方法,这种模式遵循 IAsyncResult 异步模式(Apm,Asynchronous Programming Model),主要特点包括:
-
异步方法的调用
- ++调用 BeginXXX 方法(如 BeginAccept、BeginConnect、BeginReceive)时,方法会立即返回一个 IAsyncResult 对象++ ,而++实际的网络操作在后台进行++ ,++不会阻塞调用线程++。
-
回调函数与 IAsyncResult
- 你需要传入一个回调函数,这个函数会在操作完成时被调用。
- 回调函数的参数是一个 IAsyncResult 对象(++通常命名为 args++ 或 result),通过它可以获得++操作的状态及传入的状态数据++。
- 其中,AsyncState 属性保存你在调用 Begin 方法时传入的状态信息(例如,你可以传入当前的 Socket 对象或者其他上下文数据),这样在回调函数中可以将其转换回来以便使用。
-
AsyncWaitHandle
- IAsyncResult 提供的 AsyncWaitHandle 属性是一个 WaitHandle 对象,它允许你(如果需要)以同步方式等待异步操作完成。不过在事件驱动的回调模型中,++通常不需要显式调用++它。
-
EndXXX 方法
- 在回调函数中,必须调用相应的 EndXXX 方法(例如 EndAccept、EndConnect、EndReceive),这样才能完成异步操作并获取结果。
- 如果不调用 EndXXX 方法,操作可能不会被正确完成,同时系统资源也不会释放。
Socket TCP通信中的异步方法(如Begin开头的方法)的使用。例如BeginAccept、BeginConnect、BeginReceive等。这些方法使用IAsyncResult作为回调参数 ,AsyncState用于传递参数 ,IAsyncResult.AsyncWaitHandle则用于同步等待。
解释.NET中的异步编程模型,使用BeginXXX和EndXXX方法。这个模型使用IAsyncResult接口作为回调 ,通过AsyncState存储用户定义的状态 。IAsyncResult.AsyncWaitHandle用于同步等待。对于服务器,使用BeginAccept和EndAccept ;客户端则使用BeginConnect和EndConnect;还有BeginReceive和EndReceive等方法,甚至可以提到BeginSend和EndSend。
这个模型是.NET的早期异步模式 ,通过++回调处理完成事件++ ,并使用++End方法获取结果++ ,比如在++服务器端调用Socket.BeginAccept()来接收客户端++。
协同程序本质也是用了线程来实现了"异步"的协程,这些同步异步的方法,基础的使用是这样,但是不代表socket不会封装好
所以就要学习各个正常同步下的方法的,异步方法
.NET 中提供了多种方式实现 Socket 的异步操作,其中一种方式就是利用以"Async"结尾的方法(例如 AcceptAsync、ConnectAsync、SendAsync、ReceiveAsync)。这种模式采用了++基于事件的异步模式(EAP, Event-based Asynchronous Pattern)++,其核心思想是:
- ++调用异步方法时:方法会立即返回++,并不会阻塞当前线程。
- ++异步操作完成++后 :会++触发 SocketAsyncEventArgs 对象的 Completed 事件++,你可以在该事件处理程序中处理结果。
这种模型特别适合高并发场景,因为它可以有效地减少线程阻塞和资源占用。
Socket TCP 通信中使用异步方法(Async 结尾的方法)的基本原理和关键点。
主要内容包括如何使用 SocketAsyncEventArgs 来承载异步操作的参数 ,以及如何在服务端使用 AcceptAsync ,在客户端使用 ConnectAsync 和在双方发送数据时使用 SendAsync(还有 ReceiveAsync 等)。
异步的await会等后面接的Task执行完再继续执行,不是async的方法则是开个新线程,继续执行下面的函数


都是些代码片段,但逻辑上还是有错误,知识点的使用还勉强能传达出那个意思



在服务端的开始函数里,也就是开启服务器的函数中,


-
快速查找和管理客户端
使用
Dictionary<int, ...>
的好处在于,可以通过一个唯一的键(这里用的是int
类型的客户端 ID)快速查找到对应的客户端连接信息。这样在需要更新、检查或删除某个客户端的状态时,可以直接通过这个 ID 进行操作。 -
同时存储多个数据项
每个客户端不仅需要保存其连接的
Socket
对象(用于后续发送和接收数据),还需要记录上次收到消息的时间(用DateTime
表示)。- 使用
Tuple<Socket, DateTime>
可以把这两个不同类型的数据打包成一个整体,方便在一个数据结构中同时保存这两项信息,而不需要定义单独的类或使用多个集合分别管理。
- 使用


本例中,DateTime
用来记录每个客户端最后一次发送消息的时刻
服务器端需要为每个客户端维护"最后收到数据的时间"
示例代码使用一个 Dictionary<int, DateTime>
存储客户端(假设用整数 ID 作为标识)与最后更新时间的映射,并启动一个后台线程定时检测超时连接
e
包含了与事件相关的额外信息(例如触发的时间)
在本例中,我们主要使用它作为事件签名的必须参数,而实际代码并没有用到它的具体属性
- 在这个例子中,
sender
通常就是你创建并启动的Timer
实例。 - 用途 :在某些情况下,你可能需要从
sender
中获取更多信息(例如转换为Timer
类型以访问 Timer 的属性),但在简单的用例中,你可以不必对它进行额外操作。

关闭连接,记得停止定时器
Timer 类:它的主要作用是定时触发事件



