文章目录
- 1 IP/端口类
- 1.1 IPAddress
- 1.2 IPEndPoint
- 2 域名解析
- 2.1 IPHostEntry
- 2.2 Dns
- 3 序列化与反序列化
- 3.1 序列化
- 3.1.1 内置类型 -> 字节数组
- 3.1.2 字符串 -> 字节数组
- 3.1.3 类对象 -> 字节数组
- 3.2 反序列化
- 3.2.1 字节数组 -> 内置类型
- 3.2.2 字节数组 -> 字符串
- 3.2.3 字节数组 -> 类对象
1 IP/端口类
要进行网络通信,首先需要找到对应设备,IP和端口号是定位网络中设备必不可少的关键元素。
C# 中提供了对应的 IP 和端口相关的类来声明对应信息。
1.1 IPAddress
- 命名空间:
System.Net
- 类名:
IPAddress
// 1.用 byte 数组进行初始化
byte[] ipAddress = new byte[] { 118, 102, 111, 11 };
IPAddress ip1 = new IPAddress(ipAddress);
// 2.用 long 长整型进行初始化
// 4 字节对应的长整型,一般不建议使用
// 76, 66, 6F, 0B 对应 10 进制下的 118, 102, 111, 11
IPAddress ip2 = new IPAddress(0x76666F0B);
// 3.推荐使用的方式,使用字符串转换
IPAddress ip3 = IPAddress.Parse("118.102.111.11");
**特殊 IP 地址:**127.0.0.1 代表本机地址。
1.2 IPEndPoint
IPEndPoint 类将网络端点表示为 IP 地址和端口号,是 IP 地址和端口号的组合。
- 命名空间:
System.Net
- 类名:
IPEndPoint
// 1.用 4 子节长整型 ip 与端口号初始化
IPEndPoint ipPoint = new IPEndPoint(0x79666F0B, 8080);
// 2.用 IPAddress 与端口号初始化,推荐使用
IPEndPoint ipPoint2 = new IPEndPoint(IPAddress.Parse("118.102.111.11"), 8080);
2 域名解析
IP 地址记忆困难,为了方便记忆,采用域名来代替 IP 地址标识站点地址。比如登录百度网页时,通过域名 “www.baidu.com”进行登录,而不是记录复杂的 IP 地址。
域名解析即域名到 IP 地址的转换过程,也称域名指向、服务器设置、域名配置以及反向 IP 登记等。域名解析工作由 DNS(Domain Name System)服务器完成。
域名系统(Domain Name System,DNS)是互联网的一项服务,是将域名和 IP 地址相互映射的分布式数据库,能够更方便地访问互联网。
2.1 IPHostEntry
该类不会主动声明,而是作为某些方法的返回值返回信息。
主要通过该类对象获取返回的信息。
- 命名空间:
System.Net
- 类名:
IPHostEntry
- 主要作用:域名解析后的返回值 可以通过该对象获取 IP 地址、主机名等信息。
namespace System.Net
{
public class IPHostEntry
{
public IPAddress[] AddressList { get; set; } // 关联 IP
public string[] Aliases { get; set; } // 主机别名列表
public string HostName { get; set; } // DNS 名称
}
}
2.2 Dns
主要作用:提供静态方法,根据域名获取 IP 地址。
- 命名空间:
System.Net
- 类名:
Dns
同步加载
using System.Net;
public class Lesson2 : MonoBehaviour
{
private void Start()
{
// 1.获取本地系统的主机名
print(Dns.GetHostName());
// 2.同步获取指定 IP 地址的 DNS 信息
var entry = Dns.GetHostEntry("www.baidu.com"); // 网页端口号默认为 80
foreach (var ip in entry.AddressList)
{
print("ip: " + ip);
}
foreach (var alias in entry.Aliases)
{
print("alias: " + alias);
}
print("DNS 服务器名称: " + entry.HostName);
}
}

