73.Unity网络开发基础知识总结

  1. 73.总结
    1. 73.1 知识点
      1. 学习的主要内容
      2. 必须达到的水平
      3. 学会举一反三
      4. 学会了这些对于我们的意义
    2. 73.2 核心要点速览
      1. 网络基本概念
      2. IP地址、端口与Mac地址
      3. 客户端与服务端
      4. 数据通信模型
      5. 网络协议模型
        1. OSI 七层模型与 TCP/IP 四层模型对比
      6. TCP 与 UDP 协议对比
        1. TCP 三次握手
        2. TCP 四次挥手
      7. 网络通信概述
        1. 游戏联网类型
        2. 连接方式对比
        3. 通信方式
      8. IP地址和端口类
      9. 网络通信中序列化和反序列化2进制数据
        1. 字符编码概述
        2. 序列化/反序列化核心类
        3. 序列化流程
        4. BaseData 序列化基类
      10. Socket类
      11. TCP同步服务端和客户端基础实现
        1. 服务端基本实现
        2. 客户端实现:
        3. 多客户端处理与封装设计
      12. 区分消息类型
      13. 分包和粘包
      14. TCP同步退出消息和心跳消息
        1. 客户端主动断开连接
        2. 心跳消息
      15. TCP异步通信
        1. 同步 vs 异步
        2. 两种异步 API 风格
        3. 异步服务端封装要点
      16. Socket类TCP异步常用成员
      17. UDP通信
        1. UDP vs TCP 核心区别
        2. UDP 编程流程
        3. UDP 关键特点
        4. UDP 同步收发示例
        5. 实现UDP服务端通信收发字符串
      18. Socket类UDP异步常用方法
      19. FTP
        1. FTP上传文件
        2. FTP下载文件
      20. HTTP通信
        1. HTTP 工作原理
        2. HTTP 关键类
        3. HTTP 下载数据
        4. HTTP 上传数据
        5. Get 与 Post 对比
        6. ContentType 常用类型
      21. WWW
      22. WWWForm
      23. UnityWebRequest
        1. 常用上传和获取数据
        2. 高级上传和获取数据
      24. Protobuf
        1. Protobuf配置规则
        2. Protobuf协议使用
      25. 大小端模式
        1. 大小端模式的基本概念
        2. 大小端模式的影响及转换
      26. 消息加密解密
        1. 加密解密基本概念
        2. 加密算法分类
      27. UnityWebRequest高级操作
        1. DownloadHandler 下载处理类
        2. UploadHandler 上传处理类
      28. 自定义协议生成工具
      29. Protobuf协议工具
    3. 73.3 面试题精选
      1. 基础题
        1. 1. IP地址和端口的作用是什么?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      2. 进阶题
        1. 1. C/S模型和P2P模型有什么区别?游戏开发中如何选择?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
      3. 深度题
        1. 1. 请详细说明TCP三次握手和四次挥手的过程及原因
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        2. 2. TCP和UDP有什么区别?游戏开发中如何选择?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        3. 3. 什么是分包和黏包?如何解决?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        4. 4. 心跳消息的作用是什么?如何实现?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章
        5. 5. HTTP中Get和Post有什么区别?
          1. 题目
          2. 深入解析
          3. 答题示例
          4. 参考文章

73.总结


73.1 知识点

学习的主要内容

必须达到的水平

学会举一反三

学会了这些对于我们的意义


73.2 核心要点速览

网络基本概念

网络类型对比

类型 范围 典型场景
局域网(LAN) 小范围(家庭、学校、公司) 内部文件共享、打印机共享
以太网 局域网的一种实现方式 有线局域网标准
城域网(MAN) 城市范围 城市宽带网络
广域网(WAN) 跨地区、跨国 企业跨地域互联
互联网(Internet) 全球范围 全球信息互联
万维网(WWW) 基于互联网的信息系统 网页浏览
  • 网络本质:由若干设备和连接设备的链路构成,数据以二进制电脉冲形式传输
  • 拓扑结构:星型、总线型、环型、树型、网状等,各有优缺点

IP地址、端口与Mac地址

IP地址:网络中设备的逻辑地址,用于定位网络中的计算机

  • IPv4:32位,4字节,如 192.168.1.1
  • IPv6:128位,16字节,地址空间更大
  • 特殊地址:127.0.0.1 表示本机

端口号:标识计算机上的具体应用程序,范围 0~65535

  • 0~1023:知名端口(系统保留)
  • 1024~49151:注册端口
  • 49152~65535:动态端口

Mac地址:网卡的物理地址,全球唯一,用于局域网内设备识别

三者关系:IP 定位网络中的计算机 → 端口定位计算机上的程序 → Mac 地址用于局域网内的物理寻址

客户端与服务端

角色 职责
客户端 发起连接请求,向服务端请求数据或服务
服务端 监听连接,处理请求,返回响应

游戏服务器职责

  • 玩家数据存储
  • 消息转发
  • 网络连接管理
  • 游戏逻辑处理

数据通信模型

模型 特点 适用场景
C/S(Client/Server) 客户端请求、服务端响应,集中式管理 网络游戏、企业应用
B/S(Browser/Server) 浏览器作为客户端,无需安装客户端软件 Web 应用
P2P(Peer to Peer) 对等网络,每个节点既是客户端又是服务端 局域网游戏、文件共享

游戏架构选择:网络游戏多用 C/S 模型,局域网游戏可用 P2P 模型

网络协议模型

网络协议:设备之间进行网络连接和通信的标准和规则

OSI 七层模型与 TCP/IP 四层模型对比

OSI 七层模型 TCP/IP 四层模型 主要职能
应用层 应用层 为应用程序提供服务,定义协议(HTTP、FTP、DNS 等)
表示层 应用层 数据格式转换、加密解密
会话层 应用层 建立、管理、维护会话
传输层 传输层 端到端连接,加入端口信息(TCP、UDP)
网络层 网络层 IP 地址选址、路由选择
数据链路层 网络接口层 分帧、确定 MAC 地址
物理层 网络接口层 传输比特流和电信号

程序员关注点:主要工作在应用层和传输层,Socket 编程涉及传输层

TCP 与 UDP 协议对比

特性 TCP UDP
连接方式 面向连接 无连接
可靠性 可靠(无差错、不丢失、不重复、有序) 不可靠
传输效率 较低 较高
连接对象 一对一 支持一对多、多对多
适用场景 文件传输、消息推送 实时游戏、视频直播

TCP 三次握手

  1. 客户端 → 服务端:发送连接请求
  2. 服务端 → 客户端:授予连接
  3. 客户端 → 服务端:确认连接

目的:建立可靠的通信连接

TCP 四次挥手

  1. 客户端 → 服务端:请求断开连接
  2. 服务端 → 客户端:确认,等待发送剩余数据
  3. 服务端 → 客户端:数据发送完毕,可以断开
  4. 客户端 → 服务端:确认断开

目的:优雅地终止连接,确保数据完整传输

网络通信概述

游戏联网类型

类型 特点 典型场景
弱联网 偶尔需要网络,离线也能玩 单机游戏、休闲游戏
强联网 必须联网,实时交互 MMO、MOBA、FPS

连接方式对比

类型 特点 协议 适用场景
长连接 保持连接,实时通信 TCP/UDP 实时对战、聊天
短连接 用完即断,按需连接 HTTP 数据查询、资源下载

通信方式

方式 特点 适用场景
Socket 底层通信,支持 TCP/UDP,实时性强 游戏实时通信
HTTP 请求-响应模式,无状态 资源下载、数据接口
FTP 文件传输协议 文件上传下载

IP地址和端口类

分类 详情
IPAddress类 命名空间:System.Net
初始化方式:
1. byte数组:byte[] ipAddress = new byte[] { 118, 102, 111, 11 }; IPAddress iPAddress1 = new IPAddress(ipAddress);
2. long长整型:IPAddress iPAddress2 = new IPAddress(0x76666F0B);(不建议使用)
3. 字符串转换:IPAddress iPAddress3 = IPAddress.Parse(“118.102.111.11”);(推荐)
特殊IP地址:127.0.0.1代表本机地址
静态成员:IPAddress.IPv6Any(获取可用的IPv6地址)
IPEndPoint类 命名空间:System.Net
作用:将网络端点表示为IP地址和端口号,用IP地址和端口号表示计算机的一个程序
初始化方式:
1. long长整型IP和端口号:IPEndPoint iPEndPoint1 = new IPEndPoint(0x79666F0B, 8080);
2. IP地址对象和端口号:IPEndPoint iPEndPoint2 = new IPEndPoint(IPAddress.Parse(“118.102.111.11”), 8080);(推荐)
IPHostEntry 作为域名解析返回值,用于获取IP、主机名等信息。成员变量有:AddressList(获取关联IP)、Aliases(获取主机别名列表)、HostName(获取DNS名称)
Dns 静态类,提供根据域名获取IP的方法。常用方法有:GetHostName(获取本地主机名,如print(Dns.GetHostName());)、GetHostEntry(同步获取IP,如IPHostEntry entry = Dns.GetHostEntry(“www.baidu.com");)、GetHostEntryAsync(异步获取IP,如Task task = Dns.GetHostEntryAsync(“www.baidu.com“); await task;)

网络通信中序列化和反序列化2进制数据

字符编码概述

编码 特点 适用场景
ASCII 128 个字符,1 字节表示 英文环境
Unicode 统一字符集,包含全球符号 字符集标准
UTF-8 Unicode 的变长实现,1~4 字节 网络通信推荐

乱码原因:编码和解码使用了不同的字符编码规则

序列化/反序列化核心类

类名 详情
BitConverter 所在命名空间:System
1. GetBytes():将非字符串类型数据(如int、short、bool等)转换为字节数组,用于类对象二进制序列化时处理非字符串类型变量。
2. ToInt32()、ToInt16()、ToBoolean()等:将字节数组转换为对应的非字符串类型数据(如int、short、bool等),用于类对象二进制反序列化时将字节数组分组转换为对应类型变量。
Encoding 所在命名空间:System.Text
1. UTF8.GetBytes():将字符串转换为字节数组,在类对象二进制序列化时,先计算字符串字节数组长度并序列化,再序列化字符串实际内容。
2. UTF8.GetString():传入字节数组、起始位置和长度,将字节数组转换为字符串,在类对象二进制反序列化时,先读取字符串字节数组长度,再根据长度和字节数组转换为字符串。

序列化流程

  1. 计算容量:确定字节数组大小(字符串需先计算长度)
  2. 声明容器:创建对应大小的 byte[]
  3. 依次写入:使用 CopyTo 将各字段字节数组写入容器

字符串处理:先写长度(4 字节 int),再写字符串内容

BaseData 序列化基类

自定义序列化基类可封装常用读写方法:

  • 写入WriteIntWriteShortWriteFloatWriteStringWriteData
  • 读取ReadIntReadShortReadFloatReadStringReadData<T>
  • 抽象方法GetBytesNum()Writing()Reading()

为什么不用 BinaryFormatter:跨语言兼容性差,服务端可能用其他语言

Socket类

类名 详情
Socket 命名空间:System.Net.Sockets
作用:支持TCP/IP网络通信的基本操作单位,是网络通信的数据通道,包含本机与对方主机的IP地址和端口、双方通信协议信息,可连接客户端和服务端以收发消息。常用于长连接游戏的通信。
类型:
1. 流套接字:用于TCP通信,提供面向连接、可靠、有序、无差错且无重复的数据传输服务。
2. 数据报套接字:用于UDP通信,提供无连接通信服务,数据包长度不超32KB,不保证正确性和顺序,可能重发、丢失。
3. 原始套接字:用于IP数据包通信,访问协议较低层,用于侦听和分析数据包(不常用)。
构造函数:public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType);
构造函数参数说明:
1. AddressFamily:网络寻址枚举类型,常用InterNetwork(IPv4寻址)、InterNetwork6(IPv6寻址)。
2. SocketType:套接字枚举类型,常用Dgram(用于UDP通信)、Stream(用于TCP通信)。
3. ProtocolType:协议类型枚举类型,常用TCP、UDP。
常用搭配:
1. SocketType.Dgram + ProtocolType.Udp:UDP协议通信。
2. SocketType.Stream + ProtocolType.Tcp:TCP协议通信。
实例化:
1. TCP流套接字:Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
2. UDP数据报套接字:Socket socketUdp = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
常用属性:
1. Connected:套接字的连接状态。
2. SocketType:套接字的类型。
3. ProtocolType:套接字的协议类型。
4. AddressFamily:套接字的寻址方案。
5. Available:套接字可读取字节数。
6. LocalEndPoint:本机EndPoint对象。
7. RemoteEndPoint:远程EndPoint对象。
常用方法:
服务端:
1. Bind:绑定IP和端口,传入IPEndPoint对象。
2. Listen:设置客户端连接的最大数,传入整型数字。
3. Accept:等待客户端连入,返回一个Socket对象。
客户端:
1. Connect:连接远程服务端,传入IPEndPoint对象。
客户端服务端通用:
1. Shutdown:释放连接并关闭Socket,选择Both可同时防止发送和接收。
2. Close:关闭连接,释放所有Socket关联资源。
总结:TCP和UDP两种长连接通信方案基于Socket套接字,需掌握TCP和UDP的Socket套接字声明。

TCP同步服务端和客户端基础实现

服务端基本实现

(1)创建套接字Socket对象(TCP):

//1.创建套接字Socket(TCP)
Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

(2)用Bind方法将套接字与本地地址绑定:

//2.用Bind方法将套接字与本地地址绑定
try
{
    IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);//把本机作为服务端程序 IP地址传入本机
    socketTcp.Bind(iPEndPoint);//绑定
}
catch (Exception e)
{
    //如果IP地址不合法或者端口号被占用可能报错
    Console.WriteLine("绑定报错" + e.Message);
    return;
}

(3)用Listen方法监听:

//3.用Listen方法监听
socketTcp.Listen(1024);//最大接收1024个客户端
Console.WriteLine("服务端绑定监听结束,等待客户端连入");

(4)用Accept方法等待客户端连接,建立连接,Accept返回新套接字:

//5.建立连接,Accept返回新套接字
Socket socketClient = socketTcp.Accept();
//Accept是阻塞式的方法 会把主线程卡住 一定要等到客户端接入后才会继续执行后面的代码
//客户端接入后 返回新的Socket对象 这个新的Socket可以理解为客户端和服务端的通信通道
Console.WriteLine("有客户端连入了");

(5)用Send和Receive相关方法收发数据:

//6.用Send和Receive相关方法收发数据
//发送字符串转成的字节数组给客户端
socketClient.Send(Encoding.UTF8.GetBytes("欢迎连入服务端"));
//声明接收客户端信息的字节数组 声明1024容量代表能接收1kb的信息
byte[] result = new byte[1024];
//接收客户端信息 返回值为接收到的字节数
int receiveNum = socketClient.Receive(result);
//打印 远程发送信息的客户端的IP和端口 以及 发送过来的字符串
Console.WriteLine("接收到了{0}发来的消息:{1}",
    socketClient.RemoteEndPoint.ToString(),
    Encoding.UTF8.GetString(result, 0, receiveNum));

(6)用Shutdown方法释放连接:

//7.用Shutdown方法释放连接
//注意断开的是客户端和服务端的通信通道
socketClient.Shutdown(SocketShutdown.Both);

(7)关闭套接字:

//8.关闭套接字
//注意关闭的是客户端和服务端的通信通道
socketClient.Close();

客户端实现:

(1)创建套接字Socket Tcp

Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

(2)用Connect方法与服务端相连:

//确定服务端的IP和端口 正常来说填的应该是远端服务器的ip地址以及端口号
//由于只有一台电脑用于测试 本机也当做服务器 所以传入当前电脑的ip地址
IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
try
{
    //连接
    socketTcp.Connect(iPEndPoint);
}
catch (SocketException e)
{
    //如果连接没有开启或者服务器异常 会报错 不同的返回码代表不同报错
    if (e.ErrorCode == 10061)
        print("服务器拒绝连接");
    else
        print("连接服务器失败" + e.ErrorCode);
    return;
}

(3)用Send和Receive相关方法收发数据:

//接收数据 
//声明接收数据字节数组
byte[] receiveBytes = new byte[1024];
//Receive方法接收数据 返回接收多少字节
int receiveNum = socketTcp.Receive(receiveBytes);

print("收到服务端发来的消息:" + Encoding.UTF8.GetString(receiveBytes, 0, receiveNum));

//发送数据
socketTcp.Send(Encoding.UTF8.GetBytes("你好,我是韬老狮的客户端"));

(4)用Shutdown方法释放连接:

//4.用Shutdown方法释放连接
socketTcp.Shutdown(SocketShutdown.Both);

(5)关闭套接字:

//5.关闭套接字
socketTcp.Close();

多客户端处理与封装设计

核心问题:同步 Socket 的 AcceptSendReceive 会阻塞主线程

解决思路

  1. 使用独立线程处理连接接受和消息收发
  2. 使用消息队列解耦主线程和网络线程
  3. 使用线程池处理耗时消息解析

服务端封装要点

职责
ServerSocket 管理监听套接字、客户端字典、广播方法
ClientSocket 封装单个客户端的收发逻辑、自动分配 ID

客户端封装要点

职责
TcpNetManager 单例模式、消息队列、Update 中处理接收消息

线程安全:操作客户端字典时需加锁,遍历时不能直接移除元素

区分消息类型

问题:多种消息类型(玩家信息、聊天信息、登录信息等)序列化后都是字节数组,接收方如何区分?

解决方案:在消息头部添加消息 ID(int/short/byte/long)

消息结构

| 消息ID (4字节) | 消息体内容 |

实现要点

  1. 创建 BaseMessage 基类继承 BaseData,添加 GetID() 方法
  2. 具体消息类继承 BaseMessage,实现序列化/反序列化
  3. 接收时先解析前 4 字节得到 ID,再根据 ID 选择对应类反序列化
int msgID = BitConverter.ToInt32(receiveBytes, 0);
switch (msgID)
{
    case 1001:
        PlayerMessage msg = new PlayerMessage();
        msg.Reading(receiveBytes, 4);
        break;
}

分包和粘包

概念

  • 分包:一个消息被拆成多个包发送
  • 黏包:多个消息黏在一起发送
  • 注意:分包和黏包可能同时发生

解决思路:在消息头部添加消息长度字段

消息结构(解决分包黏包后):

| 消息ID (4字节) | 消息长度 (4字节) | 消息体内容 |

