Kcp.cs 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. // Kcp based on https://github.com/skywind3000/kcp
  2. // Kept as close to original as possible.
  3. using System;
  4. using System.Collections.Generic;
  5. namespace kcp2k
  6. {
  7. public class Kcp
  8. {
  9. // original Kcp has a define option, which is not defined by default:
  10. // #define FASTACK_CONSERVE
  11. public const int RTO_NDL = 30; // no delay min rto
  12. public const int RTO_MIN = 100; // normal min rto
  13. public const int RTO_DEF = 200; // default RTO
  14. public const int RTO_MAX = 60000; // maximum RTO
  15. public const int CMD_PUSH = 81; // cmd: push data
  16. public const int CMD_ACK = 82; // cmd: ack
  17. public const int CMD_WASK = 83; // cmd: window probe (ask)
  18. public const int CMD_WINS = 84; // cmd: window size (tell)
  19. public const int ASK_SEND = 1; // need to send CMD_WASK
  20. public const int ASK_TELL = 2; // need to send CMD_WINS
  21. public const int WND_SND = 32; // default send window
  22. public const int WND_RCV = 128; // default receive window. must be >= max fragment size
  23. public const int MTU_DEF = 1200; // default MTU (reduced to 1200 to fit all cases: https://en.wikipedia.org/wiki/Maximum_transmission_unit ; steam uses 1200 too!)
  24. public const int ACK_FAST = 3;
  25. public const int INTERVAL = 100;
  26. public const int OVERHEAD = 24;
  27. public const int DEADLINK = 20;
  28. public const int THRESH_INIT = 2;
  29. public const int THRESH_MIN = 2;
  30. public const int PROBE_INIT = 7000; // 7 secs to probe window size
  31. public const int PROBE_LIMIT = 120000; // up to 120 secs to probe window
  32. public const int FASTACK_LIMIT = 5; // max times to trigger fastack
  33. internal struct AckItem
  34. {
  35. internal uint serialNumber;
  36. internal uint timestamp;
  37. }
  38. // kcp members.
  39. internal int state;
  40. readonly uint conv; // conversation
  41. internal uint mtu;
  42. internal uint mss; // maximum segment size := MTU - OVERHEAD
  43. internal uint snd_una; // unacknowledged. e.g. snd_una is 9 it means 8 has been confirmed, 9 and 10 have been sent
  44. internal uint snd_nxt;
  45. internal uint rcv_nxt;
  46. internal uint ssthresh; // slow start threshold
  47. internal int rx_rttval; // average deviation of rtt, used to measure the jitter of rtt
  48. internal int rx_srtt; // smoothed round trip time (a weighted average of rtt)
  49. internal int rx_rto;
  50. internal int rx_minrto;
  51. internal uint snd_wnd; // send window
  52. internal uint rcv_wnd; // receive window
  53. internal uint rmt_wnd; // remote window
  54. internal uint cwnd; // congestion window
  55. internal uint probe;
  56. internal uint interval;
  57. internal uint ts_flush;
  58. internal uint xmit;
  59. internal uint nodelay; // not a bool. original Kcp has '<2 else' check.
  60. internal bool updated;
  61. internal uint ts_probe; // timestamp probe
  62. internal uint probe_wait;
  63. internal uint dead_link;
  64. internal uint incr;
  65. internal uint current; // current time (milliseconds). set by Update.
  66. internal int fastresend;
  67. internal int fastlimit;
  68. internal bool nocwnd; // no congestion window
  69. internal readonly Queue<Segment> snd_queue = new Queue<Segment>(16); // send queue
  70. internal readonly Queue<Segment> rcv_queue = new Queue<Segment>(16); // receive queue
  71. // snd_buffer needs index removals.
  72. // C# LinkedList allocates for each entry, so let's keep List for now.
  73. internal readonly List<Segment> snd_buf = new List<Segment>(16); // send buffer
  74. // rcv_buffer needs index insertions and backwards iteration.
  75. // C# LinkedList allocates for each entry, so let's keep List for now.
  76. internal readonly List<Segment> rcv_buf = new List<Segment>(16); // receive buffer
  77. internal readonly List<AckItem> acklist = new List<AckItem>(16);
  78. internal byte[] buffer;
  79. readonly Action<byte[], int> output; // buffer, size
  80. // get how many packet is waiting to be sent
  81. public int WaitSnd => snd_buf.Count + snd_queue.Count;
  82. // segment pool to avoid allocations in C#.
  83. // this is not part of the original C code.
  84. readonly Pool<Segment> SegmentPool = new Pool<Segment>(
  85. // create new segment
  86. () => new Segment(),
  87. // reset segment before reuse
  88. (segment) => segment.Reset(),
  89. // initial capacity
  90. 32
  91. );
  92. // ikcp_create
  93. // create a new kcp control object, 'conv' must equal in two endpoint
  94. // from the same connection.
  95. public Kcp(uint conv, Action<byte[], int> output)
  96. {
  97. this.conv = conv;
  98. this.output = output;
  99. snd_wnd = WND_SND;
  100. rcv_wnd = WND_RCV;
  101. rmt_wnd = WND_RCV;
  102. mtu = MTU_DEF;
  103. mss = mtu - OVERHEAD;
  104. rx_rto = RTO_DEF;
  105. rx_minrto = RTO_MIN;
  106. interval = INTERVAL;
  107. ts_flush = INTERVAL;
  108. ssthresh = THRESH_INIT;
  109. fastlimit = FASTACK_LIMIT;
  110. dead_link = DEADLINK;
  111. buffer = new byte[(mtu + OVERHEAD) * 3];
  112. }
  113. // ikcp_segment_new
  114. // we keep the original function and add our pooling to it.
  115. // this way we'll never miss it anywhere.
  116. Segment SegmentNew() => SegmentPool.Take();
  117. // ikcp_segment_delete
  118. // we keep the original function and add our pooling to it.
  119. // this way we'll never miss it anywhere.
  120. void SegmentDelete(Segment seg) => SegmentPool.Return(seg);
  121. // ikcp_recv
  122. // receive data from kcp state machine
  123. // returns number of bytes read.
  124. // returns negative on error.
  125. // note: pass negative length to peek.
  126. public int Receive(byte[] buffer, int len)
  127. {
  128. // kcp's ispeek feature is not supported.
  129. // this makes 'merge fragment' code significantly easier because
  130. // we can iterate while queue.Count > 0 and dequeue each time.
  131. // if we had to consider ispeek then count would always be > 0 and
  132. // we would have to remove only after the loop.
  133. //
  134. //bool ispeek = len < 0;
  135. if (len < 0)
  136. throw new NotSupportedException("Receive ispeek for negative len is not supported!");
  137. if (rcv_queue.Count == 0)
  138. return -1;
  139. if (len < 0) len = -len;
  140. int peeksize = PeekSize();
  141. if (peeksize < 0)
  142. return -2;
  143. if (peeksize > len)
  144. return -3;
  145. bool recover = rcv_queue.Count >= rcv_wnd;
  146. // merge fragment.
  147. int offset = 0;
  148. len = 0;
  149. // original KCP iterates rcv_queue and deletes if !ispeek.
  150. // removing from a c# queue while iterating is not possible, but
  151. // we can change to 'while Count > 0' and remove every time.
  152. // (we can remove every time because we removed ispeek support!)
  153. while (rcv_queue.Count > 0)
  154. {
  155. // unlike original kcp, we dequeue instead of just getting the
  156. // entry. this is fine because we remove it in ANY case.
  157. Segment seg = rcv_queue.Dequeue();
  158. Buffer.BlockCopy(seg.data.GetBuffer(), 0, buffer, offset, (int)seg.data.Position);
  159. offset += (int)seg.data.Position;
  160. len += (int)seg.data.Position;
  161. uint fragment = seg.frg;
  162. // note: ispeek is not supported in order to simplify this loop
  163. // unlike original kcp, we don't need to remove seg from queue
  164. // because we already dequeued it.
  165. // simply delete it
  166. SegmentDelete(seg);
  167. if (fragment == 0)
  168. break;
  169. }
  170. // move available data from rcv_buf -> rcv_queue
  171. int removed = 0;
  172. foreach (Segment seg in rcv_buf)
  173. {
  174. if (seg.sn == rcv_nxt && rcv_queue.Count < rcv_wnd)
  175. {
  176. // can't remove while iterating. remember how many to remove
  177. // and do it after the loop.
  178. // note: don't return segment. we only add it to rcv_queue
  179. ++removed;
  180. // add
  181. rcv_queue.Enqueue(seg);
  182. rcv_nxt++;
  183. }
  184. else
  185. {
  186. break;
  187. }
  188. }
  189. rcv_buf.RemoveRange(0, removed);
  190. // fast recover
  191. if (rcv_queue.Count < rcv_wnd && recover)
  192. {
  193. // ready to send back CMD_WINS in flush
  194. // tell remote my window size
  195. probe |= ASK_TELL;
  196. }
  197. return len;
  198. }
  199. // ikcp_peeksize
  200. // check the size of next message in the recv queue
  201. public int PeekSize()
  202. {
  203. int length = 0;
  204. if (rcv_queue.Count == 0) return -1;
  205. Segment seq = rcv_queue.Peek();
  206. if (seq.frg == 0) return (int)seq.data.Position;
  207. if (rcv_queue.Count < seq.frg + 1) return -1;
  208. foreach (Segment seg in rcv_queue)
  209. {
  210. length += (int)seg.data.Position;
  211. if (seg.frg == 0) break;
  212. }
  213. return length;
  214. }
  215. // ikcp_send
  216. // sends byte[] to the other end.
  217. public int Send(byte[] buffer, int offset, int len)
  218. {
  219. // fragment count
  220. int count;
  221. if (len < 0) return -1;
  222. // streaming mode: removed. we never want to send 'hello' and
  223. // receive 'he' 'll' 'o'. we want to always receive 'hello'.
  224. // calculate amount of fragments necessary for 'len'
  225. if (len <= mss) count = 1;
  226. else count = (int)((len + mss - 1) / mss);
  227. // original kcp uses WND_RCV const even though rcv_wnd is the
  228. // runtime variable. may or may not be correct, see also:
  229. // see also: https://github.com/skywind3000/kcp/pull/291/files
  230. if (count >= WND_RCV) return -2;
  231. if (count == 0) count = 1;
  232. // fragment
  233. for (int i = 0; i < count; i++)
  234. {
  235. int size = len > (int)mss ? (int)mss : len;
  236. Segment seg = SegmentNew();
  237. if (len > 0)
  238. {
  239. seg.data.Write(buffer, offset, size);
  240. }
  241. // seg.len = size: WriteBytes sets segment.Position!
  242. seg.frg = (byte)(count - i - 1);
  243. snd_queue.Enqueue(seg);
  244. offset += size;
  245. len -= size;
  246. }
  247. return 0;
  248. }
  249. // ikcp_update_ack
  250. void UpdateAck(int rtt) // round trip time
  251. {
  252. // https://tools.ietf.org/html/rfc6298
  253. if (rx_srtt == 0)
  254. {
  255. rx_srtt = rtt;
  256. rx_rttval = rtt / 2;
  257. }
  258. else
  259. {
  260. int delta = rtt - rx_srtt;
  261. if (delta < 0) delta = -delta;
  262. rx_rttval = (3 * rx_rttval + delta) / 4;
  263. rx_srtt = (7 * rx_srtt + rtt) / 8;
  264. if (rx_srtt < 1) rx_srtt = 1;
  265. }
  266. int rto = rx_srtt + Math.Max((int)interval, 4 * rx_rttval);
  267. rx_rto = Utils.Clamp(rto, rx_minrto, RTO_MAX);
  268. }
  269. // ikcp_shrink_buf
  270. internal void ShrinkBuf()
  271. {
  272. if (snd_buf.Count > 0)
  273. {
  274. Segment seg = snd_buf[0];
  275. snd_una = seg.sn;
  276. }
  277. else
  278. {
  279. snd_una = snd_nxt;
  280. }
  281. }
  282. // ikcp_parse_ack
  283. // removes the segment with 'sn' from send buffer
  284. internal void ParseAck(uint sn)
  285. {
  286. if (Utils.TimeDiff(sn, snd_una) < 0 || Utils.TimeDiff(sn, snd_nxt) >= 0)
  287. return;
  288. // for-int so we can erase while iterating
  289. for (int i = 0; i < snd_buf.Count; ++i)
  290. {
  291. Segment seg = snd_buf[i];
  292. if (sn == seg.sn)
  293. {
  294. snd_buf.RemoveAt(i);
  295. SegmentDelete(seg);
  296. break;
  297. }
  298. if (Utils.TimeDiff(sn, seg.sn) < 0)
  299. {
  300. break;
  301. }
  302. }
  303. }
  304. // ikcp_parse_una
  305. void ParseUna(uint una)
  306. {
  307. int removed = 0;
  308. foreach (Segment seg in snd_buf)
  309. {
  310. if (Utils.TimeDiff(una, seg.sn) > 0)
  311. {
  312. // can't remove while iterating. remember how many to remove
  313. // and do it after the loop.
  314. ++removed;
  315. SegmentDelete(seg);
  316. }
  317. else
  318. {
  319. break;
  320. }
  321. }
  322. snd_buf.RemoveRange(0, removed);
  323. }
  324. // ikcp_parse_fastack
  325. void ParseFastack(uint sn, uint ts)
  326. {
  327. if (Utils.TimeDiff(sn, snd_una) < 0 || Utils.TimeDiff(sn, snd_nxt) >= 0)
  328. return;
  329. foreach (Segment seg in snd_buf)
  330. {
  331. if (Utils.TimeDiff(sn, seg.sn) < 0)
  332. {
  333. break;
  334. }
  335. else if (sn != seg.sn)
  336. {
  337. #if !FASTACK_CONSERVE
  338. seg.fastack++;
  339. #else
  340. if (Utils.TimeDiff(ts, seg.ts) >= 0)
  341. seg.fastack++;
  342. #endif
  343. }
  344. }
  345. }
  346. // ikcp_ack_push
  347. // appends an ack.
  348. void AckPush(uint sn, uint ts)
  349. {
  350. acklist.Add(new AckItem{ serialNumber = sn, timestamp = ts });
  351. }
  352. // ikcp_parse_data
  353. void ParseData(Segment newseg)
  354. {
  355. uint sn = newseg.sn;
  356. if (Utils.TimeDiff(sn, rcv_nxt + rcv_wnd) >= 0 ||
  357. Utils.TimeDiff(sn, rcv_nxt) < 0)
  358. {
  359. SegmentDelete(newseg);
  360. return;
  361. }
  362. InsertSegmentInReceiveBuffer(newseg);
  363. MoveReceiveBufferDataToReceiveQueue();
  364. }
  365. // inserts the segment into rcv_buf, ordered by seg.sn.
  366. // drops the segment if one with the same seg.sn already exists.
  367. // goes through receive buffer in reverse order for performance.
  368. //
  369. // note: see KcpTests.InsertSegmentInReceiveBuffer test!
  370. // note: 'insert or delete' can be done in different ways, but let's
  371. // keep consistency with original C kcp.
  372. internal void InsertSegmentInReceiveBuffer(Segment newseg)
  373. {
  374. bool repeat = false; // 'duplicate'
  375. // original C iterates backwards, so we need to do that as well.
  376. int i;
  377. for (i = rcv_buf.Count - 1; i >= 0; i--)
  378. {
  379. Segment seg = rcv_buf[i];
  380. if (seg.sn == newseg.sn)
  381. {
  382. // duplicate segment found. nothing will be added.
  383. repeat = true;
  384. break;
  385. }
  386. if (Utils.TimeDiff(newseg.sn, seg.sn) > 0)
  387. {
  388. // this entry's sn is < newseg.sn, so let's stop
  389. break;
  390. }
  391. }
  392. // no duplicate? then insert.
  393. if (!repeat)
  394. {
  395. rcv_buf.Insert(i + 1, newseg);
  396. }
  397. // duplicate. just delete it.
  398. else
  399. {
  400. SegmentDelete(newseg);
  401. }
  402. }
  403. // move available data from rcv_buf -> rcv_queue
  404. void MoveReceiveBufferDataToReceiveQueue()
  405. {
  406. int removed = 0;
  407. foreach (Segment seg in rcv_buf)
  408. {
  409. if (seg.sn == rcv_nxt && rcv_queue.Count < rcv_wnd)
  410. {
  411. // can't remove while iterating. remember how many to remove
  412. // and do it after the loop.
  413. ++removed;
  414. rcv_queue.Enqueue(seg);
  415. rcv_nxt++;
  416. }
  417. else
  418. {
  419. break;
  420. }
  421. }
  422. rcv_buf.RemoveRange(0, removed);
  423. }
  424. // ikcp_input
  425. // used when you receive a low level packet (e.g. UDP packet)
  426. // => original kcp uses offset=0, we made it a parameter so that high
  427. // level can skip the channel byte more easily
  428. public int Input(byte[] data, int offset, int size)
  429. {
  430. uint prev_una = snd_una;
  431. uint maxack = 0;
  432. uint latest_ts = 0;
  433. int flag = 0;
  434. if (data == null || size < OVERHEAD) return -1;
  435. while (true)
  436. {
  437. uint ts = 0;
  438. uint sn = 0;
  439. uint len = 0;
  440. uint una = 0;
  441. uint conv_ = 0;
  442. ushort wnd = 0;
  443. byte cmd = 0;
  444. byte frg = 0;
  445. // enough data left to decode segment (aka OVERHEAD bytes)?
  446. if (size < OVERHEAD) break;
  447. // decode segment
  448. offset += Utils.Decode32U(data, offset, ref conv_);
  449. if (conv_ != conv) return -1;
  450. offset += Utils.Decode8u(data, offset, ref cmd);
  451. offset += Utils.Decode8u(data, offset, ref frg);
  452. offset += Utils.Decode16U(data, offset, ref wnd);
  453. offset += Utils.Decode32U(data, offset, ref ts);
  454. offset += Utils.Decode32U(data, offset, ref sn);
  455. offset += Utils.Decode32U(data, offset, ref una);
  456. offset += Utils.Decode32U(data, offset, ref len);
  457. // subtract the segment bytes from size
  458. size -= OVERHEAD;
  459. // enough remaining to read 'len' bytes of the actual payload?
  460. if (size < len || len < 0) return -2;
  461. if (cmd != CMD_PUSH && cmd != CMD_ACK &&
  462. cmd != CMD_WASK && cmd != CMD_WINS)
  463. return -3;
  464. rmt_wnd = wnd;
  465. ParseUna(una);
  466. ShrinkBuf();
  467. if (cmd == CMD_ACK)
  468. {
  469. if (Utils.TimeDiff(current, ts) >= 0)
  470. {
  471. UpdateAck(Utils.TimeDiff(current, ts));
  472. }
  473. ParseAck(sn);
  474. ShrinkBuf();
  475. if (flag == 0)
  476. {
  477. flag = 1;
  478. maxack = sn;
  479. latest_ts = ts;
  480. }
  481. else
  482. {
  483. if (Utils.TimeDiff(sn, maxack) > 0)
  484. {
  485. #if !FASTACK_CONSERVE
  486. maxack = sn;
  487. latest_ts = ts;
  488. #else
  489. if (Utils.TimeDiff(ts, latest_ts) > 0)
  490. {
  491. maxack = sn;
  492. latest_ts = ts;
  493. }
  494. #endif
  495. }
  496. }
  497. }
  498. else if (cmd == CMD_PUSH)
  499. {
  500. if (Utils.TimeDiff(sn, rcv_nxt + rcv_wnd) < 0)
  501. {
  502. AckPush(sn, ts);
  503. if (Utils.TimeDiff(sn, rcv_nxt) >= 0)
  504. {
  505. Segment seg = SegmentNew();
  506. seg.conv = conv_;
  507. seg.cmd = cmd;
  508. seg.frg = frg;
  509. seg.wnd = wnd;
  510. seg.ts = ts;
  511. seg.sn = sn;
  512. seg.una = una;
  513. if (len > 0)
  514. {
  515. seg.data.Write(data, offset, (int)len);
  516. }
  517. ParseData(seg);
  518. }
  519. }
  520. }
  521. else if (cmd == CMD_WASK)
  522. {
  523. // ready to send back CMD_WINS in flush
  524. // tell remote my window size
  525. probe |= ASK_TELL;
  526. }
  527. else if (cmd == CMD_WINS)
  528. {
  529. // do nothing
  530. }
  531. else
  532. {
  533. return -3;
  534. }
  535. offset += (int)len;
  536. size -= (int)len;
  537. }
  538. if (flag != 0)
  539. {
  540. ParseFastack(maxack, latest_ts);
  541. }
  542. // cwnd update when packet arrived
  543. if (Utils.TimeDiff(snd_una, prev_una) > 0)
  544. {
  545. if (cwnd < rmt_wnd)
  546. {
  547. if (cwnd < ssthresh)
  548. {
  549. cwnd++;
  550. incr += mss;
  551. }
  552. else
  553. {
  554. if (incr < mss) incr = mss;
  555. incr += (mss * mss) / incr + (mss / 16);
  556. if ((cwnd + 1) * mss <= incr)
  557. {
  558. cwnd = (incr + mss - 1) / ((mss > 0) ? mss : 1);
  559. }
  560. }
  561. if (cwnd > rmt_wnd)
  562. {
  563. cwnd = rmt_wnd;
  564. incr = rmt_wnd * mss;
  565. }
  566. }
  567. }
  568. return 0;
  569. }
  570. // ikcp_wnd_unused
  571. uint WndUnused()
  572. {
  573. if (rcv_queue.Count < rcv_wnd)
  574. return rcv_wnd - (uint)rcv_queue.Count;
  575. return 0;
  576. }
  577. // ikcp_flush
  578. // flush remain ack segments
  579. public void Flush()
  580. {
  581. int offset = 0; // buffer ptr in original C
  582. bool lost = false; // lost segments
  583. // helper functions
  584. void MakeSpace(int space)
  585. {
  586. if (offset + space > mtu)
  587. {
  588. output(buffer, offset);
  589. offset = 0;
  590. }
  591. }
  592. void FlushBuffer()
  593. {
  594. if (offset > 0)
  595. {
  596. output(buffer, offset);
  597. }
  598. }
  599. // 'ikcp_update' haven't been called.
  600. if (!updated) return;
  601. // kcp only stack allocates a segment here for performance, leaving
  602. // its data buffer null because this segment's data buffer is never
  603. // used. that's fine in C, but in C# our segment is class so we need
  604. // to allocate and most importantly, not forget to deallocate it
  605. // before returning.
  606. Segment seg = SegmentNew();
  607. seg.conv = conv;
  608. seg.cmd = CMD_ACK;
  609. seg.wnd = WndUnused();
  610. seg.una = rcv_nxt;
  611. // flush acknowledges
  612. foreach (AckItem ack in acklist)
  613. {
  614. MakeSpace(OVERHEAD);
  615. // ikcp_ack_get assigns ack[i] to seg.sn, seg.ts
  616. seg.sn = ack.serialNumber;
  617. seg.ts = ack.timestamp;
  618. offset += seg.Encode(buffer, offset);
  619. }
  620. acklist.Clear();
  621. // probe window size (if remote window size equals zero)
  622. if (rmt_wnd == 0)
  623. {
  624. if (probe_wait == 0)
  625. {
  626. probe_wait = PROBE_INIT;
  627. ts_probe = current + probe_wait;
  628. }
  629. else
  630. {
  631. if (Utils.TimeDiff(current, ts_probe) >= 0)
  632. {
  633. if (probe_wait < PROBE_INIT)
  634. probe_wait = PROBE_INIT;
  635. probe_wait += probe_wait / 2;
  636. if (probe_wait > PROBE_LIMIT)
  637. probe_wait = PROBE_LIMIT;
  638. ts_probe = current + probe_wait;
  639. probe |= ASK_SEND;
  640. }
  641. }
  642. }
  643. else
  644. {
  645. ts_probe = 0;
  646. probe_wait = 0;
  647. }
  648. // flush window probing commands
  649. if ((probe & ASK_SEND) != 0)
  650. {
  651. seg.cmd = CMD_WASK;
  652. MakeSpace(OVERHEAD);
  653. offset += seg.Encode(buffer, offset);
  654. }
  655. // flush window probing commands
  656. if ((probe & ASK_TELL) != 0)
  657. {
  658. seg.cmd = CMD_WINS;
  659. MakeSpace(OVERHEAD);
  660. offset += seg.Encode(buffer, offset);
  661. }
  662. probe = 0;
  663. // calculate window size
  664. uint cwnd_ = Math.Min(snd_wnd, rmt_wnd);
  665. // if congestion window:
  666. if (!nocwnd) cwnd_ = Math.Min(cwnd, cwnd_);
  667. // move data from snd_queue to snd_buf
  668. // sliding window, controlled by snd_nxt && sna_una+cwnd
  669. //
  670. // ELI5: 'snd_nxt' is what we want to send.
  671. // 'snd_una' is what hasn't been acked yet.
  672. // copy up to 'cwnd_' difference between them (sliding window)
  673. while (Utils.TimeDiff(snd_nxt, snd_una + cwnd_) < 0)
  674. {
  675. if (snd_queue.Count == 0) break;
  676. Segment newseg = snd_queue.Dequeue();
  677. newseg.conv = conv;
  678. newseg.cmd = CMD_PUSH;
  679. newseg.wnd = seg.wnd;
  680. newseg.ts = current;
  681. newseg.sn = snd_nxt++;
  682. newseg.una = rcv_nxt;
  683. newseg.resendts = current;
  684. newseg.rto = rx_rto;
  685. newseg.fastack = 0;
  686. newseg.xmit = 0;
  687. snd_buf.Add(newseg);
  688. }
  689. // calculate resent
  690. uint resent = fastresend > 0 ? (uint)fastresend : 0xffffffff;
  691. uint rtomin = nodelay == 0 ? (uint)rx_rto >> 3 : 0;
  692. // flush data segments
  693. int change = 0;
  694. foreach (Segment segment in snd_buf)
  695. {
  696. bool needsend = false;
  697. // initial transmit
  698. if (segment.xmit == 0)
  699. {
  700. needsend = true;
  701. segment.xmit++;
  702. segment.rto = rx_rto;
  703. segment.resendts = current + (uint)segment.rto + rtomin;
  704. }
  705. // RTO
  706. else if (Utils.TimeDiff(current, segment.resendts) >= 0)
  707. {
  708. needsend = true;
  709. segment.xmit++;
  710. xmit++;
  711. if (nodelay == 0)
  712. {
  713. segment.rto += Math.Max(segment.rto, rx_rto);
  714. }
  715. else
  716. {
  717. int step = (nodelay < 2) ? segment.rto : rx_rto;
  718. segment.rto += step / 2;
  719. }
  720. segment.resendts = current + (uint)segment.rto;
  721. lost = true;
  722. }
  723. // fast retransmit
  724. else if (segment.fastack >= resent)
  725. {
  726. if (segment.xmit <= fastlimit || fastlimit <= 0)
  727. {
  728. needsend = true;
  729. segment.xmit++;
  730. segment.fastack = 0;
  731. segment.resendts = current + (uint)segment.rto;
  732. change++;
  733. }
  734. }
  735. if (needsend)
  736. {
  737. segment.ts = current;
  738. segment.wnd = seg.wnd;
  739. segment.una = rcv_nxt;
  740. int need = OVERHEAD + (int)segment.data.Position;
  741. MakeSpace(need);
  742. offset += segment.Encode(buffer, offset);
  743. if (segment.data.Position > 0)
  744. {
  745. Buffer.BlockCopy(segment.data.GetBuffer(), 0, buffer, offset, (int)segment.data.Position);
  746. offset += (int)segment.data.Position;
  747. }
  748. if (segment.xmit >= dead_link)
  749. {
  750. state = -1;
  751. }
  752. }
  753. }
  754. // kcp stackallocs 'seg'. our C# segment is a class though, so we
  755. // need to properly delete and return it to the pool now that we are
  756. // done with it.
  757. SegmentDelete(seg);
  758. // flash remain segments
  759. FlushBuffer();
  760. // update ssthresh
  761. // rate halving, https://tools.ietf.org/html/rfc6937
  762. if (change > 0)
  763. {
  764. uint inflight = snd_nxt - snd_una;
  765. ssthresh = inflight / 2;
  766. if (ssthresh < THRESH_MIN)
  767. ssthresh = THRESH_MIN;
  768. cwnd = ssthresh + resent;
  769. incr = cwnd * mss;
  770. }
  771. // congestion control, https://tools.ietf.org/html/rfc5681
  772. if (lost)
  773. {
  774. // original C uses 'cwnd', not kcp->cwnd!
  775. ssthresh = cwnd_ / 2;
  776. if (ssthresh < THRESH_MIN)
  777. ssthresh = THRESH_MIN;
  778. cwnd = 1;
  779. incr = mss;
  780. }
  781. if (cwnd < 1)
  782. {
  783. cwnd = 1;
  784. incr = mss;
  785. }
  786. }
  787. // ikcp_update
  788. // update state (call it repeatedly, every 10ms-100ms), or you can ask
  789. // Check() when to call it again (without Input/Send calling).
  790. //
  791. // 'current' - current timestamp in millisec. pass it to Kcp so that
  792. // Kcp doesn't have to do any stopwatch/deltaTime/etc. code
  793. public void Update(uint currentTimeMilliSeconds)
  794. {
  795. current = currentTimeMilliSeconds;
  796. if (!updated)
  797. {
  798. updated = true;
  799. ts_flush = current;
  800. }
  801. int slap = Utils.TimeDiff(current, ts_flush);
  802. if (slap >= 10000 || slap < -10000)
  803. {
  804. ts_flush = current;
  805. slap = 0;
  806. }
  807. if (slap >= 0)
  808. {
  809. ts_flush += interval;
  810. if (Utils.TimeDiff(current, ts_flush) >= 0)
  811. {
  812. ts_flush = current + interval;
  813. }
  814. Flush();
  815. }
  816. }
  817. // ikcp_check
  818. // Determine when should you invoke update
  819. // Returns when you should invoke update in millisec, if there is no
  820. // input/send calling. you can call update in that time, instead of
  821. // call update repeatly.
  822. //
  823. // Important to reduce unnecessary update invoking. use it to schedule
  824. // update (e.g. implementing an epoll-like mechanism, or optimize update
  825. // when handling massive kcp connections).
  826. public uint Check(uint current_)
  827. {
  828. uint ts_flush_ = ts_flush;
  829. int tm_flush = 0x7fffffff;
  830. int tm_packet = 0x7fffffff;
  831. if (!updated)
  832. {
  833. return current_;
  834. }
  835. if (Utils.TimeDiff(current_, ts_flush_) >= 10000 ||
  836. Utils.TimeDiff(current_, ts_flush_) < -10000)
  837. {
  838. ts_flush_ = current_;
  839. }
  840. if (Utils.TimeDiff(current_, ts_flush_) >= 0)
  841. {
  842. return current_;
  843. }
  844. tm_flush = Utils.TimeDiff(ts_flush_, current_);
  845. foreach (Segment seg in snd_buf)
  846. {
  847. int diff = Utils.TimeDiff(seg.resendts, current_);
  848. if (diff <= 0)
  849. {
  850. return current_;
  851. }
  852. if (diff < tm_packet) tm_packet = diff;
  853. }
  854. uint minimal = (uint)(tm_packet < tm_flush ? tm_packet : tm_flush);
  855. if (minimal >= interval) minimal = interval;
  856. return current_ + minimal;
  857. }
  858. // ikcp_setmtu
  859. // Change MTU (Maximum Transmission Unit) size.
  860. public void SetMtu(uint mtu)
  861. {
  862. if (mtu < 50 || mtu < OVERHEAD)
  863. throw new ArgumentException("MTU must be higher than 50 and higher than OVERHEAD");
  864. buffer = new byte[(mtu + OVERHEAD) * 3];
  865. this.mtu = mtu;
  866. mss = mtu - OVERHEAD;
  867. }
  868. // ikcp_interval
  869. public void SetInterval(uint interval)
  870. {
  871. if (interval > 5000) interval = 5000;
  872. else if (interval < 10) interval = 10;
  873. this.interval = interval;
  874. }
  875. // ikcp_nodelay
  876. // configuration: https://github.com/skywind3000/kcp/blob/master/README.en.md#protocol-configuration
  877. // nodelay : Whether nodelay mode is enabled, 0 is not enabled; 1 enabled.
  878. // interval :Protocol internal work interval, in milliseconds, such as 10 ms or 20 ms.
  879. // resend :Fast retransmission mode, 0 represents off by default, 2 can be set (2 ACK spans will result in direct retransmission)
  880. // nc :Whether to turn off flow control, 0 represents “Do not turn off” by default, 1 represents “Turn off”.
  881. // Normal Mode: ikcp_nodelay(kcp, 0, 40, 0, 0);
  882. // Turbo Mode: ikcp_nodelay(kcp, 1, 10, 2, 1);
  883. public void SetNoDelay(uint nodelay, uint interval = INTERVAL, int resend = 0, bool nocwnd = false)
  884. {
  885. this.nodelay = nodelay;
  886. if (nodelay != 0)
  887. {
  888. rx_minrto = RTO_NDL;
  889. }
  890. else
  891. {
  892. rx_minrto = RTO_MIN;
  893. }
  894. if (interval >= 0)
  895. {
  896. if (interval > 5000) interval = 5000;
  897. else if (interval < 10) interval = 10;
  898. this.interval = interval;
  899. }
  900. if (resend >= 0)
  901. {
  902. fastresend = resend;
  903. }
  904. this.nocwnd = nocwnd;
  905. }
  906. // ikcp_wndsize
  907. public void SetWindowSize(uint sendWindow, uint receiveWindow)
  908. {
  909. if (sendWindow > 0)
  910. {
  911. snd_wnd = sendWindow;
  912. }
  913. if (receiveWindow > 0)
  914. {
  915. // must >= max fragment size
  916. rcv_wnd = Math.Max(receiveWindow, WND_RCV);
  917. }
  918. }
  919. }
  920. }