Module: Mnet
- Defined in:
- lib/mnet.rb,
lib/mnet/version.rb
Defined Under Namespace
Modules: SessionIO Classes: Endpoint, Frame, KcpSession, Packet, Server, Session
Constant Summary collapse
- MAGIC =
"\x12\xE9\x97".b
- MAGIC_LEN =
3- SESSION_ID_LEN =
16- IP_MTU =
Conservative MTU for mobile networks / VPN tunnels (mosh uses 1280).
1280- DEFAULT_MSS =
IP(20) + UDP(8) + our header + GCM tag(16) leaves ~1190 for a segment.
1200- GCM_OVERHEAD =
AES-256-GCM 每包的额外开销:12 字节 nonce + 16 字节认证标签。
28- MIN_RTO =
RTO bounds (mosh: MIN_RTO = 50ms, MAX_RTO = 1000ms).
0.05- MAX_RTO =
1.0- MAX_OLD_SOCKET_AGE =
How long to keep a superseded (hopped-away-from) socket alive to catch delayed packets (mosh: MAX_OLD_SOCKET_AGE = 60000ms).
60.0- MSG_DONTWAIT =
非阻塞发送标志:POSIX 有 MSG_DONTWAIT,Windows 没有(用 nonblock 模式兜底)。
Socket.const_defined?(:MSG_DONTWAIT) ? Socket::MSG_DONTWAIT : 0
- TYPE_SYN =
Message types (mirrors the enum-message-types pattern from tmux-protocol.h).
0x01- TYPE_SYNACK =
0x02- TYPE_DATA =
0x03- TYPE_ACK =
0x04- TYPE_FIN =
0x05- TYPE_PING =
0x06- TYPE_PONG =
0x07- FLAG_NONE =
0x00- FLAG_MIGRATE =
0x01- FLAG_KCP =
session uses the KCP (C) engine instead of the Ruby ARQ
0x02- KCP_WND_RCV =
KCP 接收窗口(ikcp.c 内 IKCP_WND_RCV,固定 128)。单次 ikcp_send 超过 窗口大小的分段数会返回 -2 丢弃,故 KcpSession#write 按此切块。
128- HEADER_LEN =
MAGIC_LEN + SESSION_ID_LEN + 8 + 8 + 1 + 1 + 4
- MAX_RETRIES =
10- TYPES =
{ TYPE_SYN => "SYN", TYPE_SYNACK => "SYNACK", TYPE_DATA => "DATA", TYPE_ACK => "ACK", TYPE_FIN => "FIN", TYPE_PING => "PING", TYPE_PONG => "PONG" }.freeze
- VERSION =
'0.1.6'
Class Method Summary collapse
- .addr_pair(addr) ⇒ Object
- .fmt(addr) ⇒ Object
-
.kcp_mtu(key = nil) ⇒ Object
KCP 引擎的 MTU。每个 KCP 包会被整体包进 Mnet 头(HEADER_LEN)再作为单个 UDP 数据报发送,加密时还要再叠加 GCM 开销,因此 KCP 自身的 MTU 必须为这些 开销留出空间,保证整包 <= IP_MTU,避免 IP 分片(移动网络/VPN 下分片极易丢)。.
- .now ⇒ Object
- .pack(session_id, seq, ack, type, flags, window, payload = "".b) ⇒ Object
-
.session_id_from_key(key) ⇒ Object
Session id is derived from the shared session key (mosh-style), so the id identifies the key and vice versa; without a key it is just random.
-
.socket_pair ⇒ Object
A connected stream socket pair, portable across Windows/Linux/macOS.
- .type_name(type) ⇒ Object
- .unpack(data) ⇒ Object
Class Method Details
.addr_pair(addr) ⇒ Object
100 101 102 |
# File 'lib/mnet.rb', line 100 def addr_pair(addr) [addr[3], addr[1]] end |
.fmt(addr) ⇒ Object
104 105 106 |
# File 'lib/mnet.rb', line 104 def fmt(addr) "#{addr[0]}:#{addr[1]}" end |
.kcp_mtu(key = nil) ⇒ Object
KCP 引擎的 MTU。每个 KCP 包会被整体包进 Mnet 头(HEADER_LEN)再作为单个 UDP 数据报发送,加密时还要再叠加 GCM 开销,因此 KCP 自身的 MTU 必须为这些 开销留出空间,保证整包 <= IP_MTU,避免 IP 分片(移动网络/VPN 下分片极易丢)。
121 122 123 |
# File 'lib/mnet.rb', line 121 def kcp_mtu(key = nil) IP_MTU - HEADER_LEN - (key ? GCM_OVERHEAD : 0) end |
.now ⇒ Object
108 109 110 |
# File 'lib/mnet.rb', line 108 def now Process.clock_gettime(Process::CLOCK_MONOTONIC) end |
.pack(session_id, seq, ack, type, flags, window, payload = "".b) ⇒ Object
83 84 85 86 |
# File 'lib/mnet.rb', line 83 def pack(session_id, seq, ack, type, flags, window, payload = "".b) [MAGIC, session_id, seq, ack, type, flags, window, payload] .pack("a3a16Q>Q>CCNa*") end |
.session_id_from_key(key) ⇒ Object
Session id is derived from the shared session key (mosh-style), so the id identifies the key and vice versa; without a key it is just random.
114 115 116 |
# File 'lib/mnet.rb', line 114 def session_id_from_key(key) OpenSSL::Digest::SHA256.digest(key)[0, SESSION_ID_LEN] end |
.socket_pair ⇒ Object
A connected stream socket pair, portable across Windows/Linux/macOS. Prefers a UNIX socketpair; falls back to a loopback TCP pair where AF_UNIX socketpair is unavailable (Windows).
128 129 130 131 132 133 134 135 136 137 |
# File 'lib/mnet.rb', line 128 def socket_pair Socket.pair(:UNIX, :STREAM, 0) rescue SystemCallError, NotImplementedError server = TCPServer.new("127.0.0.1", 0) port = server.addr[1] client = TCPSocket.new("127.0.0.1", port) accepted = server.accept server.close [accepted, client] end |
.type_name(type) ⇒ Object
96 97 98 |
# File 'lib/mnet.rb', line 96 def type_name(type) TYPES.fetch(type, "UNKNOWN(#{type})") end |
.unpack(data) ⇒ Object
88 89 90 91 92 93 94 |
# File 'lib/mnet.rb', line 88 def unpack(data) return nil if data.bytesize < HEADER_LEN magic, sid, seq, ack, type, flags, window = data.unpack("a3a16Q>Q>CCN") return nil unless magic == MAGIC Packet.new(sid, seq, ack, type, flags, window, data.byteslice(HEADER_LEN, data.bytesize - HEADER_LEN) || "".b) end |