处理逻辑

  1. 维护缓存数组 cacheBytes 和缓存长度 cacheNum
  2. 收到数据后拼接到缓存数组尾部
  3. 循环解析:先判断是否有完整头部(8字节),再根据消息长度判断是否有完整消息体
  4. 完整消息:解析并入队;不完整:保留到下次继续处理

关键代码结构

// 拼接新数据
receiveBytes.CopyTo(cacheBytes, cacheNum);
cacheNum += receiveNum;

while (true)
{
    if (cacheNum - nowIndex >= 8) // 可解析头部
    {
        msgID = BitConverter.ToInt32(cacheBytes, nowIndex);
        msgLength = BitConverter.ToInt32(cacheBytes, nowIndex + 4);
    }
    if (cacheNum - nowIndex >= 8 + msgLength) // 可解析完整消息
    {
        // 解析消息体...
        nowIndex += 8 + msgLength;
    }
    else // 分包:缓存剩余数据
    {
        Array.Copy(cacheBytes, nowIndex, cacheBytes, 0, cacheNum - nowIndex);
        cacheNum -= nowIndex;
        break;
    }
}

TCP同步退出消息和心跳消息

客户端主动断开连接

问题:客户端直接调用 ShutdownClose 后,服务端无法及时感知

解决方案

  1. 客户端调用 Disconnect(false) 后再 Close()
  2. 发送自定义退出消息 QuitMessage 通知服务端

服务端处理要点

  • 操作客户端字典时加 lock 保证线程安全
  • 使用 delList 暂存待移除客户端,遍历结束后统一处理
  • 收发消息时检测 Connected 状态

心跳消息

概念:长连接中定期发送的特殊数据包,用于确认对方在线

作用

  1. 检测非正常断开的客户端(超时判断)
  2. 保持连接活跃,防止防火墙/路由器断开

实现要点

  • 客户端:定时发送 HeartMessage(如每 2 秒)
  • 服务端:记录上次收到消息时间,超时则断开
// 客户端定时发送
InvokeRepeating("SendHeartMsg", 0, SEND_HEART_MSG_TIME);

// 服务端超时检测
if (frontTime != -1 && 
    DateTime.Now.Ticks / TimeSpan.TicksPerSecond - frontTime >= TIME_OUT_TIME)
{
    // 超时,断开连接
}

TCP异步通信

同步 vs 异步

特性 同步方法 异步方法
执行方式 方法执行完毕后才继续 方法可能未执行完就继续
阻塞 阻塞当前线程 不阻塞,内部开多线程
适用场景 简单逻辑、控制台程序 游戏客户端、高并发服务端

两种异步 API 风格

Begin/End 系列

  • BeginAccept + EndAccept:服务端接受连接
  • BeginConnect + EndConnect:客户端连接
  • BeginReceive + EndReceive:接收消息
  • BeginSend + EndSend:发送消息
  • 回调函数参数类型:IAsyncResult
  • 获取 Socket:asyncResult.AsyncState as Socket

Async 系列

  • AcceptAsyncConnectAsyncSendAsyncReceiveAsync
  • 参数类型:SocketAsyncEventArgs
  • 回调事件:Completed
  • 设置缓冲区:SetBuffer(bytes, offset, count)

异步服务端封装要点

职责
ServerSocket 管理监听套接字、客户端字典、BeginAccept
ClientSocket 封装单个客户端的异步收发、心跳检测

注意:异步方法中分包黏包处理与同步相同,但接收后数据直接在 cacheBytes

Socket类TCP异步常用成员

类名 方法 参数 详情
Socket BeginAccept(AsyncCallback callback, object state) callback:处理异步接受客户端连接操作完成时的回调函数;state:服务端Socket对象 服务端开始接收客户端的连接。
Socket EndAccept(IAsyncResult asyncResult) asyncResult:BeginAccept方法返回的IAsyncResult对象 服务端检测到客户端的连接结束,得到客户端Socket。
Socket BeginConnect(EndPoint remoteEP, AsyncCallback callback, object state) remoteEP:服务器的终结点(如IPEndPoint);callback:处理客户端异步连接操作完成时的回调函数;state:客户端的Socket对象 客户端异步连接到服务器。
Socket EndConnect(IAsyncResult asyncResult) asyncResult:BeginConnect方法返回的IAsyncResult对象 客户端完成异步连接到服务器端的操作。
Socket BeginReceive(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state) buffer:接收消息的字节数组;offset:从接收消息的字节数组第几位开始存储;size:字节数组长度;socketFlags:Socket标识;callback:接收消息回调函数;state:Socket对象 开始接收消息。
Socket EndReceive(IAsyncResult asyncResult) asyncResult:BeginReceive方法返回的IAsyncResult对象 结束接收消息,返回接收的字节数组长度。
Socket BeginSend(byte[] buffer, int offset, int size, SocketFlags socketFlags, AsyncCallback callback, object state) buffer:发送消息的字节数组;offset:从发送消息的字节数组第几位开始发送;size:字节数组长度;socketFlags:Socket标识;callback:发送消息回调函数;state:Socket对象 开始发送消息。
Socket EndSend(IAsyncResult asyncResult) asyncResult:BeginSend方法返回的IAsyncResult对象 结束发送消息,返回成功发送的字节数。
Socket AcceptAsync(SocketAsyncEventArgs e) e:SocketAsyncEventArgs对象,用于处理异步接受客户端连接操作完成后的操作 服务端异步接受客户端连接。
Socket ConnectAsync(SocketAsyncEventArgs e) e:SocketAsyncEventArgs对象,用于处理异步连接到服务器操作完成后的操作 客户端异步连接到服务器。
Socket SendAsync(SocketAsyncEventArgs e) e:SocketAsyncEventArgs对象,设置好发送数据的缓冲区等信息,用于处理异步发送消息操作完成后的操作 异步发送消息。
Socket ReceiveAsync(SocketAsyncEventArgs e) e:SocketAsyncEventArgs对象,设置好接收数据的缓冲区等信息,用于处理异步接收消息操作完成后的操作 异步接收消息。

UDP通信

UDP vs TCP 核心区别

特性 UDP TCP
连接方式 无连接,直接发 面向连接,需三次握手
可靠性 不保证送达、不保证顺序 可靠传输、有序
消息边界 保留消息边界,无黏包 流式传输,存在分包黏包
资源消耗
适用场景 实时游戏、音视频直播 文件传输、消息推送

UDP 编程流程

服务端/客户端通用流程

  1. 创建套接字:Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
  2. 绑定本机地址:Bind(IPEndPoint)
  3. 收发消息:SendTo / ReceiveFrom
  4. 关闭:Shutdown + Close

UDP 关键特点

无黏包:UDP 保留消息边界,每条消息独立

分包限制

  • 互联网:单包 ≤ 548 字节
  • 局域网:单包 ≤ 1472 字节
  • 超过限制会自动分包,可能导致丢包

安全注意:商业游戏中通常先建立 TCP 连接验证身份,再用 UDP 通信

UDP 同步收发示例

//1.创建套接字 寻址类型还是Ipv4 Soket类型使用数据包 协议选择Udp
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

//2.绑定本机地址 注意模拟测试时客户端本机和服务端远程机的端口号不能一样
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
socket.Bind(ipPoint);

//3.发送到指定目标 注意模拟测试时客户端本机和服务端远程机的端口号不能一样
IPEndPoint remoteIpPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
//指定要发送的字节数 和 远程计算机的 IP和端口
socket.SendTo(Encoding.UTF8.GetBytes("韬老狮来了"), remoteIpPoint);

//4.接收消息
//装接收消息的字节数组
byte[] bytes = new byte[512];
//装接收消息的IP地址和端口的对象 在ReceiveFrom方法会使用ref赋值 主要是用来记录 谁发的信息给你 传入函数后 在内部 它会帮助我们进行赋值
EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);
//接收消息 得到消息长度
int length = socket.ReceiveFrom(bytes, ref remoteIpPoint2);
print("IP:" + (remoteIpPoint2 as IPEndPoint).Address.ToString() +
    "port:" + (remoteIpPoint2 as IPEndPoint).Port +
    "发来了" +
    Encoding.UTF8.GetString(bytes, 0, length));

//5.释放关闭
socket.Shutdown(SocketShutdown.Both);
socket.Close();

实现UDP服务端通信收发字符串

//1.创建套接字 寻址类型还是Ipv4 Soket类型使用数据包 协议选择Udp
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

//2.绑定本机地址 注意模拟测试时客户端本机和服务端远程机的端口号不能一样
IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
socket.Bind(ipPoint);
Console.WriteLine("服务器开启");

//3.接收消息
//装接收消息的字节数组
byte[] bytes = new byte[512];
//装接收消息的IP地址和端口的对象 在ReceiveFrom方法会使用ref赋值 主要是用来记录 谁发的信息给你 传入函数后 在内部 它会帮助我们进行赋值
EndPoint remoteIpPoint2 = new IPEndPoint(IPAddress.Any, 0);
int length = socket.ReceiveFrom(bytes, ref remoteIpPoint2);
Console.WriteLine("IP:" + (remoteIpPoint2 as IPEndPoint).Address.ToString() +
    "port:" + (remoteIpPoint2 as IPEndPoint).Port +
    "发来了" +
    Encoding.UTF8.GetString(bytes, 0, length));

