本文主要调研游戏网络加速,网络代理的技术方案和实现,可以用于解决网速低、网络信号差等带来的卡顿、丢帧等体验问题
一、整体方案
- 复杂繁多的运营商:对于没有建立互联的运营商,需要先经数据中心迂回再到上车点
- 代理服务器:上车点目前不承担认证协商能力,需要部署代理服务器与 App 之间进行认证协商/加解密,同时为加快代理服务器到上车点的传输,可以部署到同个 IDC
- GAAP 专线:使用部署的私有 VPC 在内网传输
- 游戏目标服务器:下车点进行 DNAT 转换,为避免伪造攻击,源 IP 需多个
二、Android VPN 加速方案
- 从本地接口的文件描述符(fd)读取传出的 IP 数据包,进行加密并通过 VPN 网关发送到代理服务器,代理服务器寻找加速节点,经过加速专线到达游戏服务器
- 将传入的数据包(从 VPN 网关接收并解密)写入本地接口的文件描述符(fd)。
- 5.0 及以上按应用拦截,5.0 以下按目标 IP 地址拦截,(0.0.0.0) 拦截所有流量
方案一:包裹 IP 数据包以 UDP/TCP 数据包转发
通过 UDP/TCP 包裹 IP 数据包转发到代理服务器,代理服务器脱去 UDP/TCP 头获取拦截的 IP 数据包,再进行 NAT IP 地址转换,转发到目标服务器的加速节点。
- TCP/UDP 对 IP 包进行封装,代理服务器再将该 IP 包发到网络协议栈中
优点:
- 终端只需要透传 IP 报文包,无需关心内部状态
缺点:
- 传输的包大小增大,多了 udp/tcp 和 ip 的头部
方案二:解析 IP 数据包以 SOCKS 代理协议转发
使用 tun2socks 将 IP 数据包解析成原始数据,再用 SOCKS 协议封装进行转发
- 解析了 IP 数据包后,需要维护 TCP/IP 的协议栈交互
- 使用 shadowsocks/socks5 等方式协商认证/加密传输到代理服务器
- 代理服务器转发到加速节点上
优点:
- 数据包体积较小,没有多余的 ip 和 tcp/udp 头部
缺点:
- 需要维护与 tun0 的 TCP/IP 协议栈交互
- lwip 会进行魔改,维护成本高
tun2socks 方案
1)与Android VpnService fd 交互
- IP 读取:fd_handler read fd 后在 device_read_handler_send 中进行分发,tcp 丢给 lwip 的 ip_input 处理,udp 自己处理 process_device_udp_packet
- IP 写回:BTap_Send write fd ,其中 tcp 通过 netif_output_fn 走 lwip 输出,udp 通过 udpgw_client_handler_received 输出
2)TCP 交互
TCP 读取:
- 将读取到的 IP 数据包写入 pbuf ,作为 lwip 的 ip_input 输入
- 监听接入 tcp 的数据报文,进行 socks 封装,通过 BSocksClient 进行发送
TCP 写回:
- 在 BSocksClient 收到数据解析后,将 tcp 数据通过 tcp_write 进入 lwip
- lwip 的 tcp 数据经过 ip 报文包装,在 output_fn 中将数据通过 BTap_Send 写回 fd 中。
3)UDP 交互
- UDP 读取:收到 ip 数据报文后,若是 udp 的报文,则解析数据后,用 socks 封装通过 SocksUdpGwClient 发送
- UDP 写回:监听 udp 的数据报文,对 socks 进行解析,再封装 ip 报文通过 BTap_Send 写回 fd 中。
Socks5 协议封装
- 认证方法协商
- 身份信息认证
- 发起请求携带转发的目标地址和端口
缺点:
- SOCKS5 连接需要三次握手,有连接延迟
- SOCKS5 协议只负责传输,不负责加密,如 Http 协议通过 SOCKS5 代理的流量将全是明文,需要用其他方式如 TLS 等来保证的安全性
方案三:解析 IP 数据包以 shadowsocks 协议转发
- 建立 SOCKS Server,将数据转发到本地建的 shadowsocks local
- shadowsocks local 对数据进行加密,先通知目标服务器的地址信息,再传输密文和随机数 IV
- shadowsocks server 对数据进行解密,再进行转发
优点:
- 数据密文传输,有较强隐蔽性
缺点:
- 没有协商认证过程,加密方式和加密的用户密码是事先定义好的
shadowsocks 协议加密
- 传输目标地址和端口
- 使用 AES-256 等算法根据用户密码生成密钥,再根据密钥+随机数IV生成的密文和 IV 进行传输
方案四:解析 IP 数据包直接转发(腾讯云GAAP方案)
- 客户端获取单个 App 的接入点 VIP 和端口,并将数据包转发
- 接入点进行 NAT 源地址和目标地址转换,目标地址根据 VIP 匹配控制台配置的目标 IP 和端口,客户端源 IP 和端口 TOA 携带
优点:
- 无需数据包的编解码,速度快
- 不携带游戏服务器信息
缺点:
- 不能通用加速,只能配置 VIP 和目标 IP 信息的基础上才能加速
- 没有认证方式,VIP 和端口容易被攻击
三、报文分析
1、TCP/UDP
IP 报文
- version:IP协议版本,4bit,0100(IPv4),0110(IPv6)
- header length:IP包头长度,4bit,包长度可变
- TOS(Type of Service):服务类型,8bit,3bit用来表示该数据包的优先级, 4bit表示可选的服务类型(最小延迟, 最大吞吐, 最大可靠性, 最低成本),还有1bit保留为0;但未被广泛使用,在RFC2474中 这 8 bit 被重新定义
- DSCP(DiffServ):区分服务字段,6bit,相比之前3bit 8 类,增长到最多定义 64 个不同的服务类型
- ECN(Explicit CongestionNotification):显示拥塞通知,2bit,00:不支持,01/10:支持,11:发生拥塞
- totoal length:总长度,16bit,IP包最大长度65535字节
- identification:标识符,16bit,配合 flags、fragment offset 使用,当数据包分包后,所有拆开的小包都被标记相同的值,以便目的端区分
- flags:标记,3bit
- 第一位是保留位,未使用为0
- 第二位是DF(Don’t Fragment)位,DF位设为1时表明路由器不能对该数据包分包。如果该数据包无法在不分段的情况下发送,则路由器会丢弃该数据包并返回一个错误信息
- 第三位是MF(More Fragments)位,当路由器对一个上层数据包分段,则路由器会在除了最后一个分段的IP包的包头中将MF位设为1
- fragment offset:片偏移,13bit,表示该IP包在该组分片包中位置,接收端靠此来组装还原IP包。
- TTL:生存时间,8bit,一个数据包在网络上的最多经过多少次转发, 如果超过该数字, 就丢弃该包,当IP包在网络上传送时,每经过一个路由器,TTL就自动减一。值为0时,则丢弃报文。防止报文进入环路
- protocol:报文协议类型,8bit, 上层可选协议为: TCP, UDP, ICMP, IGMP
- header checksum:头校验和,16bit,IP 头部的正确性检测,不包含数据部分。路由器会改变 TTL,所以路由器会为每个通过的数据包重新计算这个值
- source address:源地址,32bit
- destination address:目的地址,32bit
- optional:可选,变长
TCP 报文
面向连接的、可靠的、字节流传输协议
- 可靠性:需要超时、重传、窗口管理、流量控制、拥塞控制等功能来解决比特错误、包乱序、包重复、丢包等传输错误
- 字节流:需要用 TCP 窗口管理、拥塞控制等功能来对应用层发送的数据进行统一分片或打包发送
- 源端口:16bit,发送应用程序对应的端口,端口在0~65535之间,在收到服务请求时,操作系统动态地为客户端的应用程序分配端口号
- 目的端口:16bit,报文接收计算机上的应用程序地址端口
- 发送序列号:32bit,TCP 报文中第一个数据字节,标识从TCP源端向TCP目标端发送的数据字节流;当建立一个新的连接时,SYN变为1,此字段包含连接的初始顺序号 ISN
- 确认序列号:32bit,只有ACK为1时有效,它包含目标端所期望收到源端的下一个数据字节
- 头部长度:4bit,TCP 头部长度,最大为15/32bit,TCP头部长度默认20字节,最大60字节
- 保留:4bit,000
- 标识位:8bit
- CWR:拥塞窗口减少标识被发送主机的设置,表明它接收到了设置 ECE 标识的TCP包,发送端通过降低发送窗口大小来降低发送速率
- ECE:ECN响应标志,被用来表明 TCP 3次握手时表明一个 TCP 端是具备 ECN 功能的,并表明接收到的 TCP 包的 IP 头部的 ECN 被设置为 11
- URG:紧急指针有效
- ACK:确认序号有效
- PSH:接收方应该尽快将这个报文段交给应用层
- RST:重建连接
- SYN:发起一个连接
- FIN:释放一个连接
- 窗口:16bit,流量控制,本机期望一次接收的字节数
- 校验和:16bit,对整个 TCP 报文段(TCP 头部和TCP 数据进行校验和计算),并由目标端进行验证,对于大的数据包,不能可靠的反应比特错误,应用层需要添加自己的校验方式
- 紧急指针:16bit,指向后面是优先数据的字节,若没有被设置,则作为填充
- 选项,长度不变,但必须是 32bit 的整数倍
UDP 报文
无连接,数据包套接字,不保证到达性
- 源端口:16bit,
- 目的端口:16bit
- 长度:16bit
- 校验:16bit
2、LWIP TCP/IP
LWIP 是一个轻量型的 TCP/IP 协议栈,我们可以在拦截 Android IP 报文包后,将其解析成 TCP 报文桥接到外部使用 Socks 协议发出:
拦截 IP 报文后,输出 tcp_input
,我们可以在 tcp_input
中将协议转发到外部。
当收到外部消息后,使用代理 Socks 协议解析成裸数据,然后使用 LWIP tcp_write
写回,并封装成 IP 报文回给 Android VPN。
四、Demo 示例
我们使用 shadowsocks 在服务端部署,并在客户端进行 VPN 代理转发。
服务部署
1)shadowsocks 安装
CentOS:
1 | yum install python-setuptools && easy_install pip pip install shadowsocks |
如腾讯云上区分 python2或python3,需要这样:
1 | pip2 install shadowsocks |
2)shadowsocks server 启动
新建一个 shadowsocks.json 配置:
1 | { |
- server:0.0.0.0,表示本网络中的本机IPV4地址,如果写的其他 IP 会有 socket.error: [Errno 99] Cannot assign requested address 错误
- server_port:服务端口需要加安全组
启动运行 shadowsocks server
1 | 运行在前台 |
本地连接
下载个 shadowsocks app 设置已经部署的 IP:Port 进行连接: