文字游戏的长连接:Telnet 选项协商与状态同步浅析
MUD 客户端与服务器之间默认走 Telnet 协议,底层是一条 TCP 长连接,一个连接对应一个玩家会话。理解这条连接上发生了什么,是排查「玩家掉线」「画面乱码」类问题的基础。
IAC 选项协商
连接建立后,双方会通过 IAC 报文交换 DO / WILL / DONT / WONT,协商各自支持的能力。实践中常见的有几类:
- TTYPE:客户端上报终端类型,驱动据此决定是否发送 ANSI 颜色码。
- NAWS:协商窗口大小,驱动按行列数分页输出,避免长列表刷屏。
- GMCP:通用 MUD 通信协议,在 Telnet 之上传输结构化数据,血量、地图、房间信息都可以走它。
TCP 是字节流,必须自行按行切分
客户端一次发来的数据不一定是一条完整命令,也可能几条挤在一个包里。服务端的标准做法是维护输入缓冲,遇到换行符才切出一条命令交给命令解析器。
// 简化的按行切分逻辑
void net_read(string data) {
input_buffer += data;
while (strsrch(input_buffer, "\n") != -1) {
string line = input_buffer[0..strsrch(input_buffer, "\n") - 1];
input_buffer = input_buffer[strsrch(input_buffer, "\n") + 1..];
process_command(trim(line));
}
}
状态同步靠事件广播
文字游戏没有帧同步的概念,世界的一切变化都以事件广播体现:移动、look、战斗消息、环境描述。工程上要控制两件事——广播范围(同房间 / 同区域 / 全服)和广播频率。全服广播必须限流,否则一条世界公告就能把所有人踢下线。
断线与重连
为每个连接设置心跳超时,超时未收到数据的连接主动关闭。玩家断线后不要立即销毁角色对象,保留一段 linkdeath 时间,重连时恢复原会话——这对文字游戏的体验至关重要。
输入安全
对单条命令长度、频率做限制;GMCP 等子协议数据同样需要校验,构造异常报文可能影响驱动稳定性,这类攻击在公网 MUD 上真实存在。