//4.发送到指定目标
//由于服务端先收了消息 所以服务端已经知道谁发了消息给服务端 是使用remoteIpPoint2记录的 服务端直接发给它就行了
socket.SendTo(Encoding.UTF8.GetBytes("欢迎发送消息给服务器"), remoteIpPoint2);

//5.释放关闭
socket.Shutdown(SocketShutdown.Both);
socket.Close();

Socket类UDP异步常用方法

类名 方法 参数 详情
Socket BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP, AsyncCallback callback, object state) buffer:要发送的数据字节数组;offset:从字节数组的哪个位置开始发送;size:发送数据的长度;socketFlags:SocketFlags标识;remoteEP:目标IP和端口号;callback:回调函数;state:回调函数的参数 开始向指定IP和端口异步发送数据,将结果异步传回回调函数进行处理
Socket EndSendTo(IAsyncResult asyncResult) asyncResult:BeginSendTo方法返回的IAsyncResult对象 结束异步发送操作
Socket BeginReceiveFrom(byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state) buffer:缓冲区;offset:缓冲区的起始位置;size:最大接收数据长度;socketFlags:SocketFlags标识;remoteEP:接收数据的来源IPEndPoint;callback:回调函数;state:回调函数的参数 开始异步从UDP客户端接收数据,将接收到的数据放入缓冲区,接收数据的来源ip和端口号会被保存,有数据到达时调用回调函数处理
Socket EndReceiveFrom(IAsyncResult asyncResult, ref EndPoint remoteEP) asyncResult:BeginReceiveFrom方法返回的IAsyncResult对象;remoteEP:接收数据的来源IPEndPoint 结束异步接收操作,并返回接收到的字节数
Socket SendToAsync(SocketAsyncEventArgs e) e:SocketAsyncEventArgs对象,设置好要发送的数据、目标IP地址等信息 开始向指定IP和端口发送数据,发送完成后在回调函数处理
Socket ReceiveFromAsync(SocketAsyncEventArgs e) e:SocketAsyncEventArgs对象,设置好接收数据的缓冲区、接收来源的IPEndPoint等信息 开始异步从UDP客户端接收数据,接收完成后在回调函数进行处理

FTP

类名 用途/功能 重要方法 重要成员
NetworkCredential(System.Net) 在网络身份验证中存储用户名和密码信息,用于 FTP 文件传输场景下提供访问 FTP 服务器所需的认证凭据
FtpWebRequest(System.Net) 用于执行上传、下载、删除 FTP 服务器上的文件等操作 1. Create:创建新 Ftp 请求。
2. Abort:终止 Ftp 传输。
3. GetRequestStream:获取用于上传的流。
4. GetResponse:返回 FTP 服务器响应。
1. Credentials:设置通信凭证为NetworkCredential对象。
2. KeepAlive:设置完成请求时是否关闭到 FTP 服务器的控制连接。
3. Method:设置 FTP 请求操作命令,如删除、下载、列目录等。
4. UseBinary:指定是否采用二进制模式传输数据。
5. RenameTo:重命名文件。
FtpWebResponse(System.Net) 封装 FTP 服务器对请求的响应,提供操作状态及从服务器下载的数据,用于表示响应信息,含响应代码、消息等 1. Close:释放所有资源。
2. GetResponseStream:返回从 FTP 服务器下载数据的流。
1. ContentLength:接收到数据的长度。
2. ContentType:接收数据的类型。
3. StatusCode:FTP 服务器下发的最新状态码。
4. StatusDescription:状态代码的文本描述。
5. BannerMessage:登录前建立连接时服务器发送的消息。
6. ExitMessage:获取 FTP 会话结束时服务器发送的消息。
7. LastModified:获取 FTP 服务器上文件的最后修改日期和时间。

FTP上传文件

try
{
    // 创建一个Ftp连接,pic.png代表想上传名叫pic的png图片。
    // 这里的ftp://127.0.0.1是使用本机开启服务器进行测试,实际使用时应该是远端服务器IP。
    FtpWebRequest ftpWebRequest = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/pic.png")) as FtpWebRequest;

    // 设置通信凭证(如果不支持匿名,就必须设置这一步)。
    // 将代理相关信息置空,避免服务器同时有http相关服务造成冲突。
    ftpWebRequest.Proxy = null;

    // 创建并设置通信凭证。
    NetworkCredential networkCredential = new NetworkCredential("MrTao", "MrTao");
    ftpWebRequest.Credentials = networkCredential;

    // 请求完毕后是否关闭控制连接,如果想要关闭,可以设置为false。
    ftpWebRequest.KeepAlive = false;

    // 设置操作命令。
    ftpWebRequest.Method = WebRequestMethods.Ftp.UploadFile;//设置命令操作为上传文件。

    // 指定传输类型,使用二进制。
    ftpWebRequest.UseBinary = true;

    // 得到用于上传的流对象。
    Stream uploadRequestStream = ftpWebRequest.GetRequestStream();

    // 开始上传,使用流读取StreamingAssets文件夹下的名叫test的图片。
    using (FileStream fileStream = File.OpenRead(Application.streamingAssetsPath + "/test.png"))
    {
        // 我们可以一点一点的把这个文件中的字节数组读取出来,然后存入到上传流中。
        byte[] bytes = new byte[1024];

        // 返回值是真正从文件中读了多少个字节。
        int contentLength = fileStream.Read(bytes, 0, bytes.Length);

        // 不停的去读取文件中的字节,除非读取完毕了,不然一直读,并且写入到上传流中。
        while (contentLength != 0)
        {
            // 写入上传流中。
            uploadRequestStream.Write(bytes, 0, contentLength);

            // 写完了继续读。
            contentLength = fileStream.Read(bytes, 0, bytes.Length);
        }

        // 出了循环就证明写完了。
        fileStream.Close();
        uploadRequestStream.Close();

        // 上传完毕。
        print("上传结束");
    }
}
catch (Exception e)
{
    print("上传出错,失败" + e.Message);
}

FTP下载文件

try
{
    // 创建一个Ftp连接。
    // 这里和上传不同,上传的文件名是自己定义的,下载的文件名一定是资源服务器上有的,比如一张叫pic的图片。
    FtpWebRequest ftpWebRequest = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/pic.png")) as FtpWebRequest;

    // 设置通信凭证(如果不支持匿名,就必须设置这一步)。
    ftpWebRequest.Credentials = new NetworkCredential("MrTao", "MrTao");

    // 请求完毕后是否关闭控制连接,如果要进行多次操作,可以设置为false。
    ftpWebRequest.KeepAlive = false;

    // 设置操作命令。
    ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;

    // 指定传输类型。
    ftpWebRequest.UseBinary = true;

    // 代理设置为空。
    ftpWebRequest.Proxy = null;

    // 得到用于下载的流对象。
    // 相当于把请求发送给FTP服务器,返回值就会携带我们想要的信息。
    FtpWebResponse ftpWebResponse = ftpWebRequest.GetResponse() as FtpWebResponse;

    // 这就是下载的流。
    Stream downloadResponseStream = ftpWebResponse.GetResponseStream();

    // 开始下载。
    print(Application.persistentDataPath);
    using (FileStream fileStream = File.Create(Application.persistentDataPath + "/pic2.png"))
    {
        // 读取流的字节数组。
        byte[] bytes = new byte[1024];

        // 读取下载下来的流数据。
        int contentLength = downloadResponseStream.Read(bytes, 0, bytes.Length);

        // 一点一点的下载到本地流中。
        while (contentLength != 0)
        {
            // 把读取出来的字节数组写入到本地文件流中。
            fileStream.Write(bytes, 0, contentLength);

            // 继续读。
            contentLength = downloadResponseStream.Read(bytes, 0, bytes.Length);
        }

        // 下载结束,关闭流。
        downloadResponseStream.Close();
        fileStream.Close();
    }
    print("下载结束");
}
catch (Exception e)
{
    print("下载出错" + e.Message);
}

HTTP通信

HTTP 工作原理

HTTP 本质:基于 TCP 的通信协议

通信流程:建立连接 → 请求 → 响应 → 断开连接

请求类型

  • GET:获取数据
  • POST:上传数据,同时可获取响应
  • HEAD:只获取消息头,不返回具体内容

常见状态码

  • 200:成功
  • 404:找不到目标
  • 500:服务器内部错误

HTTP 关键类

类名(命名空间) 用途 重要方法 重要成员
HttpWebRequest(System.Net) 用于向服务器发送HTTP客户端请求,进行消息通信、上传、下载等操作 1. Create:创建新的WebRequest,用于HTTP相关操作
2. Abort:终止文件传输
3. GetRequestStream:获取用于上传的流
4. GetResponse:返回HTTP服务器响应
5. Begin/EndGetRequestStream:异步获取用于上传的流
6. Begin/EndGetResponse:异步获取返回的HTTP服务器响应
1. Credentials:通信凭证,设置为NetworkCredential对象
2. PreAuthenticate:是否随请求发送一个身份验证标头,一般需身份验证时设为true
3. Headers:构成标头的名称/值对的集合
4. ContentLength:发送信息的字节数,上传信息时需先设置
5. ContentType:POST请求时,需对发送内容进行内容类型设置
6. Method:操作命令设置,如Get、Post、Head等
HttpWebResponse(System.Net) 用于获取服务器反馈信息,通过HttpWebRequest对象的GetResponse()方法获取,使用完毕用Close释放 1. Close:释放所有资源
2. GetResponseStream:返回从HTTP服务器下载数据的流
1. ContentLength:接收到数据的长度
2. ContentType:接收数据的类型
3. StatusCode:HTTP服务器下发的最新状态码
4. StatusDescription:HTTP服务器下发的状态代码的文本
5. BannerMessage:登录前建立连接时HTTP服务器发送的消息
6. ExitMessage:HTTP会话结束时服务器发送的消息
7. LastModified:HTTP服务器上的文件的上次修改日期和时间
NetworkCredential、Uri、Stream、FileStream(相关命名空间) 在HTTP通讯时使用方式与FTP类似 - -

