Kcp.cs 36 KB

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