2025-05-07 Unity 网络基础8——UDP同步异步通信

文章目录

  • 1 UDP 概述
    • 1.1 通信流程
    • 1.2 TCP 与 UDP
    • 1.3 UDP 分包
    • 1.4 UDP 黏包
  • 2 同步通信
    • 2.1 服务端
    • 2.2 客户端
    • 2.3 测试
  • 3 异步通信
    • 3.1 Bgin / End 方法
    • 3.2 Async 方法

1 UDP 概述

1.1 通信流程

​ 客户端和服务端的流程如下:

  1. 创建套接字 Socket。
  2. Bind() 方法将套接字与本地地址进行绑定。
  3. ReceiveFrom()SendTo() 方法在套接字上收发消息。
  4. Shutdown() 方法释放连接。
  5. 关闭套接字。
image-20250507200652499

1.2 TCP 与 UDP

image-20250507200900429 image-20250507200922560

1.3 UDP 分包

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

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

MTU(Maximum Transmission Unit)

​ 最大传输单元,用来通知对方所能接受数据服务单元的最大尺寸,不同操作系统会提供用户一个默认值。

​ 以太网和 802.3 对数据帧的长度限制,其最大值分别是 1500 字节和 1492 字节。

​ 由于 UDP 包本身带有一些信息,因此建议:

  • 局域网环境下:1472 字节以内(1500 减去 UDP 头部 28 为 1472)。

  • 互联网环境下:548 字节以内(老的 ISP 拨号网络的标准值为 576 减去 UDP 头部 28 为 548)。

    只要遵守这个规则,就不会出现自动分包的情况。

​ 如果想要发送的消息确实比较大,可以进行手动分包,将其拆分成多个消息,每个消息不超过限制。但手动分包的前提是要解决 UDP 的丢包和无序问题。

1.4 UDP 黏包

​ UDP 本身作为无连接的不可靠的传输协议(适合频繁发送较小的数据包),不会对数据包进行合并发送。一端直接发送数据,不会对数据合并。

​ 因此 UDP 当中不会出现黏包问题(除非手动进行黏包)。

2 同步通信

​ 区别于 TCP,UDP 发送和接收消息的方式为 SendTo()ReceiveFrom(),需要传入指定的 EndPoint 以指明将消息发送到哪和从哪里接收消息。

SendTo()

image-20250507203606641
  • 参数

    • buffer:要发送的数据缓冲区。

    • size:要发送的数据的字节数。

    • socketFlags:发送操作的控制标志。

    • remoteEP:远程终结点,指定数据要发送到的目标地址。

  • 返回值

    • 发送的字节数。

ReceiveFrom()

image-20250507203752224
  • 参数
    • buffer:字节数组,用于存储接收到的数据。
    • size:指定从接收缓冲区中读取的最大字节数。
    • socketFlags:枚举值,用于指定接收操作的行为。
    • remoteEPEndPoint对象,用于存储发送方的网络地址。这个参数是引用类型,所以方法调用后,它将包含发送方的地址信息。
  • 返回值
    • 接收到的字节数。

2.1 服务端

// See https://aka.ms/new-console-template for more informationusing System.Net;
using System.Net.Sockets;
using System.Text;// 1.创建套接字
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);// 2.绑定本机地址
var ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
socket.Bind(ipPoint);
Console.WriteLine("服务器开启,等待消息中...");// 3.接受消息
var      buffer         = new byte[512];
EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);
var      length         = socket.ReceiveFrom(buffer, ref remoteIpPoint2);
Console.WriteLine("IP: " + (remoteIpPoint2 as IPEndPoint).Address +" Port: " + (remoteIpPoint2 as IPEndPoint).Port +" 发来了 " +Encoding.UTF8.GetString(buffer, 0, length));// 4.发送到指定目标
var remoteIpPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socket.SendTo(Encoding.UTF8.GetBytes("hi"), remoteIpPoint);// 5.释放关闭
socket.Shutdown(SocketShutdown.Both);
socket.Close();Console.ReadKey();

2.2 客户端