HTTP 下载数据

Head 请求类型:检测资源可用性

HttpWebRequest httpWebRequest = HttpWebRequest.Create(new Uri("http://192.168.1.101:8000/HTTPRoot/pic.png")) as HttpWebRequest;
httpWebRequest.Method = WebRequestMethods.Http.Head;
httpWebRequest.Timeout = 2000;
HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse;
if (httpWebResponse.StatusCode == HttpStatusCode.OK)
{
    print("文件存在且可用");
    print(httpWebResponse.ContentLength);
    print(httpWebResponse.ContentType);
}

Get 请求类型:下载资源到本地

HttpWebRequest httpWebRequest = HttpWebRequest.Create(new Uri("http://192.168.1.101:8000/HTTPRoot/pic.png")) as HttpWebRequest;
httpWebRequest.Method = WebRequestMethods.Http.Get;
httpWebRequest.Timeout = 3000;
HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse;
if (httpWebResponse.StatusCode == HttpStatusCode.OK)
{
    using (FileStream fileStream = File.Create(Application.persistentDataPath + "/httpPic.png"))
    {
        Stream downloadResponseStream = httpWebResponse.GetResponseStream();
        byte[] bytes = new byte[2048];
        int contentLength = downloadResponseStream.Read(bytes, 0, bytes.Length);
        while (contentLength != 0)
        {
            fileStream.Write(bytes, 0, contentLength);
            contentLength = downloadResponseStream.Read(bytes, 0, bytes.Length);
        }
    }
}

Get 请求携带额外参数

  • 格式:http://域名:端口/虚拟目录/文件名?参数名=参数值&参数名=参数值
  • 示例:http://www.aspxfans.com:8080/news/child/index.asp?boardID=5&ID=24618&page=1

HTTP 上传数据

上传规则

  1. ContentType = multipart/form-data; boundary=边界字符串
  2. 数据格式:
--边界字符串
Content-Disposition: form-data; name="字段名";filename="服务器文件名"
Content-Type: application/octet-stream

(文件数据体)
--边界字符串--

上传示例

HttpWebRequest httpWebRequest = HttpWebRequest.Create("http://192.168.1.101:8000/HTTPRoot/") as HttpWebRequest;
httpWebRequest.Method = WebRequestMethods.Http.Post;
httpWebRequest.ContentType = "multipart/form-data;boundary=MrTao";
httpWebRequest.Timeout = 500000;
httpWebRequest.Credentials = new NetworkCredential("MrTao", "MrTao");
httpWebRequest.PreAuthenticate = true;

string head = "--MrTao\r\n" +
    "Content-Disposition:form-data;name=\"file\";filename=\"http上传的文件.jpg\"\r\n" +
    "Content-Type:application/octet-stream\r\n\r\n";
byte[] headBytes = Encoding.UTF8.GetBytes(head);
byte[] endBytes = Encoding.UTF8.GetBytes("\r\n--MrTao--\r\n");

using (FileStream localFileStream = File.OpenRead(Application.streamingAssetsPath + "/test.png"))
{
    httpWebRequest.ContentLength = headBytes.Length + localFileStream.Length + endBytes.Length;
    Stream uploadRequestStream = httpWebRequest.GetRequestStream();
    uploadRequestStream.Write(headBytes, 0, headBytes.Length);
    // 写入文件数据...
    uploadRequestStream.Write(endBytes, 0, endBytes.Length);
}

Get 与 Post 对比

特性 Get Post
主要用途 获取数据 上传数据
参数位置 URL 中暴露 请求体中隐藏
数据大小 有限制(约 2048 字符) 无限制
安全性 较低 较高
缓存 可缓存 不可缓存

ContentType 常用类型

类型 用途
application/octet-stream 通用二进制数据
text/plain 通用文本数据
application/x-www-form-urlencoded 键值对参数
multipart/form-data 复合类型(文件上传)

WWW

类名(命名空间) 作用 常用方法 常用变量
WWW(UnityEngine) 用于简单访问网页,可进行数据的下载和上传。使用 HTTP 协议时默认请求类型是 Get,进行 Post 上传需配合 WWWForm 类 1. WWW:构造函数,用于创建一个 WWW 请求
2. GetAudioClip:从下载数据返回一个音效切片 AudioClip 对象
3. LoadImageIntoTexture:用下载数据中的图像来替换现有的一个 Texture2D 对象
4. LoadFromCacheOrDownload:从缓存加载 AB 包对象,如果该包不在缓存则自动下载存储到缓存中,以便以后直接从本地缓存中加载
1. assetBundle:如果加载的数据是 AssetBundle,则可直接获取加载结果
2. audioClip:如果加载的数据是音效切片文件,可直接获取加载结果(新版本用 GetAudioClip 方法)
3. bytes:以字节数组形式获取加载内容
4. bytesDownloaded:已下载的字节数
5. error:下载出错时返回错误消息
6. isDone:判断下载是否完成
7. movie:下载视频时可获取 MovieTexture 类型结果(新版本用 GetMovieTexture 方法)
8. progress:下载进度
9. text:下载数据为字符串时,以字符串形式返回内容
10. texture:下载数据为图片时,以 Texture2D 形式返回加载结果

WWWForm

类名 作用 注意事项 常用方法
WWWForm 在使用 WWW 类下载数据的基础上,若需上传数据,可结合该类使用。主要用于集成数据,可设置上传的参数或者二进制数据,结合使用时主要用 Post 请求类型,通过 Http 协议上传处理 使用 WWW 结合 WWWForm 上传数据通常需要与后端程序制定上传规则 1. WWWForm:构造函数
2. AddBinaryData:添加二进制数据
3. AddField:添加字段

UnityWebRequest

常用上传和获取数据

获取

类名 方法 方法解析 参数
UnityWebRequest Get 创建一个用于获取文本或二进制数据的 UnityWebRequest 对象。 uri:请求资源的 URI 地址,如 “http://192.168.1.101:8000/HTTPRoot/test.txt
UnityWebRequestTexture GetTexture 创建一个用于获取纹理数据的 UnityWebRequest 对象。 uri:请求纹理资源的 URI 地址,可以是 http、ftp 或 file 协议的地址,如 “http://192.168.1.101:8000/HTTPRoot/pic.png"、"ftp://127.0.0.1/pic.png“ 或 “file://“ + Application.streamingAssetsPath + “/test.png”
UnityWebRequestAssetBundle GetAssetBundle 创建一个用于获取 AB 包数据的 UnityWebRequest 对象。 uri:请求 AB 包资源的 URI 地址,如 “http://192.168.1.101:8000/HTTPRoot/model
UnityWebRequest SendWebRequest 发送 Web 请求并等待服务器响应。
DownloadHandlerTexture GetContent 从 UnityWebRequest 对象中获取下载的纹理数据。 request:UnityWebRequest 对象,用于获取其中的纹理数据
DownloadHandlerAssetBundle GetContent 从 UnityWebRequest 对象中获取下载的 AB 包数据。 request:UnityWebRequest 对象,用于获取其中的 AB 包数据

上传

类名/接口 方法/描述 参数
IMultipartFormSection 数据相关类继承的父接口,可使用父类类型的集合来存储子类对象
MultipartFormDataSection 用于传递键值对数据的类 1. 构造函数(二进制字节数组):MultipartFormDataSection(byte[] data),参数data为二进制字节数组
2. 构造函数(字符串):MultipartFormDataSection(string data),参数data为字符串
3. 构造函数(参数名,参数值,编码类型,资源类型):MultipartFormDataSection(string name, string data, Encoding encoding, string contentType),参数name为参数名,data为参数值(字符串或字节数组形式),encoding为编码类型,contentType为资源类型
4. 构造函数(参数名,字节数组,资源类型):MultipartFormDataSection(string name, byte[] data, string contentType),参数name为参数名,data为字节数组,contentType为资源类型
MultipartFormFileSection 用于传递文件数据的类 1. 构造函数(字节数组):MultipartFormFileSection(byte[] data),参数data为字节数组
2. 构造函数(文件名,字节数组):MultipartFormFileSection(string fileName, byte[] data),参数fileName为文件名,data为字节数组
3. 构造函数(字符串数据,文件名):MultipartFormFileSection(string data, string fileName),参数data为字符串数据,fileName为文件名
4. 构造函数(字符串数据,编码格式,文件名):MultipartFormFileSection(string data, Encoding encoding, string fileName),参数data为字符串数据,encoding为编码格式,fileName为文件名
5. 构造函数(表单名,字节数组,文件名,文件类型):MultipartFormFileSection(string formName, byte[] data, string fileName, string contentType),参数formName为表单名,data为字节数组,fileName为文件名,contentType为文件类型
6. 构造函数(表单名,字符串数据,编码格式,文件名):MultipartFormFileSection(string formName, string data, Encoding encoding, string fileName),参数formName为表单名,data为字符串数据,encoding为编码格式,fileName为文件名
UnityWebRequest Post方法:创建一个用于使用 POST 请求上传数据的UnityWebRequest对象
Put方法:创建一个用于使用 PUT 请求上传数据的UnityWebRequest对象
SendWebRequest方法:发送 Web 请求并等待服务器响应
1. Post方法:UnityWebRequest.Post(string uri, List formData),参数uri为请求的 URI 地址,formData为包含上传数据的IMultipartFormSection列表
2. Put方法:UnityWebRequest.Put(string uri, byte[] data),参数uri为请求的 URI 地址,data为要上传的文件内容(字节数组形式)
3. SendWebRequest方法:无参数
NetWWWManager UploadFile方法:启动异步上传文件的操作
UploadFileAsync方法:异步上传文件的具体实现
1. UploadFile方法:UploadFile(string fileName, string localPath, UnityAction<UnityWebRequest.Result> action),参数fileName为上传后的文件名,localPath为本地要上传文件的路径,action为上传完成后的回调函数
2. UploadFileAsync方法:UploadFileAsync(string fileName, string localPath, UnityAction<UnityWebRequest.Result> action),参数fileName为上传后的文件名,localPath为本地要上传文件的路径,action为上传完成后的回调函数