异步加载
using System.Net;
public class Lesson2 : MonoBehaviour
{
private void Start()
{
// 1.获取本地系统的主机名
print(Dns.GetHostName());
// 2.异步获取指定 IP 地址的 DNS 信息
GetHostEntryAsync();
}
private async void GetHostEntryAsync()
{
var entry = await Dns.GetHostEntryAsync("www.baidu.com");
foreach (var ip in entry.AddressList)
{
print("ip: " + ip);
}
foreach (var alias in entry.Aliases)
{
print("alias: " + alias);
}
print("DNS 服务器名称: " + entry.HostName);
}
}
3 序列化与反序列化
在网络通信中,需要将传递的类对象信息序列化为 2 进制数据(商业游戏中,一般为 byte 字节数组),再将该 2 进制数据通过网络传输给远端设备,远端设备获取到该 2 进制数据后再将其反序列化为对应的类对象。

-
序列化:将类对象信息转换为可保存或传输的格式的过程。
-
反序列化:与序列化相反,将保存或传输过来的格式转换为类对象的过程。
网络通信中常用内容:
BitConverter
:主要用于处理各类型和字节数组间的相互转换。Encoding
:主要用于处理字符串类型和字节数组间的相互转换。File
:文件操作类,用于操作文件。FileStream
:文件流类,以流的形式进行文件存储读取操作。MemoryStrem
:内存流对象。- 加密:了解 2 进制数据加密的常用手段和思路。
BinaryFormatter 类可以将 C# 类对象快速转换为字节数组数据。
在网络开发时,不会使用 BinaryFormatter 进行数据序列化和反序列化。因为客户端和服务端的开发语言多数情况下不同,BinaryFormatter 序列化的数据无法兼容其它语言。
若保证开发语言均为 C#,可以使用 BinaryFormatter,参考链接:2023-05-27 Unity 2进制4——类对象的序列化与反序列化_unity 二进制序列化-CSDN博客。
3.1 序列化
3.1.1 内置类型 -> 字节数组
- 关键类:
BitConverter
- 所在命名空间:
System
- 主要作用:除字符串的其它常用类型和字节数组相互转换。
using System;
using System.Text;
public class Lesson3 : MonoBehaviour
{
private void Start()
{
var bytes = BitConverter.GetBytes(1); // 将 1 转为 byte 数组
}
}
3.1.2 字符串 -> 字节数组
- 关键类:
Encoding
- 命名空间:
System.Text
- 主要作用:将字符串类型和字节数组相互转换,并且决定转换时使用的字符编码类型。网络通信时建议使用 UTF-8 类型。
using System;
using System.Text;
public class Lesson3 : MonoBehaviour
{
private void Start()
{
var bytes2 = Encoding.UTF8.GetBytes("hello world"); // 将 "hello world" 转为 byte 数组
}
}
3.1.3 类对象 -> 字节数组
以如下自定义类为例:
public class PlayerInfo
{
public int Lev;
public string Name;
public int Exp;
public bool Sex;
}
-
明确字节数组的容量
对于 string 类型,除了存储字符串内容,还需要先存储其长度。
否则,反序列化时无法确定后续成员的索引位置。
var bytesLength = sizeof(int) + // Lev:int 类型,占用 4 个字节 sizeof(int) + // Name Length:int 类型,占用 4 个字节 Encoding.UTF8.GetBytes(Name).Length + // Name:string 类型,占用 Name.Length 个字节 sizeof(int) + // Exp:int 类型,占用 4 个字节 sizeof(bool); // Sex:bool 类型,占用 1 个字节
-
申明装载信息的字节数组容器
var playerBytes = new byte[bytesLength];
-
将对象信息转为字节数组,放入该容器中
CopyTo 方法的第二个参数代表从容器的第几个位置开始存储。
var offset = 0; // Lev BitConverter.GetBytes(Lev).CopyTo(playerBytes, offset); offset += sizeof(int); // Name var strBytes = Encoding.UTF8.GetBytes(Name); BitConverter.GetBytes(strBytes.Length).CopyTo(playerBytes, offset); // 存储长度 offset += sizeof(int); strBytes.CopyTo(playerBytes, offset); // 存储内容 offset += strBytes.Length; // Exp BitConverter.GetBytes(Exp).CopyTo(playerBytes, offset); offset += sizeof(int); // Sex BitConverter.GetBytes(Sex).CopyTo(playerBytes, offset); offset += sizeof(bool);
完整代码:
public class PlayerInfo
{
public int Lev;
public string Name;
public int Exp;
public bool Sex;
public byte[] GetBytes()
{
var bytesLength = sizeof(int) + // Lev:int 类型,占用 4 个字节
sizeof(int) + // Name Length:int 类型,占用 4 个字节
Encoding.UTF8.GetBytes(Name).Length + // Name:string 类型,占用 Name.Length 个字节
sizeof(int) + // Exp:int 类型,占用 4 个字节
sizeof(bool); // Sex:bool 类型,占用 1 个字节
var playerBytes = new byte[bytesLength];
var offset = 0;
// Lev
BitConverter.GetBytes(Lev).CopyTo(playerBytes, offset);
offset += sizeof(int);
// Name
var strBytes = Encoding.UTF8.GetBytes(Name);
BitConverter.GetBytes(strBytes.Length).CopyTo(playerBytes, offset); // 存储长度
offset += sizeof(int);
strBytes.CopyTo(playerBytes, offset); // 存储内容
offset += strBytes.Length;
// Exp
BitConverter.GetBytes(Exp).CopyTo(playerBytes, offset);
offset += sizeof(int);
// Sex
BitConverter.GetBytes(Sex).CopyTo(playerBytes, offset);
offset += sizeof(bool);
return playerBytes;
}
}
3.2 反序列化
3.2.1 字节数组 -> 内置类型
- 关键类:
BitConverter
- 所在命名空间:
System
- 主要作用:除字符串的其它常用类型和字节数组相互转换。
byte[] bytes = BitConverter.GetBytes(99);
int i = BitConverter.ToInt32(bytes, 0);
print(i); // 99
3.2.2 字节数组 -> 字符串
- 关键类:
Encoding
- 命名空间:
System.Text
- 主要作用:将字符串类型和字节数组相互转换,并且决定转换时使用的字符编码类型。网络通信时建议使用 UTF-8 类型。
byte[] bytes2 = Encoding.UTF8.GetBytes("hello world!");
string str = Encoding.UTF8.GetString(bytes2, 0, bytes2.Length);
print(str); // hello world!
3.2.3 字节数组 -> 类对象
-
获取到对应的字节数组
PlayerInfo info = new PlayerInfo { Lev = 10, Name = "zheliku", Exp = 88, Sex = false }; byte[] playerBytes = info.GetBytes();
-
将字节数组按照序列化时的顺序进行反序列化
将对应字节分组转换为对应类型变量。
PlayerInfo info2 = new PlayerInfo(); // 等级 int index = 0; info2.Lev = BitConverter.ToInt32(playerBytes, index); index += 4; print(info2.Lev); // 10 // 姓名的长度 int length = BitConverter.ToInt32(playerBytes, index); index += 4; // 姓名字符串 info2.Name = Encoding.UTF8.GetString(playerBytes, index, length); index += length; print(info2.Name); // zheliku // 攻击力 info2.Exp = BitConverter.ToInt16(playerBytes, index); index += 2; print(info2.Exp); // 88 // 性别 info2.Sex = BitConverter.ToBoolean(playerBytes, index); index += 1; print(info2.Sex); // false
完整代码:
public class PlayerInfo
{
public int Lev;
public string Name;
public int Exp;
public bool Sex;
public byte[] GetBytes()
{
...
}
public void FromBytes(byte[] bytes)
{
// 等级
int index = 0;
Lev = BitConverter.ToInt32(bytes, index);
index += 4;
// 姓名的长度
int length = BitConverter.ToInt32(bytes, index);
index += 4;
// 姓名字符串
Name = Encoding.UTF8.GetString(bytes, index, length);
index += length;
// 攻击力
Exp = BitConverter.ToInt16(bytes, index);
index += 2;
// 性别
Sex = BitConverter.ToBoolean(bytes, index);
index += 1;
}
}
更多序列化进阶内容,参考文章:2025-03-21 Unity 序列化 —— 自定义2进制序列化-CSDN博客。