using UnityEngine;namespace Lesson
{using System;using System.Net;using System.Net.Sockets;using System.Text;public class Lesson14 : MonoBehaviour{private void Start(){// 1.创建套接字var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);// 2.绑定本机地址var ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);socket.Bind(ipPoint);// 3.发送到指定目标var remoteIpPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);socket.SendTo(Encoding.UTF8.GetBytes("hello"), remoteIpPoint);// 4.接受消息var      buffer         = new byte[512];EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);var      length         = socket.ReceiveFrom(buffer, ref remoteIpPoint2);print("IP: " + (remoteIpPoint2 as IPEndPoint).Address +" Port: " + (remoteIpPoint2 as IPEndPoint).Port +" 发来了 " +Encoding.UTF8.GetString(buffer, 0, length));// 5.释放关闭socket.Shutdown(SocketShutdown.Both);socket.Close();}}
}

2.3 测试

​ 先运行服务器,再运行 Unity,可以看到双端互发消息。

image-20250507204056792 image-20250507204040425

3 异步通信

3.1 Bgin / End 方法

BeginSendTo()

image-20250507213337331
  • 参数

    • buffer:要发送的数据缓冲区。
    • offset:缓冲区中开始发送数据的偏移量。
    • size:要发送的数据字节数。
    • socketFlags:用于指定发送操作的选项。例如,可以用来指定是否使用紧急数据。
    • remoteEP:远程终结点。它指定了要发送数据的目标地址。
    • callback:异步操作完成时要调用的回调方法。
    • state:一个用户定义的对象,它包含异步操作的状态信息。
  • 返回值

    • 返回IAsyncResult对象,表示异步操作的状态和结果。可以通过调用EndSendTo()方法来获取异步操作的结果。

BeginReceiveFrom()

image-20250507213648699
  • 参数

    • buffer:字节数组,用于存储接收到的数据。
    • offset:在buffer数组中开始存储接收数据的偏移量。
    • size:要接收的数据的字节数。
    • socketFlags:控制接收操作的标志。例如,SocketFlags.Partial表示接收的数据可能不完整。
    • remoteEPEndPoint对象,用于存储发送方的地址。这个参数是引用类型,所以方法调用`后,它会被更新为发送方的地址。
    • callback:异步操作完成时要调用的回调方法。
    • state:用户定义的对象,包含与异步操作相关的状态信息。
  • 返回值

    • 返回IAsyncResult对象,表示异步操作的状态。通过这个对象,可以检查异步操作是否完成,或者等待操作完成。

代码示例

using UnityEngine;namespace Lesson
{using System;using System.Net;using System.Net.Sockets;using System.Text;public class Lesson16 : MonoBehaviour{private byte[] _buffer = new byte[512];private void Start(){// 创建一个UDP套接字var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);// 创建一个IP地址和端口号的EndPointEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);// 将字符串转换为字节数组byte[] bytes = Encoding.UTF8.GetBytes("123123lkdsajlfjas");// 开始发送数据到指定的EndPointsocket.BeginSendTo(bytes, 0, bytes.Length, SocketFlags.None, ipPoint, SendCallback, socket);// 开始接收数据socket.BeginReceiveFrom(_buffer, 0, _buffer.Length, SocketFlags.None, ref ipPoint, ReceiveCallback, (socket, ipPoint));}private void ReceiveCallback(IAsyncResult ar){try{(Socket s, EndPoint ipPoint) info = ((Socket, EndPoint)) ar.AsyncState;// 返回值 就是接收了多少个 字节数int length = info.s.EndReceiveFrom(ar, ref info.ipPoint);// 处理消息// ...// 处理完消息 又继续接受消息info.s.BeginReceiveFrom(_buffer, 0, _buffer.Length, SocketFlags.None, ref info.ipPoint, ReceiveCallback, info);}catch (SocketException s){print("接受消息出问题: " + s.SocketErrorCode + " : " + s.Message);}}private void SendCallback(IAsyncResult ar){try{Socket s = ar.AsyncState as Socket;s.EndSendTo(ar);print("发送成功");}catch (SocketException s){print("发送失败: " + s.SocketErrorCode + " : " + s.Message);}}}
}

3.2 Async 方法

SendToAsync()

image-20250507214235716

ReceiveFromAsync()

image-20250507214808031

代码示例

using UnityEngine;namespace Lesson
{using System;using System.Net;using System.Net.Sockets;using System.Text;public class Lesson16 : MonoBehaviour{private byte[] _buffer = new byte[512];private void Start(){// 创建一个UDP套接字var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);// 创建一个IP地址和端口号的EndPointEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);// 将字符串转换为字节数组byte[] bytes = Encoding.UTF8.GetBytes("123123lkdsajlfjas");var args = new SocketAsyncEventArgs();// 设置发送数据的缓冲区args.SetBuffer(bytes, 0, bytes.Length);// 添加发送完成事件args.Completed += SendToAsyncCompleted;socket.SendToAsync(args);var args2 = new SocketAsyncEventArgs();// 设置接收数据的缓冲区args2.SetBuffer(_buffer, 0, _buffer.Length);// 添加接收完成事件args2.Completed += ReceiveFromAsyncCompleted;socket.ReceiveFromAsync(args2);}private void SendToAsyncCompleted(object s, SocketAsyncEventArgs args){if (args.SocketError == SocketError.Success){print("发送成功");}else{print("发送失败");}}private void ReceiveFromAsyncCompleted(object s, SocketAsyncEventArgs args){if (args.SocketError == SocketError.Success){print("接收成功");// 具体收了多少个字节// args.BytesTransferred// 可以通过以下两种方式获取到收到的字节数组内容// args.Buffer// cacheBytes// 解析消息// ...Socket socket = s as Socket;//只需要设置 从第几个位置开始接 能接多少args.SetBuffer(0, _buffer.Length);socket.ReceiveFromAsync(args);}else{print("接收失败");}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/80116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何减少锁竞争并细化锁粒度以提高 Rust 多线程程序的性能?

在并发编程中,锁(Lock)是一种常用的同步机制,用于保护共享数据免受多个线程同时访问造成的竞态条件(Race Condition)。然而,不合理的锁使用会导致严重的性能瓶颈,特别是在高并发场景…

AGV智能搬运机器人:富唯智能引领工业物流高效变革

在智能制造与工业4.0深度融合的今天,物流环节的高效与精准已成为企业核心竞争力的关键。富唯智能凭借其自主研发的AGV智能搬运机器人,以创新技术重塑工业物流标准,助力企业实现降本增效的跨越式发展。 一、技术突破:精准导航与智能…

K8s 资源分类

K8s 资源分类图谱 内置资源的分类 1、工作负载相关: Pod:最小的部署单元,包含一个或多个容器。 Deployment:管理无状态应用的副本和滚动更新。 StatefulSet:适用于有状态应用(如数据库)&#…

VLM-AD:通过视觉语言模型监督实现端到端自动驾驶

《VLM-AD: End-to-End Autonomous Driving through Vision-Language Model Supervision》2024年12月发表,来自Cruise和美国东北大学的论文。 人类驾驶员依靠常识推理来驾驭多样化和动态的现实世界场景。现有的端到端(E2E)自动驾驶&#xff0…

目标检测中的损失函数(三) | SIoU WIoUv1 WIoUv2 WIoUv3

🚀该系列将会持续整理和更新BBR相关的问题,如有错误和不足恳请大家指正,欢迎讨论!!! SCYLLA-IoU(SIoU)来自挂在2022年arxiv上的文章:《SIoU Loss: More Powerful Learnin…

http Status 400 - Bbad request 网站网页经常报 HTTP 400 错误,清缓存后就好了的原因

目录 一、HTTP 400 错误的常见成因(一)问题 URL(二)缓存与 Cookie 异常(三)请求头信息错误(四)请求体数据格式不正确(五)文件尺寸超标(六)请求方法不当二、清缓存为何能奏效三、其他可以尝试的解决办法(一)重新检查 URL(二)暂时关闭浏览器插件(三)切换网络环…

【DeepMLF】具有可学习标记的多模态语言模型,用于情感分析中的深度融合

这是一篇我完全看不懂的论文,写的好晦涩,适合唬人,所以在方法部分我以大白话为主 abstract 在多模态情感分析(MSA)中,多模态融合已经得到了广泛的研究,但融合深度和多模态容量分配的作用还没有得到充分的研究。在这项工作中,我们将融合深度、可扩展性和专用多模容量作…

【ASP.net】在Windows 11上安装IIS并测试C# Web项目的踩坑实录

摘要 多年未接触.NET技术栈的田辛老师,最近因项目需求重新搭建测试环境。本文记录了Windows 11环境下安装IIS服务的全过程,以及一个让开发者抓狂的“空白页面”问题的解决方案。 1. 基础环境配置 工欲善其事,必先利其器。本次环境搭建选择…

【IP101】图像特征提取技术:从传统方法到深度学习的完整指南

🌟 特征提取魔法指南 🎨 在图像处理的世界里,特征提取就像是寻找图像的"指纹",让我们能够识别和理解图像的独特性。让我们一起来探索这些神奇的特征提取术吧! 📚 目录 基础概念 - 特征的"体…

HybridCLR 详解:Unity 全平台原生 C# 热更新方案

HybridCLR(原 Huatuo)是 Unity 平台革命性的热更新解决方案,它通过扩展 Unity 的 IL2CPP 运行时,实现了基于原生 C# 的完整热更新能力。下面从原理到实践全面解析这一技术。 一、核心原理剖析 1. 技术架构 原始 IL2CPP 流程&am…

机器学习——逻辑回归ROC练习

一、 题目要求: 给定以下二分类模型的预测结果,手动绘制ROC曲线并计算AUC值: y_true [0, 1, 0, 1, 0, 1] # 真实标签(0负类,1正类) y_score [0.2, 0.7, 0.3, 0.6, 0.1, 0.8] # 模型预测得分 代码展示…

Python项目源码69:Excel数据筛选器1.0(tkinter+sqlite3+pandas)

功能说明:以下是一个使用Tkinter和Pandas实现的完整示例,支持Excel数据读取、双表格展示和高级条件筛选功能: 1.文件操作:点击"打开文件"按钮选择Excel文件(支持.xlsx和.xls格式),自…

php8 枚举使用教程

简介 PHP 从 8.1 开始原生支持枚举(enum),这是 PHP 向类型安全和现代语言特性迈进的重要一步。枚举可以定义一组有穷的、不可变的常量集合,常用于表示状态值、选项类型等。 基础语法 PHP 支持两种类型的枚举: 纯枚…

【Linux】Linux环境基础开发工具

前言 本篇博客我们来了解Linux环境下一些基础开发工具 💓 个人主页:zkf& ⏩ 文章专栏:Linux 若有问题 评论区见📝 🎉欢迎大家点赞👍收藏⭐文章 目录 1.Linux 软件包管理器 yum 2.Linux开发工具 2.1…

vue2开发者sass预处理注意

vue2开发者sass预处理注意 sass的预处理器,早年使用node-sass,也就是vue2最初默认的编译器。 sass官方推出了dart-sass来替代。 node-sass已经停维很久了。 vue3默认使用的是dart-sass。 Uniapp的官方文档截图 从 HBuilderX 4.56 ,vue2 …

Spring MVC Controller 方法的返回类型有哪些?

Spring MVC Controller 方法的返回类型非常灵活,可以根据不同的需求返回多种类型的值。Spring MVC 会根据返回值的类型和相关的注解来决定如何处理响应。 以下是一些常见的 Controller 方法返回类型: String: 最常见的类型之一,用于返回逻辑…

[ctfshow web入门] web55

信息收集 这里把小写字母都过滤了&#xff0c;众所周知linux是大小写区分的&#xff0c;没有小写字母根本整不出来命令 if(isset($_GET[c])){$c$_GET[c];if(!preg_match("/\;|[a-z]|\|\%|\x09|\x26|\>|\</i", $c)){system($c);} }else{highlight_file(__FILE…

2021-11-11 C++泰勒sin(x)以2步进乘方除以阶乘加减第N项

缘由c书本题&#xff0c;求解了&#xff0c;求解-编程语言-CSDN问答 int n 10, d 3, z -1; double x 2.5, xx x;while (n){xx (乘方(x, d) / 阶乘(d)) * z;d 2, --n, z * -1;}std::cout << xx << std::endl;

湖仓一体化介绍

目录 一、湖仓一体化的定义与核心概念 二、湖仓一体化出现的背景 (一)数据仓库的局限性 (二

仓颉编程语言快速入门:从零构建全场景开发能力

在万物互联的智能时代,编程语言的演进始终与计算范式的革新紧密相连。华为推出的仓颉编程语言(Cangjie Programming Language)以“原生智能化、天生全场景”为核心理念,为开发者提供了一种兼顾高效开发与极致性能的新选择。本文将带你从零开始,快速掌握这门面向未来的语言…