高级上传和获取数据

获取

类名 方法/属性 方法的参数
UnityWebRequest 构造函数
UnityWebRequest url 字符串类型的服务器地址
UnityWebRequest method 如UnityWebRequest.kHttpVerbPOST等请求类型常量
UnityWebRequest timeout 整数类型的超时时间(单位:毫秒)
UnityWebRequest redirectLimit 整数类型,0表示不进行重定向
UnityWebRequest responseCode
UnityWebRequest result
UnityWebRequest error
UnityWebRequest downloadHandler 实现了DownloadHandler的对象,如DownloadHandlerBuffer等
UnityWebRequest uploadHandler 实现了UploadHandler的对象
UnityWebRequest Get 字符串类型的请求URL
UnityWebRequest GetTexture 字符串类型的纹理URL
UnityWebRequestAssetBundle GetAssetBundle 字符串类型的AssetBundle URL
UnityWebRequest Put 字符串类型的请求URL和字节数组形式的上传内容
UnityWebRequest Post 字符串类型的请求URL和List类型的上传数据列表
UnityWebRequest isDone
UnityWebRequest downloadProgress
UnityWebRequest downloadedBytes
UnityWebRequest uploadProgress
UnityWebRequest uploadedBytes
UnityWebRequest SendWebRequest
DownloadHandlerBuffer 构造函数
DownloadHandlerFile 构造函数 字符串类型的本地文件保存路径
DownloadHandlerTexture 构造函数
DownloadHandlerAssetBundle 构造函数 字符串类型的请求URL和校验码(ulong类型,不知道则传入0)
UnityWebRequestMultimedia GetAudioClip 字符串类型的音频文件URL和AudioType类型的音频类型
DownloadHandlerAudioClip GetContent UnityWebRequest对象
CustomDownLoadFileHandler 构造函数(无参)
CustomDownLoadFileHandler 构造函数(字节数组) 字节数组类型的参数
CustomDownLoadFileHandler 构造函数(路径) 字符串类型的本地存储路径
CustomDownLoadFileHandler GetData
CustomDownLoadFileHandler ReceiveData byte[]类型的数据和int类型的数据长度
CustomDownLoadFileHandler ReceiveContentLengthHeader ulong类型的内容长度
CustomDownLoadFileHandler CompleteContent

上传

类名 方法/属性 方法的参数 描述
UnityWebRequest 构造函数 string uri, string method:请求的 URL 和 HTTP 请求方法(如 UnityWebRequest.kHttpVerbPOST) 创建一个 UnityWebRequest 对象,用于网络请求
UnityWebRequest uploadHandler 实现了 UploadHandler 的对象,如 UploadHandlerRaw、UploadHandlerFile 等 设置上传处理对象,用于处理上传的数据
UnityWebRequest SendWebRequest 发送网络请求,并等待请求返回
UnityWebRequest result 获取网络请求的结果(成功或失败)
UploadHandlerRaw 构造函数 byte[] data:要上传的字节数组 创建一个用于上传字节数组的 UploadHandlerRaw 对象
UploadHandlerRaw contentType string 类型的内容类型,如 “类型/细分类型” 设置上传数据的内容类型,若不设置,默认是 application/octet - stream
UploadHandlerFile 构造函数 string path:要上传的文件路径 创建一个用于上传文件的 UploadHandlerFile 对象

Protobuf

Protobuf配置规则

