自由学习记录(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 类:它的主要作用是定时触发事件

相关推荐
梦里是谁N15 分钟前
【deepseek之我问】如何把AI技术与教育相结合,适龄教育,九年义务教育,以及大学教育,更着重英语学习。如何结合,给出观点。结合最新智能体Deepseek
人工智能·学习
虾球xz24 分钟前
游戏引擎学习第116天
java·学习·游戏引擎
linwq838 分钟前
OkHttp使用和源码分析学习(二)
学习·okhttp
肥肠可耐的西西公主42 分钟前
前端(AJAX)学习笔记(CLASS 2):图书管理案例以及图片上传
前端·笔记·学习
贩卖纯净水.2 小时前
REACT学习DAY02(恨连接不上服务器)
服务器·学习·react.js
南风过闲庭3 小时前
操作系统研究
大数据·人工智能·科技·学习·ai·系统架构
陈无左耳、6 小时前
HarmonyOS学习第2天: 解锁语言与框架的无限可能
学习·华为·harmonyos
朝九晚五ฺ7 小时前
【Linux探索学习】第三十弹——线程互斥与同步(上):深入理解线程保证安全的机制
linux·运维·学习
柃歌7 小时前
【UCB CS 61B SP24】Lecture 5 - Lists 3: DLLists and Arrays学习笔记
java·数据结构·笔记·学习·算法
剑走偏锋o.O8 小时前
MyBatis框架详解与核心配置解读
java·学习·mybatis