第1章 网络编程基础

从 MMORPG 客户端/服务器职责、OSI 与 TCP/IP,到 Socket 生命周期、字节序、TCP 字节流和 UDP 数据报,重建可验证的通信地基。

从第1章的核心问题开始

网络程序的第一条纪律是区分传输事实与业务消息。TCP 只提供有序字节流,不提供消息边界;UDP 保留数据报边界,却不保证到达和顺序。服务器必须先定义权威职责、编码规则和连接生命周期,再选择传输并实现组帧。

官方目录命中与来源边界

本页明确覆盖:网络游戏服务器端开发概述、网络游戏类型、MMORPG服务器与客户端功能划分、网络通信协议、网络协议、OSI通信协议模型、TCP/IP协议、TCP/IP协议族、Socket编程原理、套接字、Socket通信流程、Socket函数、IP地址转换、字节转换、基本Socket通信、TCP/IP通信实现、UDP/IP通信实现。这些名称来自 2007 年版公开书目。公开目录没有披露的细分内容会标为教学展开;现代 API、安全要求和部署方法用于帮助今天的读者验证原机制,不代表原书在 2007 年已经使用这些实现。

#机制或证据边界可观察解释
1职责先于接口MMORPG 客户端负责输入与表现,服务器负责身份、规则、共享世界和持久结果。客户端发来的是请求,不能直接成为权威状态。
2分层用于定位OSI 模型帮助判断故障属于链路、网络、传输还是应用语义;TCP/IP 协议族把 IP 寻址、TCP 可靠流、UDP 数据报等能力组合起来。
3Socket 生命周期服务端依次创建套接字、绑定地址、监听、接受连接、收发并关闭;每一步都可能部分成功,错误路径必须释放唯一所有者持有的资源。
4表示必须统一IP 文本与二进制地址、主机字节序与网络字节序、定长头与变长体都要有单一转换边界,不能把本机结构体直接发送到网络。
5传输语义不同TCP 接收可能得到半包或多包,必须按长度循环组帧;UDP 一次接收对应一个数据报,但需要序号、丢包策略和最大报文限制。

核心概念与因果关系

、、、、。这些概念不能只停留在定义上:每个概念都要落到输入、内部状态、输出和失败证据。读者应能指出哪个模块拥有状态、哪个边界拒绝非法输入、哪个指标证明系统没有把成本推给下游。

职责先于接口

MMORPG 客户端负责输入与表现,服务器负责身份、规则、共享世界和持久结果。客户端发来的是请求,不能直接成为权威状态。

验证时固定软件版本、配置和输入规模,先保存正常轨迹,再只改变一个边界条件。最终结果相同并不代表过程正确;必须比较阶段状态、队列水位、错误分类和恢复动作。

分层用于定位

OSI 模型帮助判断故障属于链路、网络、传输还是应用语义;TCP/IP 协议族把 IP 寻址、TCP 可靠流、UDP 数据报等能力组合起来。

验证时固定软件版本、配置和输入规模,先保存正常轨迹,再只改变一个边界条件。最终结果相同并不代表过程正确;必须比较阶段状态、队列水位、错误分类和恢复动作。

Socket 生命周期

服务端依次创建套接字、绑定地址、监听、接受连接、收发并关闭;每一步都可能部分成功,错误路径必须释放唯一所有者持有的资源。

验证时固定软件版本、配置和输入规模,先保存正常轨迹,再只改变一个边界条件。最终结果相同并不代表过程正确;必须比较阶段状态、队列水位、错误分类和恢复动作。

表示必须统一

IP 文本与二进制地址、主机字节序与网络字节序、定长头与变长体都要有单一转换边界,不能把本机结构体直接发送到网络。

验证时固定软件版本、配置和输入规模,先保存正常轨迹,再只改变一个边界条件。最终结果相同并不代表过程正确;必须比较阶段状态、队列水位、错误分类和恢复动作。

传输语义不同

TCP 接收可能得到半包或多包,必须按长度循环组帧;UDP 一次接收对应一个数据报,但需要序号、丢包策略和最大报文限制。

验证时固定软件版本、配置和输入规模,先保存正常轨迹,再只改变一个边界条件。最终结果相同并不代表过程正确;必须比较阶段状态、队列水位、错误分类和恢复动作。

六阶段证据链

可复现实验

写一个最小回显服务器和测试客户端。先预测一次 send 是否必然对应一次 recv,再分别注入 1 字节分片、两个帧合并、客户端中途断开和 UDP 序号跳变;记录解析器缓冲区、期望长度和第一处拒绝原因。

最小实现

struct FrameHeader { std::uint32_t bodyBytes; std::uint16_t type; };
 
bool decodeHeader(std::span<const std::byte> raw, FrameHeader& out) {
  if (raw.size() < 6) return false;
  out.bodyBytes = readNetworkU32(raw.subspan(0, 4));
  out.type = readNetworkU16(raw.subspan(4, 2));
  return out.bodyBytes <= 64 * 1024;
}

边界处理

bool readFrame(Socket& socket, Buffer& pending, Message& out) {
  while (!pending.hasCompleteFrame()) {
    const auto n = socket.receive(pending.writeSpan());
    if (n == 0) return false;
    pending.commit(n);
  }
  return pending.popFrame(out);
}

证据契约

struct Datagram { std::uint32_t session; std::uint32_t sequence; Bytes body; };
 
Decision accept(const Datagram& packet, ReplayWindow& window) {
  if (packet.body.size() > 1200) return Decision::RejectOversize;
  if (!window.markIfNew(packet.sequence)) return Decision::RejectReplay;
  return Decision::Dispatch;
}

实现代码只是实验的一部分。每次运行还要记录构建版本、配置摘要、样本类型、输入规模、首偏离节点、恢复动作和最终决策。若无法用相同输入重放,结论不能进入交付清单。

两个必须主动制造的失败

验收矩阵

维度正常样本边界样本失败样本通过条件
输入合法且固定容量、顺序或时序临界值畸形、重复或中断分类清晰且可重放
状态按六阶段推进达到预算但不越界在首偏离节点停止无隐式跨层写入
输出结果和证据一致退化可解释拒绝、补偿或回滚不变量恢复
交接他人按记录复验干净环境复验删除隐含依赖结论仍成立

本页签发不变量是:任何分片组合都只能产生完整且有界的消息;无效长度、失效地址、重复数据报和断开连接不会污染下一条会话。 任何演示、截图或单次成功都不能替代它。

练习

小结

  • 职责先于接口:MMORPG 客户端负责输入与表现,服务器负责身份、规则、共享世界和持久结果。客户端发来的是请求,不能直接成为权威状态。
  • 分层用于定位:OSI 模型帮助判断故障属于链路、网络、传输还是应用语义;TCP/IP 协议族把 IP 寻址、TCP 可靠流、UDP 数据报等能力组合起来。
  • Socket 生命周期:服务端依次创建套接字、绑定地址、监听、接受连接、收发并关闭;每一步都可能部分成功,错误路径必须释放唯一所有者持有的资源。
  • 表示必须统一:IP 文本与二进制地址、主机字节序与网络字节序、定长头与变长体都要有单一转换边界,不能把本机结构体直接发送到网络。
  • 已用正常、边界和失败样本验证“任何分片组合都只能产生完整且有界的消息;无效长度、失效地址、重复数据报和断开连接不会污染下一条会话。”。
  • 已把公开目录、历史语境和现代教学展开分开记录。

术语表

来源与改编边界

讨论

评论区加载中…