(1)配置文件后缀:统一使用.proto,可通过多个该后缀文件配置。
(2)版本号:第一行需指定,如syntax = “proto3”;,不写默认用proto2。
(3)注释方式:支持//单行注释和/* */多行注释。
(4)命名空间:用package指定,如package 命名空间名; 。
(5)消息类:使用message定义,格式为message 类名 { // 字段声明 }。
(6)成员类型和编号:成员类型包含浮点数(float、double )、整数(int32、int64等)、其他(bool、string、bytes ),每个成员需指定从1开始的唯一编号。
(7)特殊标识:optional表示可选赋值字段;repeated表示数组(required在proto3中已移除 )。
(8)枚举:用enum定义,首个常量必须为0,如enum 枚举名 { 常量1 = 0; 常量2 = 1; }。
(9)默认值:string为空字符串、bytes为空字节、bool为false、数值为0、枚举为0、message在C#中为空。
(10)嵌套:支持消息类和枚举的嵌套定义。
(11)保留字段:使用reserved关键字保留字段,防止已删编号被重新使用。
(12)导入定义:若使用其他配置中的类型,用import “配置文件路径”;导入。

syntax = "proto3"; // 决定了 proto 文档的版本号

// 规则1:版本号

// 规则2:注释方式
// 注释方式一
/* 注释方式二 */

// 规则11:导入定义
// 两个配置在同一路径可以这样写,在不同路径要包含文件夹路径
import "test2.proto";

// 规则3:命名空间
package GamePlayerTest; // 这决定了命名空间

// 规则4:消息类
message TestMsg {
    // 规则5:成员类型 和 唯一编号

    // 浮点数
    // = 1 不代表默认值,而是代表唯一编号,方便我们进行序列化和反序列化的处理
    // 规则6:特殊标识
    // required: 必须赋值的字段
    required float testF = 1; // C# - float
    // optional: 可以不赋值的字段
    optional double testD = 2; // C# - double

    // 变长编码
    // 所谓变长,就是会根据数字的大小来使用对应的字节数来存储,1 2 4
    // Protobuf 帮助我们优化的部分,可以尽量少的使用字节数来存储内容
    int32 testInt32 = 3; // C# - int 它不太适用于来表示负数,请使用 sint32
    // 1 2 4 8
    int64 testInt64 = 4; // C# - long 它不太适用于来表示负数,请使用 sint64

    // 更实用与表示负数类型的整数
    sint32 testSInt32 = 5; // C# - int 适用于来表示负数的整数
    sint64 testSInt64 = 6; // C# - long 适用于来表示负数的整数

    // 无符号 变长编码
    // 1 2 4
    uint32 testUInt = 7; // C# - uint 变长的编码
    uint64 testULong = 8; // C# - ulong 变长的编码

    // 固定字节数的类型
    fixed32 testFixed32 = 9; // C# - uint 它通常用来表示大于2的28次方的数,比 uint32 更有效,始终是4个字节
    fixed64 testFixed64 = 10; // C# - ulong 它通常用来表示大于2的56次方的数,比 uint64 更有效,始终是8个字节

    sfixed32 testSFixed32 = 11; // C# - int 始终4个字节
    sfixed64 testSFixed64 = 12; // C# - long 始终8个字节

    // 其它类型
    bool testBool = 13; // C# - bool
    string testStr = 14; // C# - string
    bytes testBytes = 15; // C# - BytesString 字节字符串

    // 数组 List
    repeated int32 listInt = 16; // C# - 类似 List<int> 的使用
    // 字典 Dictionary
    map<int32, string> testMap = 17; // C# - 类似 Dictionary<int, string> 的使用

    // 规则7:枚举
    // 枚举成员变量的声明需要唯一编码
    TestEnum testEnum = 18;

    // 规则8:默认值
    // 声明自定义类对象 需要唯一编码
    // 默认值是 null
    TestMsg2 testMsg2 = 19;

    // 规则9:允许嵌套
    // 嵌套一个类在另一个类当中,相当于是内部类
    message TestMsg3 {
        int32 testInt32 = 1;
    }

    TestMsg3 testMsg3 = 20;

    // 规则9:允许嵌套
    enum TestEnum2 {
        NORMAL = 0; // 第一个常量必须映射到0
        BOSS = 1;
    }

    TestEnum2 testEnum2 = 21;

    // 规则10:保留字段
    // int32 testInt3233333 = 22;

    bool testBool2123123 = 23;

    // 告诉编译器 22 被占用,不准用户使用
    // 之所以有这个功能,是为了在版本不匹配时,反序列化时,不会出现结构不统一,解析错误的问题
    reserved 22;
    reserved testInt3233333;

    // 规则11:导入定义
    GameSystemTest.HeartMsg testHeart = 24;
}

// 规则7:枚举的声明
enum TestEnum {
    NORMAL = 0; // 第一个常量必须映射到0
    BOSS = 5;
}

// 规则8:默认值
message TestMsg2 {
    int32 testInt32 = 1;
}

syntax = "proto3"; // 决定了 proto 文档的版本号
package GameSystemTest; // 这决定了命名空间

message HeartMsg {
    int64 time = 1;
}

Protobuf协议使用

序列化存储为本地文件
主要使用方法:生成的类中的 WriteTo 方法和文件流 FileStream 对象。
示例代码:

TestMsg testMsg1 = new TestMsg();
testMsg1.ListInt.Add(1);
testMsg1.TestBool = false;
testMsg1.TestD = 5.5;
testMsg1.TestInt32 = 99;
testMsg1.TestMap.Add(1, "杨");
testMsg1.TestMsg2 = new TestMsg2();
testMsg1.TestMsg2.TestInt32 = 88;
testMsg1.TestMsg3 = new TestMsg.Types.TestMsg3();
testMsg1.TestMsg3.TestInt32 = 66;

testMsg1.TestHeart = new GameSystemTest.HeartMsg();
testMsg1.TestHeart.Time = 7777;

print(Application.persistentDataPath);
using (FileStream fileStream = File.Create(Application.persistentDataPath + "/TestMsg.tao"))
{
    testMsg1.WriteTo(fileStream);
}

代码解释:先创建 TestMsg 实例并设置其属性值,然后使用 File.Create 方法创建一个文件流,将 TestMsg 实例序列化后写入该文件流,实现数据的本地存储。

反序列化本地文件
主要使用方法:生成的类中的 Parser.ParseFrom 方法和文件流 FileStream 对象。
示例代码:

using (FileStream fileStream = File.OpenRead(Application.persistentDataPath + "/TestMsg.tao"))
{
    TestMsg testMsg2 = null;
    testMsg2 = TestMsg.Parser.ParseFrom(fileStream);
    print(testMsg2.TestMap[1]);
    print(testMsg2.ListInt[0]);
    print(testMsg2.TestD);
    print(testMsg2.TestMsg2.TestInt32);
    print(testMsg2.TestMsg3.TestInt32);
    print(testMsg2.TestHeart.Time);
}

代码解释:使用 File.OpenRead 方法打开之前存储的文件流,然后调用 Parser.ParseFrom 方法从文件流中反序列化出 TestMsg 实例,最后打印该实例的属性值。

得到序列化后的字节数组
主要使用方法:生成的类中的 WriteTo 方法和内存流 MemoryStream 对象。
示例代码:

byte[] bytes = null;
using (MemoryStream memoryStream = new MemoryStream())
{
    testMsg1.WriteTo(memoryStream);
    bytes = memoryStream.ToArray();
    print("字节数组长度" + bytes.Length);
}

代码解释:创建一个内存流,将 TestMsg 实例序列化后写入该内存流,再通过 ToArray 方法将内存流中的数据转换为字节数组,方便进行网络传输。

从字节数组反序列化
主要使用方法:生成的类中的 Parser.ParseFrom 方法和内存流 MemoryStream 对象。
示例代码:

using (MemoryStream memoryStream = new MemoryStream(bytes))
{
    print("内存流当中反序列化的内容");
    TestMsg testMsg2 = TestMsg.Parser.ParseFrom(memoryStream);
    print(testMsg2.TestMap[1]);
    print(testMsg2.ListInt[0]);
    print(testMsg2.TestD);
    print(testMsg2.TestMsg2.TestInt32);
    print(testMsg2.TestMsg3.TestInt32);
    print(testMsg2.TestHeart.Time);
}

代码解释:使用包含字节数组的内存流,调用 Parser.ParseFrom 方法从内存流中反序列化出 TestMsg 实例,最后打印该实例的属性值。

大小端模式

大小端模式的基本概念

模式 高字节存储位置 低字节存储位置 特点
大端模式 内存低地址 内存高地址 符合人类阅读习惯,网络传输标准
小端模式 内存高地址 内存低地址 计算机处理效率高,x86 架构默认

示例:十六进制数据 0x11223344

  • 大端模式存储:11 22 33 44(地址 0→3)
  • 小端模式存储:44 33 22 11(地址 0→3)

大小端模式的影响及转换

影响:网络传输中,前后端语言和设备差异可能导致大小端不统一

  • C# 和 Java/Erlang/AS3 通讯需转换
  • C# 与 C++ 通信通常无需特殊处理

转换方法

操作 方法 示例
判断模式 BitConverter.IsLittleEndian print("是否是小端模式:" + BitConverter.IsLittleEndian);
转为大端 IPAddress.HostToNetworkOrder byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(i));
转为小端 IPAddress.NetworkToHostOrder int receI = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(bytes, 0));
倒序数组 Array.Reverse if (BitConverter.IsLittleEndian) Array.Reverse(bytes);

消息加密解密

加密解密基本概念

名词 含义
明文 待加密的报文(内容)
密文 加密后的报文(内容)
密钥 加密或解密过程中输入的数据
加密算法 将明文和密钥结合生成密文的方法
解密算法 将密文和密钥结合还原明文的方法

加密算法分类

类型 特点 常用算法 适用场景
单向加密 不可逆,生成固定长度值 MD5、SHA1、SHA256 密码存储
对称加密 同一密钥加密解密,速度快 DES、3DES、AES 网络通讯
非对称加密 公钥加密、私钥解密,安全性高 RSA、DSA 支付SDK、高安全场景

异或加密示例

byte key = 55;
// 加密
for (int i = 0; i < bytes.Length; i++)
    bytes[i] ^= key;
// 解密(异或两次还原)
for (int i = 0; i < bytes.Length; i++)
    bytes[i] ^= key;

注意:加密只是提高破解门槛,没有 100% 保密的数据

UnityWebRequest高级操作

DownloadHandler 下载处理类

类名 用途
DownloadHandlerBuffer 下载字节数组
DownloadHandlerFile 下载文件保存到磁盘(内存占用少)
DownloadHandlerTexture 下载图像
DownloadHandlerAssetBundle 下载 AssetBundle
DownloadHandlerAudioClip 下载音频文件

自定义下载处理:继承 DownloadHandlerScript,重写:

  • ReceiveContentLengthHeader:收到 Content-Length 时调用
  • ReceiveData:每帧收到数据时调用
  • CompleteContent:消息接收完成时调用

UploadHandler 上传处理类

类名 用途
UploadHandlerRaw 上传字节数组
UploadHandlerFile 上传文件

重要属性contentType 内容类型,默认 application/octet-stream

自定义协议生成工具

目的

  1. 减少工作量,配置一次自动生成多语言代码
  2. 降低沟通成本,避免前后端协议不统一

制作流程

  1. 确定配置方式(XML/JSON/Excel)
  2. 制定配置规则(枚举、数据类、消息类)
  3. 读取配置信息
  4. 按规则拼接字符串生成代码文件

XML配置示例

<enum name="E_PLAYER_TYPE" namespace="GamePlayer">
    <field name="MAIN">1</field>
    <field name="OTHER"/>
</enum>

<data name="PlayerData" namespace="GamePlayer">
    <field type="int" name="id"/>
    <field type="float" name="atk"/>
</data>

<message id="1001" name="PlayerMessage" namespace="GamePlayer">
    <field type="int" name="playerID"/>
</message>

Protobuf协议工具

Protobuf:谷歌开源的协议生成工具,支持 C++、Java、C#、Python 等多语言

使用流程

  1. 准备 DLL 文件和 protoc 编译器
  2. 编写 .proto 配置文件
  3. 使用 protoc 生成代码文件
  4. 导入工程使用

proto 配置规则

syntax = "proto3";           // 版本号
package GamePlayer;          // 命名空间

enum TestEnum {              // 枚举
    NORMAL = 0;              // 第一个常量必须映射到0
    BOSS = 1;
}

message TestMsg {            // 消息类
    int32 testInt32 = 1;     // 成员变量 + 唯一编号
    repeated int32 list = 2; // 数组
    map<int32, string> dic = 3; // 字典
    TestEnum e = 4;          // 枚举成员
}

Protobuf 序列化/反序列化

// 序列化到文件
using (FileStream fs = File.Create(path))
    testMsg.WriteTo(fs);

// 从文件反序列化
TestMsg msg = TestMsg.Parser.ParseFrom(fs);

// 序列化到字节数组
using (MemoryStream ms = new MemoryStream())
{
    testMsg.WriteTo(ms);
    byte[] bytes = ms.ToArray();
}

// 从字节数组反序列化
TestMsg msg = TestMsg.Parser.ParseFrom(new MemoryStream(bytes));

优势:自动处理大小端问题,跨语言兼容性好


73.3 面试题精选

基础题

1. IP地址和端口的作用是什么?

题目

请简述 IP 地址和端口在网络通信中的作用,以及它们之间的关系。

深入解析

IP 地址和端口是网络通信中定位目标程序的两个关键要素:

IP 地址

  • 网络中设备的逻辑地址,用于在网络中定位一台计算机
  • IPv4 为 32 位地址(如 192.168.1.1),IPv6 为 128 位地址
  • 127.0.0.1 是特殊的本机回环地址

