自由学习记录(35)

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:这是一个自定义的下载处理类,通常会继承 DownloadHandlerDownloadHandlerScript

其内部可能实现了对服务器返回数据的解析(例如 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 参数可以用来调整操作的行为,具体包括以下几种常见用途:

  1. SocketFlags.None

    • 含义:没有额外的特殊行为,就是按照默认方式接收数据。
    • 使用场景 :++绝大多数情况下,直接传入 SocketFlags.None 即可++。
  2. SocketFlags.++Peek++

    • 含义 :接收数据时只"窥视"数据,也就是说数据会++从接收缓冲区中被读取出来,但不会从队列中移除,后续的接收操作仍然能够读取到相同的数据++。
    • 使用场景:如果你只需要查看数据而不想消耗它,就可以使用此选项。
  3. SocketFlags.OutOfBand

    • 含义 :用于接收++带外数据(Out-of-Band Data),这种数据通常用于传递紧急信息++。
    • 使用场景:仅在你需要处理带外数据时使用,普通的数据传输一般不使用此标志。
  4. 其他标志

    • 还有一些其他的标志(如 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 类:它的主要作用是定时触发事件

相关推荐
知识分享小能手20 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
茯苓gao1 天前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾1 天前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT1 天前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa1 天前
HTML和CSS学习
前端·css·学习·html
看海天一色听风起雨落1 天前
Python学习之装饰器
开发语言·python·学习
speop1 天前
llm的一点学习笔记
笔记·学习
非凡ghost1 天前
FxSound:提升音频体验,让音乐更动听
前端·学习·音视频·生活·软件需求
ue星空1 天前
月2期学习笔记
学习·游戏·ue5
萧邀人1 天前
第二课、熟悉Cocos Creator 编辑器界面
学习