mnet 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02c89a77bbd53a45c2ad2a959619b84f620f8b6fbe46009d58cac1a86853d378
4
- data.tar.gz: ae87b7f42b17168b543ee95ae4bbfc24dde6c46c45c6fe1cf9ece66a9bae4104
3
+ metadata.gz: d0fbab81dc8d236bf99365375204d8a88fddc69bdd739873c9a911257db6da0a
4
+ data.tar.gz: 55bda39f9f15c785493da5938b8c1489dd624a28da79469276cae8460422854c
5
5
  SHA512:
6
- metadata.gz: eedc601d6d212724810a1c8ee7884cdde278c9f319c68d3c6fdc8b664d41e80fdd9dc6bad76da4bb3141871e13582f0c4f8105e5c115f23c4bb91752eb2b18d3
7
- data.tar.gz: 75450fbd37c26607fd69f967abee32b1a157288abb41b48ae3b5190a6285b3c2a6932f27d7f686afcb49d8e6e0ee3f3d60b040c4e5f24135c7500fc8dc3b788b
6
+ metadata.gz: 73998b8ba73cf9d787d41576c578f44183c3f3c5365d8131d8af3912cc688cb06812dc0dae1298e2b993e3ea521c9748dc0152f50e52bcfed8377299c2683ca7
7
+ data.tar.gz: ec19fe682e1672d3948df00c8ea5ebb7222fac2d3527daa81ad6d48f5fc6a142cfcbdf7f621a063d49b9a1ebf930d3b10a9c42bb25cec02a246a1ce28b0b5f6b
data/README.en.md CHANGED
@@ -51,6 +51,11 @@ puts sess.readpartial(1024)
51
51
  | `:mnet` | pure-Ruby ARQ, no C dependency, simple and easy to debug |
52
52
  | `:kcp` | C engine (via the `kcp` gem), ~8x faster, requires building a native extension |
53
53
 
54
+ > With `proto: :kcp`, mnet automatically sizes the KCP engine's MTU to
55
+ > `IP_MTU(1280) - Mnet header(41) - GCM overhead(28, only when a key is used)`, so the full
56
+ > datagram (Mnet header + KCP packet [+ encryption]) stays within the link MTU and is never
57
+ > IP-fragmented. Override it with the `kcp_mtu:` option if needed.
58
+
54
59
  ## Encryption (pre-shared key)
55
60
 
56
61
  Per-packet AES-256-GCM; both ends must pre-share the same 32-byte key:
data/README.md CHANGED
@@ -49,6 +49,11 @@ puts sess.readpartial(1024)
49
49
  | `:mnet` | 纯 Ruby ARQ,无 C 依赖,简单、易调试 |
50
50
  | `:kcp` | C 引擎(依赖 `kcp` gem),快约 8 倍,需编译原生扩展 |
51
51
 
52
+ > `proto: :kcp` 时,mnet 会自动把 KCP 引擎的 MTU 设为
53
+ > `IP_MTU(1280) - Mnet 头(41) - 加密开销(28,仅启用 key 时)`,
54
+ > 使「Mnet 头 + KCP 包(+ 加密)」整包不超过链路 MTU,避免 IP 分片。
55
+ > 如需自定义可用 `kcp_mtu:` 选项覆盖。
56
+
52
57
  ## 加密(预共享密钥)
53
58
 
54
59
  每包 AES-256-GCM,两端需预共享同一个 32 字节密钥:
data/lib/mnet/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mnet
4
- VERSION = '0.1.5'
4
+ VERSION = '0.1.6'
5
5
  end
data/lib/mnet.rb CHANGED
@@ -35,6 +35,9 @@ module Mnet
35
35
  # IP(20) + UDP(8) + our header + GCM tag(16) leaves ~1190 for a segment.
36
36
  DEFAULT_MSS = 1200
37
37
 
38
+ # AES-256-GCM 每包的额外开销:12 字节 nonce + 16 字节认证标签。
39
+ GCM_OVERHEAD = 28
40
+
38
41
  # RTO bounds (mosh: MIN_RTO = 50ms, MAX_RTO = 1000ms).
39
42
  MIN_RTO = 0.05
40
43
  MAX_RTO = 1.0
@@ -59,6 +62,10 @@ module Mnet
59
62
  FLAG_MIGRATE = 0x01
60
63
  FLAG_KCP = 0x02 # session uses the KCP (C) engine instead of the Ruby ARQ
61
64
 
65
+ # KCP 接收窗口(ikcp.c 内 IKCP_WND_RCV,固定 128)。单次 ikcp_send 超过
66
+ # 窗口大小的分段数会返回 -2 丢弃,故 KcpSession#write 按此切块。
67
+ KCP_WND_RCV = 128
68
+
62
69
  HEADER_LEN = MAGIC_LEN + SESSION_ID_LEN + 8 + 8 + 1 + 1 + 4
63
70
 
64
71
  MAX_RETRIES = 10
@@ -108,6 +115,13 @@ module Mnet
108
115
  OpenSSL::Digest::SHA256.digest(key)[0, SESSION_ID_LEN]
109
116
  end
110
117
 
118
+ # KCP 引擎的 MTU。每个 KCP 包会被整体包进 Mnet 头(HEADER_LEN)再作为单个
119
+ # UDP 数据报发送,加密时还要再叠加 GCM 开销,因此 KCP 自身的 MTU 必须为这些
120
+ # 开销留出空间,保证整包 <= IP_MTU,避免 IP 分片(移动网络/VPN 下分片极易丢)。
121
+ def kcp_mtu(key = nil)
122
+ IP_MTU - HEADER_LEN - (key ? GCM_OVERHEAD : 0)
123
+ end
124
+
111
125
  # A connected stream socket pair, portable across Windows/Linux/macOS.
112
126
  # Prefers a UNIX socketpair; falls back to a loopback TCP pair where AF_UNIX
113
127
  # socketpair is unavailable (Windows).
@@ -681,6 +695,7 @@ module Mnet
681
695
 
682
696
  @conv = id.unpack1("N") # KCP conv derived from the session id
683
697
  @engine = Kcp::Engine.new(@conv)
698
+ @engine.setmtu(opts.fetch(:kcp_mtu, Mnet.kcp_mtu(@key)))
684
699
 
685
700
  @m = Monitor.new
686
701
  @cv = @m.new_cond
@@ -715,10 +730,21 @@ module Mnet
715
730
  data = data.to_s.b
716
731
  return 0 if data.empty?
717
732
 
733
+ total = data.bytesize
718
734
  @m.synchronize do
719
735
  return 0 if @closed || @eof
720
- @engine.send(data)
721
- pump_output
736
+
737
+ # ikcp_send 单次最多接受 IKCP_WND_RCV(128) 个分段,超出会返回 -2 把整块
738
+ # 数据静默丢弃。这里按 (mss * (窗口-1)) 切块喂入,避免大块写被丢。
739
+ chunk = @engine.mss * (KCP_WND_RCV - 1)
740
+ offset = 0
741
+ while offset < total
742
+ n = [chunk, total - offset].min
743
+ queued = @engine.send(data.byteslice(offset, n))
744
+ break if queued <= 0
745
+ offset += queued
746
+ pump_output
747
+ end
722
748
  end
723
749
  # 窗口满(有积压)时让出 GVL,避免写线程紧循环饿死处理 ACK 的传输线程。
724
750
  Thread.pass if @engine.waitsnd > 0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mnet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - mnet developers