端口

  • 用于标识计算机上具体的网络应用程序
  • 范围 0~65535,其中 0~1023 为系统保留端口
  • 一个 IP 地址可以有 65536 个端口

两者关系

  • IP 地址定位网络中的计算机(找到哪台机器)
  • 端口定位计算机上的具体程序(找到哪个应用)
  • 组合起来(IP:端口)可以唯一确定网络中的一个通信端点
答题示例

IP 地址用于定位网络中的计算机,端口用于标识计算机上的具体应用程序。

两者组合起来可以唯一确定网络中的一个通信端点,就像”小区地址+门牌号”能精确定位一户人家。

参考文章
  • 3.网络理论-IP、端口、Mac地址

进阶题

1. C/S模型和P2P模型有什么区别?游戏开发中如何选择?

题目

请对比 C/S 模型和 P2P 模型的特点,并说明在网络游戏开发中如何选择合适的架构。

深入解析

C/S 模型(Client/Server)

  • 架构特点:客户端发起请求,服务端响应处理,集中式管理
  • 优点:数据安全性高、易于维护和扩展、防作弊能力强
  • 缺点:服务器成本高、单点故障风险
  • 适用场景:大型网络游戏、企业应用

P2P 模型(Peer to Peer)

  • 架构特点:每个节点既是客户端又是服务端,对等通信
  • 优点:无中心服务器、成本低、扩展性好
  • 缺点:数据安全性差、难以防作弊、网络延迟不稳定
  • 适用场景:局域网游戏、文件共享、即时通讯

游戏开发选择建议

  • 网络游戏:优先选择 C/S 模型,便于数据存储、防作弊、玩家匹配
  • 局域网游戏:可考虑 P2P 模型,降低服务器成本
  • 混合架构:部分功能用 P2P(如语音聊天),核心逻辑用 C/S
答题示例

C/S 模型是集中式架构,客户端请求、服务端响应,适合需要数据安全和防作弊的网络游戏;P2P 模型是对等架构,每个节点地位平等,适合局域网游戏。

网络游戏优先选择 C/S 模型,便于玩家数据管理和防作弊;局域网游戏可用 P2P 降低成本。

参考文章
  • 5.网络理论-数据通信模型

深度题

1. 请详细说明TCP三次握手和四次挥手的过程及原因

题目

请详细描述 TCP 三次握手建立连接和四次挥手断开连接的完整过程,并解释为什么需要这样设计。

深入解析

三次握手过程

  1. 第一次握手:客户端发送 SYN 报文(SYN=1,seq=x)给服务端,进入 SYN_SENT 状态
  2. 第二次握手:服务端收到 SYN,回复 SYN+ACK 报文(SYN=1,ACK=1,seq=y,ack=x+1),进入 SYN_RCVD 状态
  3. 第三次握手:客户端收到 SYN+ACK,回复 ACK 报文(ACK=1,seq=x+1,ack=y+1),进入 ESTABLISHED 状态
  4. 服务端收到 ACK 后,进入 ESTABLISHED 状态,连接建立完成

为什么需要三次握手

  • 防止已失效的连接请求报文突然到达服务端,导致服务端错误建立连接
  • 确保双方都能确认对方的接收和发送能力正常
  • 同步双方的初始序列号

四次挥手过程

  1. 第一次挥手:客户端发送 FIN 报文(FIN=1,seq=u),进入 FIN_WAIT_1 状态
  2. 第二次挥手:服务端收到 FIN,回复 ACK(ACK=1,ack=u+1),进入 CLOSE_WAIT 状态;客户端收到后进入 FIN_WAIT_2 状态
  3. 第三次挥手:服务端发送 FIN 报文(FIN=1,seq=w),进入 LAST_ACK 状态
  4. 第四次挥手:客户端收到 FIN,回复 ACK(ACK=1,ack=w+1),进入 TIME_WAIT 状态,等待 2MSL 后关闭;服务端收到 ACK 后关闭

为什么需要四次挥手

  • TCP 是全双工通信,每个方向的连接需要单独关闭
  • 服务端收到 FIN 后可能还有数据要发送,不能立即关闭
  • 确保被动关闭方收到最后的 ACK 确认
答题示例

三次握手:客户端发 SYN → 服务端回 SYN+ACK → 客户端回 ACK。目的是建立可靠连接,同步序列号,防止失效连接请求。

四次挥手:客户端发 FIN → 服务端回 ACK → 服务端发 FIN → 客户端回 ACK。因为是全双工通信,每个方向需单独关闭,服务端可能还有数据要发。

参考文章
  • 9.网络协议-TCP和UDP

2. TCP和UDP有什么区别?游戏开发中如何选择?

题目

请对比 TCP 和 UDP 协议的特点,并说明在网络游戏开发中如何选择使用。

深入解析

TCP 特点

  • 面向连接,需要三次握手建立连接
  • 可靠传输:无差错、不丢失、不重复、有序
  • 有流量控制和拥塞控制机制
  • 只支持一对一通信
  • 传输效率相对较低

UDP 特点

  • 无连接,直接发送数据
  • 不可靠传输:不保证送达、不保证顺序
  • 无流量控制和拥塞控制
  • 支持一对多、多对多通信
  • 传输效率高

游戏开发选择

场景 推荐协议 原因
玩家登录、账号验证 TCP 需要可靠传输
聊天消息、邮件 TCP 不能丢失
实时战斗同步 UDP 低延迟优先
语音通话 UDP 实时性要求高
文件下载 TCP 完整性重要

实际应用:很多游戏采用 TCP + UDP 混合方案,TCP 用于关键数据,UDP 用于实时数据

答题示例

TCP 面向连接、可靠但效率低;UDP 无连接、不可靠但效率高。

游戏开发中,登录验证、聊天等关键数据用 TCP;实时战斗、语音等对延迟敏感的数据用 UDP。实际项目常采用混合方案。

参考文章
  • 9.网络协议-TCP和UDP

3. 什么是分包和黏包?如何解决?

题目

请解释 TCP 通信中分包和黏包现象产生的原因,并说明解决方案。

深入解析

概念

  • 分包:一个消息被拆成多个包发送
  • 黏包:多个消息黏在一起发送
  • 注意:分包和黏包可能同时发生

产生原因

  • TCP 是流式协议,没有消息边界
  • 发送方缓冲区积累多条消息一起发送
  • 接收方缓冲区读取多条消息一起处理

解决方案:在消息头部添加消息长度字段

消息结构

| 消息ID (4字节) | 消息长度 (4字节) | 消息体内容 |

处理逻辑

  1. 维护缓存数组 cacheBytes 和缓存长度 cacheNum
  2. 收到数据后拼接到缓存数组尾部
  3. 循环解析:先判断是否有完整头部(8字节),再根据消息长度判断是否有完整消息体
  4. 完整消息:解析并入队;不完整:保留到下次继续处理
答题示例

分包是一个消息被拆成多个包,黏包是多个消息黏在一起。原因是 TCP 是流式协议没有消息边界。

解决方案是在消息头部添加消息长度字段,接收方维护缓存数组,根据长度判断是否收到完整消息。

参考文章
  • 24.网络通信-套接字Socket-TCP通信-同步-分包黏包-基本实现

4. 心跳消息的作用是什么?如何实现?

题目

请说明心跳消息在网络通信中的作用,并简述实现方案。

深入解析

心跳消息概念:长连接中定期发送的特殊数据包,用于确认对方在线

主要作用

  1. 检测非正常断开的客户端(超时判断)
  2. 保持连接活跃,防止防火墙/路由器断开空闲连接
  3. 及时清理无效连接,释放服务器资源

实现要点

客户端

  • 定时发送心跳消息(如每 2 秒)
  • 使用 InvokeRepeating 或协程实现

服务端

  • 记录每个客户端上次收到消息的时间
  • 定时检测超时的客户端并断开连接
// 服务端超时检测
if (frontTime != -1 && 
    DateTime.Now.Ticks / TimeSpan.TicksPerSecond - frontTime >= TIME_OUT_TIME)
{
    // 超时,断开连接
}

注意事项

  • 心跳间隔不宜过短,避免增加网络负担
  • 超时时间应大于心跳间隔的 2-3 倍
答题示例

心跳消息用于检测连接是否存活,防止防火墙断开空闲连接。客户端定时发送心跳包,服务端记录最后收到消息的时间,超时则断开连接。

参考文章
  • 27.网络通信-套接字Socket-TCP通信-同步-心跳消息-实现

5. HTTP中Get和Post有什么区别?

题目

请对比 HTTP 中 Get 和 Post 请求的区别。

深入解析
特性 Get Post
主要用途 获取数据 上传数据
参数位置 URL 中暴露 请求体中隐藏
数据大小 有限制(约 2048 字符) 无限制
安全性 较低(参数可见) 较高(参数隐藏)
缓存 可被浏览器缓存 不可缓存
传输次数 一次传输 可能分两次传输

使用建议

  • Get 用于获取资源,如查询数据、下载文件
  • Post 用于提交数据,如上传文件、表单提交
  • 敏感信息必须使用 Post
答题示例

Get 用于获取数据,参数在 URL 中暴露,有大小限制;Post 用于上传数据,参数在请求体中隐藏,无大小限制。敏感信息应使用 Post。

参考文章
  • 50.网络通信-超文本传输HTTP-Post学前准备

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