Kcp.cs 40 KB

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