├── .drone.yml
├── .gitignore
├── .travis.yml
├── CMakeLists.txt
├── LICENSE.md
├── README.md
├── encoding.h
├── fec.cpp
├── fec.h
├── fec_test.cpp
├── galois.cpp
├── galois.h
├── galois_noasm.cpp
├── galois_noasm.h
├── galois_table.c
├── ikcp.c
├── ikcp.h
├── inversion_tree.cpp
├── inversion_tree.h
├── kcp_test.cpp
├── kcpserver.go
├── logo.png
├── matrix.cpp
├── matrix.h
├── reedsolomon.cpp
├── reedsolomon.h
├── sess.cpp
└── sess.h
/.drone.yml:
--------------------------------------------------------------------------------
1 | build:
2 | image: walberla/buildenv-ubuntu-clang:3.6
3 | commands:
4 | - cmake .
5 | - make
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: cpp
3 | compiler:
4 | - clang
5 | script:
6 | - cmake .
7 | - make
8 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.8.7)
2 | project(kcp)
3 |
4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
5 |
6 | set(MAIN_TEST kcp_test.cpp)
7 | set(FEC_TEST fec_test.cpp)
8 | set(SOURCE_FILES ikcp.c sess.cpp galois.cpp galois_noasm.cpp matrix.cpp inversion_tree.cpp reedsolomon.cpp fec.cpp galois_table.c)
9 | add_executable(kcp_test ${SOURCE_FILES} ${MAIN_TEST})
10 | add_executable(fec_test ${SOURCE_FILES} ${FEC_TEST})
11 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Daniel Fu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ***libkcp***
2 |
3 | [![Powered][1]][2] [![Created At][5]][6]
4 |
5 | [1]: https://img.shields.io/badge/KCP-Powered-blue.svg
6 | [2]: https://github.com/skywind3000/kcp
7 | [3]: https://travis-ci.org/xtaci/libkcp.svg?branch=master
8 | [4]: https://travis-ci.org/xtaci/libkcp
9 | [5]: https://img.shields.io/github/created-at/xtaci/kcp-go
10 | [6]: https://img.shields.io/github/created-at/xtaci/kcp-go
11 |
12 | FEC enhanced KCP client session library for iOS/Android, compatible with [kcp-go](https://github.com/xtaci/kcp-go).
13 |
14 |
15 |
16 | ## ***Features***
17 | 1. Optimized for ***Online Games***.
18 | 1. Stream based interface, you can easily switch from your TCP based protocol to libkcp or dualstack.
19 | 1. [FEC(Forward Error Correction)](https://en.wikipedia.org/wiki/Forward_error_correction) Support with [Reed-Solomon Codes](https://en.wikipedia.org/wiki/Reed%E2%80%93Solomon_error_correction)
20 |
21 | ## ***Usage***
22 | libkcp has been designed as a ***Frame-Driven*** library, the main loop was supposed as:
23 | ```
24 | GameInit()
25 | NetworkInit()
26 | While (!isGameOver) Do // e.g: A 30FPS Game
27 | LibKCP.Read()
28 | LibKCP.Write()
29 | LibKCP.Update()
30 | Game.Logic()
31 | Game.Render()
32 | Wait(33ms) // clock
33 | End
34 | ```
35 |
36 | The ```Read/Write/Update``` functions of libkcp are ***guaranteed*** to be ***non-blocking***.
37 | Please read ```kcp_test.cpp fec_test.cpp``` for library usage.
38 |
39 | ## ***Demo***
40 | start echo server(golang)
41 | ```
42 | $go get github.com/xtaci/kcp-go
43 | $go run kcpserver.go
44 | ```
45 | compile and run libkcp(Best with CLion), and watch output.
46 |
47 | ## ***Caveats***
48 | 1. Packet level encryption has not been implemented yet.
49 |
50 | ## ***Troubleshooting***
51 | 1. C++11 is required for compiling.
52 |
--------------------------------------------------------------------------------
/encoding.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2017/1/6.
3 | //
4 |
5 | #ifndef KCP_ENCODING_H
6 | #define KCP_ENCODING_H
7 | #include
8 | //---------------------------------------------------------------------
9 | // WORD ORDER
10 | //---------------------------------------------------------------------
11 | #ifndef IWORDS_BIG_ENDIAN
12 | #ifdef _BIG_ENDIAN_
13 | #if _BIG_ENDIAN_
14 | #define IWORDS_BIG_ENDIAN 1
15 | #endif
16 | #endif
17 | #ifndef IWORDS_BIG_ENDIAN
18 | #if defined(__hppa__) || \
19 | defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
20 | (defined(__MIPS__) && defined(__MISPEB__)) || \
21 | defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
22 | defined(__sparc__) || defined(__powerpc__) || \
23 | defined(__mc68000__) || defined(__s390x__) || defined(__s390__)
24 | #define IWORDS_BIG_ENDIAN 1
25 | #endif
26 | #endif
27 | #ifndef IWORDS_BIG_ENDIAN
28 | #define IWORDS_BIG_ENDIAN 0
29 | #endif
30 | #endif
31 |
32 | /* encode 16 bits unsigned int (lsb) */
33 | inline byte *encode16u(byte *p, uint16_t w)
34 | {
35 | #if IWORDS_BIG_ENDIAN
36 | *(byte*)(p + 0) = (w & 255);
37 | *(byte*)(p + 1) = (w >> 8);
38 | #else
39 | *(unsigned short*)(p) = w;
40 | #endif
41 | p += 2;
42 | return p;
43 | }
44 |
45 | /* Decode 16 bits unsigned int (lsb) */
46 | inline byte *decode16u(byte *p, uint16_t *w)
47 | {
48 | #if IWORDS_BIG_ENDIAN
49 | *w = *(const unsigned char*)(p + 1);
50 | *w = *(const unsigned char*)(p + 0) + (*w << 8);
51 | #else
52 | *w = *(const unsigned short*)p;
53 | #endif
54 | p += 2;
55 | return p;
56 | }
57 |
58 | /* encode 32 bits unsigned int (lsb) */
59 | inline byte *encode32u(byte *p, uint32_t l)
60 | {
61 | #if IWORDS_BIG_ENDIAN
62 | *(unsigned char*)(p + 0) = (unsigned char)((l >> 0) & 0xff);
63 | *(unsigned char*)(p + 1) = (unsigned char)((l >> 8) & 0xff);
64 | *(unsigned char*)(p + 2) = (unsigned char)((l >> 16) & 0xff);
65 | *(unsigned char*)(p + 3) = (unsigned char)((l >> 24) & 0xff);
66 | #else
67 | *(uint32_t*)p = l;
68 | #endif
69 | p += 4;
70 | return p;
71 | }
72 |
73 | /* Decode 32 bits unsigned int (lsb) */
74 | inline byte *decode32u(byte *p, uint32_t *l)
75 | {
76 | #if IWORDS_BIG_ENDIAN
77 | *l = *(const unsigned char*)(p + 3);
78 | *l = *(const unsigned char*)(p + 2) + (*l << 8);
79 | *l = *(const unsigned char*)(p + 1) + (*l << 8);
80 | *l = *(const unsigned char*)(p + 0) + (*l << 8);
81 | #else
82 | *l = *(const uint32_t*)p;
83 | #endif
84 | p += 4;
85 | return p;
86 | }
87 |
88 |
89 | #endif //KCP_ENCODING_H
90 |
--------------------------------------------------------------------------------
/fec.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2017/1/2.
3 | //
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include "fec.h"
10 | #include "sess.h"
11 | #include "encoding.h"
12 |
13 | FEC::FEC(ReedSolomon enc) :enc(enc) {}
14 |
15 | FEC
16 | FEC::New(int rxlimit, int dataShards, int parityShards) {
17 | if (dataShards <= 0 || parityShards <= 0) {
18 | throw std::invalid_argument("invalid arguments");
19 | }
20 |
21 | if (rxlimit < dataShards+parityShards) {
22 | throw std::invalid_argument("invalid arguments");
23 | }
24 |
25 | FEC fec(ReedSolomon::New(dataShards, parityShards));
26 | fec.rxlimit = rxlimit;
27 | fec.dataShards = dataShards;
28 | fec.parityShards = parityShards;
29 | fec.totalShards = dataShards + parityShards;
30 | fec.paws = (0xffffffff/uint32_t(fec.totalShards) - 1) * uint32_t(fec.totalShards);
31 |
32 | return fec;
33 | }
34 |
35 | fecPacket
36 | FEC::Decode(byte *data, size_t sz) {
37 | fecPacket pkt;
38 | data = decode32u(data, &pkt.seqid);
39 | data = decode16u(data, &pkt.flag);
40 | struct timeval time;
41 | gettimeofday(&time, NULL);
42 | pkt.ts = uint32_t(time.tv_sec * 1000 + time.tv_usec/1000);
43 | pkt.data = std::make_shared>(data, data+sz - fecHeaderSize);
44 | return pkt;
45 | }
46 |
47 | void
48 | FEC::MarkData(byte *data, uint16_t sz) {
49 | data = encode32u(data,this->next);
50 | data = encode16u(data,typeData);
51 | encode16u(data,static_cast(sz + 2)); // including size itself
52 | this->next++;
53 | }
54 |
55 | void
56 | FEC::MarkFEC(byte *data) {
57 | data = encode32u(data,this->next);
58 | encode16u(data,typeFEC);
59 | this->next++;
60 | if (this->next >= this->paws) { // paws would only occurs in MarkFEC
61 | this->next = 0;
62 | }
63 | }
64 |
65 | std::vector
66 | FEC::Input(fecPacket &pkt) {
67 | std::vector recovered;
68 |
69 | uint32_t now = currentMs();
70 | if (now-lastCheck >= fecExpire) {
71 | for (auto it = rx.begin();it !=rx.end();) {
72 | if (now - it->ts > fecExpire) {
73 | it = rx.erase(it);
74 | } else {
75 | it++;
76 | }
77 | }
78 | lastCheck = now;
79 | }
80 |
81 |
82 | // insertion
83 | auto n = this->rx.size() -1;
84 | int insertIdx = 0;
85 | for (int i=n;i>=0;i--) {
86 | if (pkt.seqid == rx[i].seqid) {
87 | return recovered;
88 | } else if (pkt.seqid > rx[i].seqid) {
89 | insertIdx = i + 1;
90 | break;
91 | }
92 | }
93 | // insert into ordered rx queue
94 | rx.insert(rx.begin()+insertIdx, pkt);
95 |
96 | // shard range for current packet
97 | auto shardBegin = pkt.seqid - pkt.seqid%totalShards;
98 | auto shardEnd = shardBegin + totalShards - 1;
99 |
100 | // max search range in ordered queue for current shard
101 | auto searchBegin = insertIdx - int(pkt.seqid%totalShards);
102 | if (searchBegin < 0) {
103 | searchBegin = 0;
104 | }
105 |
106 | auto searchEnd = searchBegin + totalShards - 1;
107 | if (searchEnd >= rx.size()) {
108 | searchEnd = rx.size()-1;
109 | }
110 |
111 | if (searchEnd > searchBegin && searchEnd-searchBegin+1 >= dataShards) {
112 | int numshard = 0;
113 | int numDataShard = 0;
114 | int first = 0;
115 | size_t maxlen = 0;
116 |
117 | std::vector shardVec(totalShards);
118 | std::vector shardflag(totalShards, false);
119 |
120 | for (auto i = searchBegin; i <= searchEnd; i++) {
121 | auto seqid = rx[i].seqid;
122 | if (seqid > shardEnd) {
123 | break;
124 | } else if (seqid >= shardBegin) {
125 | shardVec[seqid%totalShards] = rx[i].data;
126 | shardflag[seqid%totalShards] = true;
127 | numshard++;
128 | if (rx[i].flag == typeData) {
129 | numDataShard++;
130 | }
131 | if (numshard == 1) {
132 | first = i;
133 | }
134 | if (rx[i].data->size() > maxlen) {
135 | maxlen = rx[i].data->size();
136 | }
137 | }
138 | }
139 |
140 | if (numDataShard == dataShards) { // no lost
141 | rx.erase(rx.begin()+first, rx.begin() + first+numshard);
142 | } else if (numshard >= dataShards) { // recoverable
143 | // equally resized
144 | for (int i=0;iresize(maxlen, 0);
147 | }
148 | }
149 |
150 | // reconstruct shards
151 | enc.Reconstruct(shardVec);
152 | for (int k =0;k rxlimit) {
163 | rx.erase(rx.begin());
164 | }
165 |
166 | return recovered;
167 | }
168 |
169 |
170 | void FEC::Encode(std::vector &shards) {
171 | // resize elements with 0 appending
172 | size_t max = 0;
173 | for (int i = 0;isize() > max) {
175 | max = shards[i]->size();
176 | }
177 | }
178 |
179 | for ( auto &s : shards) {
180 | if (s == nullptr) {
181 | s = std::make_shared>(max);
182 | } else {
183 | s->resize(max, 0);
184 | }
185 | }
186 |
187 | enc.Encode(shards);
188 | }
189 |
--------------------------------------------------------------------------------
/fec.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2017/1/2.
3 | //
4 |
5 | #ifndef KCP_FEC_H
6 | #define KCP_FEC_H
7 |
8 |
9 | #include
10 | #include
11 | #include
12 | #include "reedsolomon.h"
13 |
14 | const size_t fecHeaderSize = 6;
15 | const size_t fecHeaderSizePlus2{fecHeaderSize + 2};
16 | const uint16_t typeData = 0xf1;
17 | const uint16_t typeFEC = 0xf2;
18 | const int fecExpire = 30000;
19 |
20 | class fecPacket {
21 | public:
22 | uint32_t seqid;
23 | uint16_t flag;
24 | row_type data;
25 | uint32_t ts;
26 | };
27 |
28 | class FEC {
29 | public:
30 | FEC() = default;
31 | FEC(ReedSolomon enc);
32 |
33 | static FEC New(int rxlimit, int dataShards, int parityShards);
34 |
35 | inline bool isEnabled() { return dataShards > 0 && parityShards > 0 ; }
36 |
37 | // Input a FEC packet, and return recovered data if possible.
38 | std::vector Input(fecPacket &pkt);
39 |
40 | // Calc Parity Shards
41 | void Encode(std::vector &shards);
42 |
43 | // Decode a raw array into fecPacket
44 | static fecPacket Decode(byte *data, size_t sz);
45 |
46 | // Mark raw array as typeData, and write correct size.
47 | void MarkData(byte *data, uint16_t sz);
48 |
49 | // Mark raw array as typeFEC
50 | void MarkFEC(byte *data);
51 | private:
52 | std::vector rx; // ordered receive queue
53 | int rxlimit; // queue empty limit
54 | int dataShards;
55 | int parityShards;
56 | int totalShards;
57 | uint32_t next{0}; // next seqid
58 | ReedSolomon enc;
59 | uint32_t paws; // Protect Against Wrapped Sequence numbers
60 | uint32_t lastCheck{0};
61 | };
62 |
63 |
64 | #endif //KCP_FEC_H
65 |
--------------------------------------------------------------------------------
/fec_test.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2017/1/4.
3 | //
4 | #include
5 | #include
6 | #include "fec.h"
7 |
8 |
9 | int main() {
10 | struct timeval time;
11 | gettimeofday(&time, NULL);
12 | srand((time.tv_sec * 1000) + (time.tv_usec / 1000));
13 |
14 | int datashard = 5;
15 | int parityshard = 3;
16 | int totalshard = datashard + parityshard;
17 |
18 | FEC fec = FEC::New(20, datashard, parityshard);
19 |
20 | byte arr[] = {0, 0, 0};
21 | std::vector shards(totalshard);
22 | for (int i = 0; i < datashard; i++) {
23 | for (int j = 0; j < 3; j++) {
24 | arr[j] = byte(rand() % 255);
25 | }
26 | shards[i] = std::make_shared>(arr, &arr[3]);
27 | }
28 |
29 | for (int i = datashard; i < totalshard; i++) {
30 | shards[i] = std::make_shared>(3);
31 | }
32 |
33 | std::cout << "shards to encode:" << std::endl;
34 | for (int i = 0; i < shards.size(); i++) {
35 | for (auto b : *shards[i]) {
36 | std::cout << int(b) << " ";
37 | }
38 | std::cout << std::endl;
39 | }
40 |
41 | fec.Encode(shards);
42 | std::cout << "encoded:" << std::endl;
43 | for (int i = 0; i < shards.size(); i++) {
44 | for (auto b : *shards[i]) {
45 | std::cout << int(b) << " ";
46 | }
47 | std::cout << std::endl;
48 | }
49 |
50 | std::cout << "remove 2 datashards & 1 parityshards:" << std::endl;
51 | for (int i = 0; i < shards.size(); i++) {
52 | if (i < 2 || i == 5) {
53 | continue;
54 | }
55 |
56 | fecPacket pkt;
57 | pkt.data = shards[i];
58 | pkt.seqid = i;
59 | if (i < 5) {
60 | pkt.flag = typeData;
61 | } else {
62 | pkt.flag = typeFEC;
63 | }
64 | auto recovered = fec.Input(pkt);
65 |
66 | if (recovered.size() > 0) {
67 | std::cout << "recovered:" << std::endl;
68 | for (int i = 0; i < recovered.size(); i++) {
69 | for (auto b : *recovered[i]) {
70 | std::cout << int(b) << " ";
71 | }
72 | std::cout << std::endl;
73 | }
74 | }
75 | }
76 |
77 | std::cout << "encoding 2nd Round:" << std::endl;
78 |
79 | for (int i = 0; i < datashard; i++) {
80 | for (int j = 0; j < 3; j++) {
81 | arr[j] = byte(rand() % 255);
82 | }
83 | shards[i] = std::make_shared>(arr, &arr[3]);
84 | }
85 |
86 | for (int i = datashard; i < totalshard; i++) {
87 | shards[i] = std::make_shared>(3);
88 | }
89 |
90 | std::cout << "shards to encode:" << std::endl;
91 | for (int i = 0; i < shards.size(); i++) {
92 | for (auto b : *shards[i]) {
93 | std::cout << int(b) << " ";
94 | }
95 | std::cout << std::endl;
96 | }
97 | fec.Encode(shards);
98 | std::cout << "encoded:" << std::endl;
99 | for (int i = 0; i < shards.size(); i++) {
100 | for (auto b : *shards[i]) {
101 | std::cout << int(b) << " ";
102 | }
103 | std::cout << std::endl;
104 | }
105 |
106 | std::cout << "remove 2 datashards & 1 parityshards:" << std::endl;
107 | for (int i = 0; i < shards.size(); i++) {
108 | if (i < 2 || i == 5) {
109 | continue;
110 | }
111 |
112 | fecPacket pkt;
113 | pkt.data = shards[i];
114 | pkt.seqid = i;
115 | if (i < 5) {
116 | pkt.flag = typeData;
117 | } else {
118 | pkt.flag = typeFEC;
119 | }
120 | auto recovered = fec.Input(pkt);
121 |
122 | if (recovered.size() > 0) {
123 | std::cout << "recovered:" << std::endl;
124 | for (int i = 0; i < recovered.size(); i++) {
125 | for (auto b : *recovered[i]) {
126 | std::cout << int(b) << " ";
127 | }
128 | std::cout << std::endl;
129 | }
130 | }
131 | }
132 | }
133 |
134 |
--------------------------------------------------------------------------------
/galois.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2016/12/30.
3 | //
4 |
5 | #include
6 | #include "galois.h"
7 |
8 | extern const int fieldSize;
9 | extern byte mulTable[256][256];
10 | extern const byte logTable[];
11 | extern byte expTable[];
12 |
13 | byte galAdd(byte a, byte b) {
14 | return a ^ b;
15 | }
16 |
17 | byte galSub(byte a, byte b) {
18 | return a ^ b;
19 | }
20 |
21 | byte galMultiply(byte a, byte b) {
22 | return mulTable[a][b];
23 | }
24 |
25 | byte galDivide(byte a, byte b) {
26 | if (a == 0) {
27 | return 0;
28 | }
29 |
30 | if (b == 0) {
31 | throw std::invalid_argument("Argument 'divisor' is 0");
32 | }
33 |
34 | int logA = logTable[a];
35 | int logB = logTable[b];
36 | int logResult = logA - logB;
37 | if (logResult < 0) {
38 | logResult += 255;
39 | }
40 | return expTable[logResult];
41 | }
42 |
43 | byte galExp(byte a, byte n) {
44 | if (n == 0) {
45 | return 1;
46 | }
47 | if (a == 0) {
48 | return 0;
49 | }
50 |
51 | int logA = logTable[a];
52 | int logResult = logA * n;
53 | while (logResult >= 255) {
54 | logResult -= 255;
55 | }
56 | return expTable[logResult];
57 | }
--------------------------------------------------------------------------------
/galois.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2016/12/30.
3 | //
4 |
5 | #ifndef KCP_GALOIS_H
6 | #define KCP_GALOIS_H
7 |
8 | typedef unsigned char byte;
9 |
10 | byte galAdd(byte a, byte b);
11 |
12 | byte galSub(byte a, byte b);
13 |
14 | // galMultiply multiplies to elements of the field.
15 | // Uses lookup table ~40% faster
16 | byte galMultiply(byte a, byte b);
17 |
18 | // galDivide is inverse of galMultiply.
19 | byte galDivide(byte a, byte b);
20 |
21 | // Computes a**n.
22 | //
23 | // The result will be the same as multiplying a times itself n times.
24 | byte galExp(byte a, byte b);
25 |
26 | #endif //KCP_GALOIS_H
27 |
--------------------------------------------------------------------------------
/galois_noasm.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2016/12/30.
3 | //
4 |
5 | #include "galois_noasm.h"
6 | #include "matrix.h"
7 |
8 | extern const byte mulTable[256][256];
9 |
10 | void galMulSlice(byte c, row_type in, row_type out) {
11 | for (int n=0;nsize();n++) {
12 | (*out)[n] = mulTable[c][(*in)[n]];
13 | }
14 | }
15 |
16 | void galMulSliceXor(byte c, row_type in, row_type out) {
17 | for (int n=0;nsize();n++) {
18 | (*out)[n] ^= mulTable[c][(*in)[n]];
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/galois_noasm.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2016/12/30.
3 | //
4 |
5 | #ifndef KCP_GALOIS_NOASM_H
6 | #define KCP_GALOIS_NOASM_H
7 |
8 | #include "galois.h"
9 | #include "matrix.h"
10 |
11 | #ifdef __cplusplus
12 | extern "C" {
13 | #endif
14 | void galMulSlice(byte c, row_type in, row_type out);
15 | void galMulSliceXor(byte c, row_type in, row_type out);
16 |
17 | #ifdef __cplusplus
18 | }
19 | #endif
20 |
21 | #endif //KCP_GALOIS_NOASM_H
22 |
--------------------------------------------------------------------------------
/ikcp.c:
--------------------------------------------------------------------------------
1 | //=====================================================================
2 | //
3 | // KCP - A Better ARQ Protocol Implementation
4 | // skywind3000 (at) gmail.com, 2010-2011
5 | //
6 | // Features:
7 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
8 | // + Maximum RTT reduce three times vs tcp.
9 | // + Lightweight, distributed as a single source file.
10 | //
11 | //=====================================================================
12 | #include "ikcp.h"
13 |
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 |
21 |
22 | //=====================================================================
23 | // KCP BASIC
24 | //=====================================================================
25 | const IUINT32 IKCP_RTO_NDL = 30; // no delay min rto
26 | const IUINT32 IKCP_RTO_MIN = 100; // normal min rto
27 | const IUINT32 IKCP_RTO_DEF = 200;
28 | const IUINT32 IKCP_RTO_MAX = 60000;
29 | const IUINT32 IKCP_CMD_PUSH = 81; // cmd: push data
30 | const IUINT32 IKCP_CMD_ACK = 82; // cmd: ack
31 | const IUINT32 IKCP_CMD_WASK = 83; // cmd: window probe (ask)
32 | const IUINT32 IKCP_CMD_WINS = 84; // cmd: window size (tell)
33 | const IUINT32 IKCP_ASK_SEND = 1; // need to send IKCP_CMD_WASK
34 | const IUINT32 IKCP_ASK_TELL = 2; // need to send IKCP_CMD_WINS
35 | const IUINT32 IKCP_WND_SND = 32;
36 | const IUINT32 IKCP_WND_RCV = 32;
37 | const IUINT32 IKCP_MTU_DEF = 1400;
38 | const IUINT32 IKCP_ACK_FAST = 3;
39 | const IUINT32 IKCP_INTERVAL = 100;
40 | const IUINT32 IKCP_OVERHEAD = 24;
41 | const IUINT32 IKCP_DEADLINK = 20;
42 | const IUINT32 IKCP_THRESH_INIT = 2;
43 | const IUINT32 IKCP_THRESH_MIN = 2;
44 | const IUINT32 IKCP_PROBE_INIT = 7000; // 7 secs to probe window size
45 | const IUINT32 IKCP_PROBE_LIMIT = 120000; // up to 120 secs to probe window
46 |
47 |
48 | //---------------------------------------------------------------------
49 | // encode / decode
50 | //---------------------------------------------------------------------
51 |
52 | /* encode 8 bits unsigned int */
53 | static inline char *ikcp_encode8u(char *p, unsigned char c)
54 | {
55 | *(unsigned char*)p++ = c;
56 | return p;
57 | }
58 |
59 | /* decode 8 bits unsigned int */
60 | static inline const char *ikcp_decode8u(const char *p, unsigned char *c)
61 | {
62 | *c = *(unsigned char*)p++;
63 | return p;
64 | }
65 |
66 | /* encode 16 bits unsigned int (lsb) */
67 | static inline char *ikcp_encode16u(char *p, unsigned short w)
68 | {
69 | #if IWORDS_BIG_ENDIAN
70 | *(unsigned char*)(p + 0) = (w & 255);
71 | *(unsigned char*)(p + 1) = (w >> 8);
72 | #else
73 | *(unsigned short*)(p) = w;
74 | #endif
75 | p += 2;
76 | return p;
77 | }
78 |
79 | /* decode 16 bits unsigned int (lsb) */
80 | static inline const char *ikcp_decode16u(const char *p, unsigned short *w)
81 | {
82 | #if IWORDS_BIG_ENDIAN
83 | *w = *(const unsigned char*)(p + 1);
84 | *w = *(const unsigned char*)(p + 0) + (*w << 8);
85 | #else
86 | *w = *(const unsigned short*)p;
87 | #endif
88 | p += 2;
89 | return p;
90 | }
91 |
92 | /* encode 32 bits unsigned int (lsb) */
93 | static inline char *ikcp_encode32u(char *p, IUINT32 l)
94 | {
95 | #if IWORDS_BIG_ENDIAN
96 | *(unsigned char*)(p + 0) = (unsigned char)((l >> 0) & 0xff);
97 | *(unsigned char*)(p + 1) = (unsigned char)((l >> 8) & 0xff);
98 | *(unsigned char*)(p + 2) = (unsigned char)((l >> 16) & 0xff);
99 | *(unsigned char*)(p + 3) = (unsigned char)((l >> 24) & 0xff);
100 | #else
101 | *(IUINT32*)p = l;
102 | #endif
103 | p += 4;
104 | return p;
105 | }
106 |
107 | /* decode 32 bits unsigned int (lsb) */
108 | static inline const char *ikcp_decode32u(const char *p, IUINT32 *l)
109 | {
110 | #if IWORDS_BIG_ENDIAN
111 | *l = *(const unsigned char*)(p + 3);
112 | *l = *(const unsigned char*)(p + 2) + (*l << 8);
113 | *l = *(const unsigned char*)(p + 1) + (*l << 8);
114 | *l = *(const unsigned char*)(p + 0) + (*l << 8);
115 | #else
116 | *l = *(const IUINT32*)p;
117 | #endif
118 | p += 4;
119 | return p;
120 | }
121 |
122 | static inline IUINT32 _imin_(IUINT32 a, IUINT32 b) {
123 | return a <= b ? a : b;
124 | }
125 |
126 | static inline IUINT32 _imax_(IUINT32 a, IUINT32 b) {
127 | return a >= b ? a : b;
128 | }
129 |
130 | static inline IUINT32 _ibound_(IUINT32 lower, IUINT32 middle, IUINT32 upper)
131 | {
132 | return _imin_(_imax_(lower, middle), upper);
133 | }
134 |
135 | static inline long _itimediff(IUINT32 later, IUINT32 earlier)
136 | {
137 | return ((IINT32)(later - earlier));
138 | }
139 |
140 | //---------------------------------------------------------------------
141 | // manage segment
142 | //---------------------------------------------------------------------
143 | typedef struct IKCPSEG IKCPSEG;
144 |
145 | static void* (*ikcp_malloc_hook)(size_t) = NULL;
146 | static void (*ikcp_free_hook)(void *) = NULL;
147 |
148 | // internal malloc
149 | static void* ikcp_malloc(size_t size) {
150 | if (ikcp_malloc_hook)
151 | return ikcp_malloc_hook(size);
152 | return malloc(size);
153 | }
154 |
155 | // internal free
156 | static void ikcp_free(void *ptr) {
157 | if (ikcp_free_hook) {
158 | ikcp_free_hook(ptr);
159 | } else {
160 | free(ptr);
161 | }
162 | }
163 |
164 | // redefine allocator
165 | void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*))
166 | {
167 | ikcp_malloc_hook = new_malloc;
168 | ikcp_free_hook = new_free;
169 | }
170 |
171 | // allocate a new kcp segment
172 | static IKCPSEG* ikcp_segment_new(ikcpcb *kcp, int size)
173 | {
174 | return (IKCPSEG*)ikcp_malloc(sizeof(IKCPSEG) + size);
175 | }
176 |
177 | // delete a segment
178 | static void ikcp_segment_delete(ikcpcb *kcp, IKCPSEG *seg)
179 | {
180 | ikcp_free(seg);
181 | }
182 |
183 | // write log
184 | void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...)
185 | {
186 | char buffer[1024];
187 | va_list argptr;
188 | if ((mask & kcp->logmask) == 0 || kcp->writelog == 0) return;
189 | va_start(argptr, fmt);
190 | vsprintf(buffer, fmt, argptr);
191 | va_end(argptr);
192 | kcp->writelog(buffer, kcp, kcp->user);
193 | }
194 |
195 | // check log mask
196 | static int ikcp_canlog(const ikcpcb *kcp, int mask)
197 | {
198 | if ((mask & kcp->logmask) == 0 || kcp->writelog == NULL) return 0;
199 | return 1;
200 | }
201 |
202 | // output segment
203 | static int ikcp_output(ikcpcb *kcp, const void *data, int size)
204 | {
205 | assert(kcp);
206 | assert(kcp->output);
207 | if (ikcp_canlog(kcp, IKCP_LOG_OUTPUT)) {
208 | ikcp_log(kcp, IKCP_LOG_OUTPUT, "[RO] %ld bytes", (long)size);
209 | }
210 | if (size == 0) return 0;
211 | return kcp->output((const char*)data, size, kcp, kcp->user);
212 | }
213 |
214 | // output queue
215 | void ikcp_qprint(const char *name, const struct IQUEUEHEAD *head)
216 | {
217 | #if 0
218 | const struct IQUEUEHEAD *p;
219 | printf("<%s>: [", name);
220 | for (p = head->next; p != head; p = p->next) {
221 | const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node);
222 | printf("(%lu %d)", (unsigned long)seg->sn, (int)(seg->ts % 10000));
223 | if (p->next != head) printf(",");
224 | }
225 | printf("]\n");
226 | #endif
227 | }
228 |
229 |
230 | //---------------------------------------------------------------------
231 | // create a new kcpcb
232 | //---------------------------------------------------------------------
233 | ikcpcb* ikcp_create(IUINT32 conv, void *user)
234 | {
235 | ikcpcb *kcp = (ikcpcb*)ikcp_malloc(sizeof(struct IKCPCB));
236 | if (kcp == NULL) return NULL;
237 | kcp->conv = conv;
238 | kcp->user = user;
239 | kcp->snd_una = 0;
240 | kcp->snd_nxt = 0;
241 | kcp->rcv_nxt = 0;
242 | kcp->ts_recent = 0;
243 | kcp->ts_lastack = 0;
244 | kcp->ts_probe = 0;
245 | kcp->probe_wait = 0;
246 | kcp->snd_wnd = IKCP_WND_SND;
247 | kcp->rcv_wnd = IKCP_WND_RCV;
248 | kcp->rmt_wnd = IKCP_WND_RCV;
249 | kcp->cwnd = 0;
250 | kcp->incr = 0;
251 | kcp->probe = 0;
252 | kcp->mtu = IKCP_MTU_DEF;
253 | kcp->mss = kcp->mtu - IKCP_OVERHEAD;
254 | kcp->stream = 0;
255 |
256 | kcp->buffer = (char*)ikcp_malloc((kcp->mtu + IKCP_OVERHEAD) * 3);
257 | if (kcp->buffer == NULL) {
258 | ikcp_free(kcp);
259 | return NULL;
260 | }
261 |
262 | iqueue_init(&kcp->snd_queue);
263 | iqueue_init(&kcp->rcv_queue);
264 | iqueue_init(&kcp->snd_buf);
265 | iqueue_init(&kcp->rcv_buf);
266 | kcp->nrcv_buf = 0;
267 | kcp->nsnd_buf = 0;
268 | kcp->nrcv_que = 0;
269 | kcp->nsnd_que = 0;
270 | kcp->state = 0;
271 | kcp->acklist = NULL;
272 | kcp->ackblock = 0;
273 | kcp->ackcount = 0;
274 | kcp->rx_srtt = 0;
275 | kcp->rx_rttval = 0;
276 | kcp->rx_rto = IKCP_RTO_DEF;
277 | kcp->rx_minrto = IKCP_RTO_MIN;
278 | kcp->current = 0;
279 | kcp->interval = IKCP_INTERVAL;
280 | kcp->ts_flush = IKCP_INTERVAL;
281 | kcp->nodelay = 0;
282 | kcp->updated = 0;
283 | kcp->logmask = 0;
284 | kcp->ssthresh = IKCP_THRESH_INIT;
285 | kcp->fastresend = 0;
286 | kcp->nocwnd = 0;
287 | kcp->xmit = 0;
288 | kcp->dead_link = IKCP_DEADLINK;
289 | kcp->output = NULL;
290 | kcp->writelog = NULL;
291 |
292 | return kcp;
293 | }
294 |
295 |
296 | //---------------------------------------------------------------------
297 | // release a new kcpcb
298 | //---------------------------------------------------------------------
299 | void ikcp_release(ikcpcb *kcp)
300 | {
301 | assert(kcp);
302 | if (kcp) {
303 | IKCPSEG *seg;
304 | while (!iqueue_is_empty(&kcp->snd_buf)) {
305 | seg = iqueue_entry(kcp->snd_buf.next, IKCPSEG, node);
306 | iqueue_del(&seg->node);
307 | ikcp_segment_delete(kcp, seg);
308 | }
309 | while (!iqueue_is_empty(&kcp->rcv_buf)) {
310 | seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node);
311 | iqueue_del(&seg->node);
312 | ikcp_segment_delete(kcp, seg);
313 | }
314 | while (!iqueue_is_empty(&kcp->snd_queue)) {
315 | seg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node);
316 | iqueue_del(&seg->node);
317 | ikcp_segment_delete(kcp, seg);
318 | }
319 | while (!iqueue_is_empty(&kcp->rcv_queue)) {
320 | seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node);
321 | iqueue_del(&seg->node);
322 | ikcp_segment_delete(kcp, seg);
323 | }
324 | if (kcp->buffer) {
325 | ikcp_free(kcp->buffer);
326 | }
327 | if (kcp->acklist) {
328 | ikcp_free(kcp->acklist);
329 | }
330 |
331 | kcp->nrcv_buf = 0;
332 | kcp->nsnd_buf = 0;
333 | kcp->nrcv_que = 0;
334 | kcp->nsnd_que = 0;
335 | kcp->ackcount = 0;
336 | kcp->buffer = NULL;
337 | kcp->acklist = NULL;
338 | ikcp_free(kcp);
339 | }
340 | }
341 |
342 |
343 | //---------------------------------------------------------------------
344 | // set output callback, which will be invoked by kcp
345 | //---------------------------------------------------------------------
346 | void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len,
347 | ikcpcb *kcp, void *user))
348 | {
349 | kcp->output = output;
350 | }
351 |
352 |
353 | //---------------------------------------------------------------------
354 | // user/upper level recv: returns size, returns below zero for EAGAIN
355 | //---------------------------------------------------------------------
356 | int ikcp_recv(ikcpcb *kcp, char *buffer, int len)
357 | {
358 | struct IQUEUEHEAD *p;
359 | int ispeek = (len < 0)? 1 : 0;
360 | int peeksize;
361 | int recover = 0;
362 | IKCPSEG *seg;
363 | assert(kcp);
364 |
365 | if (iqueue_is_empty(&kcp->rcv_queue))
366 | return -1;
367 |
368 | if (len < 0) len = -len;
369 |
370 | peeksize = ikcp_peeksize(kcp);
371 |
372 | if (peeksize < 0)
373 | return -2;
374 |
375 | if (peeksize > len)
376 | return -3;
377 |
378 | if (kcp->nrcv_que >= kcp->rcv_wnd)
379 | recover = 1;
380 |
381 | // merge fragment
382 | for (len = 0, p = kcp->rcv_queue.next; p != &kcp->rcv_queue; ) {
383 | int fragment;
384 | seg = iqueue_entry(p, IKCPSEG, node);
385 | p = p->next;
386 |
387 | if (buffer) {
388 | memcpy(buffer, seg->data, seg->len);
389 | buffer += seg->len;
390 | }
391 |
392 | len += seg->len;
393 | fragment = seg->frg;
394 |
395 | if (ikcp_canlog(kcp, IKCP_LOG_RECV)) {
396 | ikcp_log(kcp, IKCP_LOG_RECV, "recv sn=%lu", seg->sn);
397 | }
398 |
399 | if (ispeek == 0) {
400 | iqueue_del(&seg->node);
401 | ikcp_segment_delete(kcp, seg);
402 | kcp->nrcv_que--;
403 | }
404 |
405 | if (fragment == 0)
406 | break;
407 | }
408 |
409 | assert(len == peeksize);
410 |
411 | // move available data from rcv_buf -> rcv_queue
412 | while (! iqueue_is_empty(&kcp->rcv_buf)) {
413 | IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node);
414 | if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) {
415 | iqueue_del(&seg->node);
416 | kcp->nrcv_buf--;
417 | iqueue_add_tail(&seg->node, &kcp->rcv_queue);
418 | kcp->nrcv_que++;
419 | kcp->rcv_nxt++;
420 | } else {
421 | break;
422 | }
423 | }
424 |
425 | // fast recover
426 | if (kcp->nrcv_que < kcp->rcv_wnd && recover) {
427 | // ready to send back IKCP_CMD_WINS in ikcp_flush
428 | // tell remote my window size
429 | kcp->probe |= IKCP_ASK_TELL;
430 | }
431 |
432 | return len;
433 | }
434 |
435 |
436 | //---------------------------------------------------------------------
437 | // peek data size
438 | //---------------------------------------------------------------------
439 | int ikcp_peeksize(const ikcpcb *kcp)
440 | {
441 | struct IQUEUEHEAD *p;
442 | IKCPSEG *seg;
443 | int length = 0;
444 |
445 | assert(kcp);
446 |
447 | if (iqueue_is_empty(&kcp->rcv_queue)) return -1;
448 |
449 | seg = iqueue_entry(kcp->rcv_queue.next, IKCPSEG, node);
450 | if (seg->frg == 0) return seg->len;
451 |
452 | if (kcp->nrcv_que < seg->frg + 1) return -1;
453 |
454 | for (p = kcp->rcv_queue.next; p != &kcp->rcv_queue; p = p->next) {
455 | seg = iqueue_entry(p, IKCPSEG, node);
456 | length += seg->len;
457 | if (seg->frg == 0) break;
458 | }
459 |
460 | return length;
461 | }
462 |
463 |
464 | //---------------------------------------------------------------------
465 | // user/upper level send, returns below zero for error
466 | //---------------------------------------------------------------------
467 | int ikcp_send(ikcpcb *kcp, const char *buffer, int len)
468 | {
469 | IKCPSEG *seg;
470 | int count, i;
471 |
472 | assert(kcp->mss > 0);
473 | if (len < 0) return -1;
474 |
475 | // append to previous segment in streaming mode (if possible)
476 | if (kcp->stream != 0) {
477 | if (!iqueue_is_empty(&kcp->snd_queue)) {
478 | IKCPSEG *old = iqueue_entry(kcp->snd_queue.prev, IKCPSEG, node);
479 | if (old->len < kcp->mss) {
480 | int capacity = kcp->mss - old->len;
481 | int extend = (len < capacity)? len : capacity;
482 | seg = ikcp_segment_new(kcp, old->len + extend);
483 | assert(seg);
484 | if (seg == NULL) {
485 | return -2;
486 | }
487 | iqueue_add_tail(&seg->node, &kcp->snd_queue);
488 | memcpy(seg->data, old->data, old->len);
489 | if (buffer) {
490 | memcpy(seg->data + old->len, buffer, extend);
491 | buffer += extend;
492 | }
493 | seg->len = old->len + extend;
494 | seg->frg = 0;
495 | len -= extend;
496 | iqueue_del_init(&old->node);
497 | ikcp_segment_delete(kcp, old);
498 | }
499 | }
500 | if (len <= 0) {
501 | return 0;
502 | }
503 | }
504 |
505 | if (len <= (int)kcp->mss) count = 1;
506 | else count = (len + kcp->mss - 1) / kcp->mss;
507 |
508 | if (count > 255) return -2;
509 |
510 | if (count == 0) count = 1;
511 |
512 | // fragment
513 | for (i = 0; i < count; i++) {
514 | int size = len > (int)kcp->mss ? (int)kcp->mss : len;
515 | seg = ikcp_segment_new(kcp, size);
516 | assert(seg);
517 | if (seg == NULL) {
518 | return -2;
519 | }
520 | if (buffer && len > 0) {
521 | memcpy(seg->data, buffer, size);
522 | }
523 | seg->len = size;
524 | seg->frg = (kcp->stream == 0)? (count - i - 1) : 0;
525 | iqueue_init(&seg->node);
526 | iqueue_add_tail(&seg->node, &kcp->snd_queue);
527 | kcp->nsnd_que++;
528 | if (buffer) {
529 | buffer += size;
530 | }
531 | len -= size;
532 | }
533 |
534 | return 0;
535 | }
536 |
537 |
538 | //---------------------------------------------------------------------
539 | // parse ack
540 | //---------------------------------------------------------------------
541 | static void ikcp_update_ack(ikcpcb *kcp, IINT32 rtt)
542 | {
543 | IINT32 rto = 0;
544 | if (kcp->rx_srtt == 0) {
545 | kcp->rx_srtt = rtt;
546 | kcp->rx_rttval = rtt / 2;
547 | } else {
548 | long delta = rtt - kcp->rx_srtt;
549 | if (delta < 0) delta = -delta;
550 | kcp->rx_rttval = (3 * kcp->rx_rttval + delta) / 4;
551 | kcp->rx_srtt = (7 * kcp->rx_srtt + rtt) / 8;
552 | if (kcp->rx_srtt < 1) kcp->rx_srtt = 1;
553 | }
554 | rto = kcp->rx_srtt + _imax_(kcp->interval, 4 * kcp->rx_rttval);
555 | kcp->rx_rto = _ibound_(kcp->rx_minrto, rto, IKCP_RTO_MAX);
556 | }
557 |
558 | static void ikcp_shrink_buf(ikcpcb *kcp)
559 | {
560 | struct IQUEUEHEAD *p = kcp->snd_buf.next;
561 | if (p != &kcp->snd_buf) {
562 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node);
563 | kcp->snd_una = seg->sn;
564 | } else {
565 | kcp->snd_una = kcp->snd_nxt;
566 | }
567 | }
568 |
569 | static void ikcp_parse_ack(ikcpcb *kcp, IUINT32 sn)
570 | {
571 | struct IQUEUEHEAD *p, *next;
572 |
573 | if (_itimediff(sn, kcp->snd_una) < 0 || _itimediff(sn, kcp->snd_nxt) >= 0)
574 | return;
575 |
576 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) {
577 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node);
578 | next = p->next;
579 | if (sn == seg->sn) {
580 | iqueue_del(p);
581 | ikcp_segment_delete(kcp, seg);
582 | kcp->nsnd_buf--;
583 | break;
584 | }
585 | if (_itimediff(sn, seg->sn) < 0) {
586 | break;
587 | }
588 | }
589 | }
590 |
591 | static void ikcp_parse_una(ikcpcb *kcp, IUINT32 una)
592 | {
593 | struct IQUEUEHEAD *p, *next;
594 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) {
595 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node);
596 | next = p->next;
597 | if (_itimediff(una, seg->sn) > 0) {
598 | iqueue_del(p);
599 | ikcp_segment_delete(kcp, seg);
600 | kcp->nsnd_buf--;
601 | } else {
602 | break;
603 | }
604 | }
605 | }
606 |
607 | static void ikcp_parse_fastack(ikcpcb *kcp, IUINT32 sn)
608 | {
609 | struct IQUEUEHEAD *p, *next;
610 |
611 | if (_itimediff(sn, kcp->snd_una) < 0 || _itimediff(sn, kcp->snd_nxt) >= 0)
612 | return;
613 |
614 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = next) {
615 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node);
616 | next = p->next;
617 | if (_itimediff(sn, seg->sn) < 0) {
618 | break;
619 | }
620 | else if (sn != seg->sn) {
621 | seg->fastack++;
622 | }
623 | }
624 | }
625 |
626 |
627 | //---------------------------------------------------------------------
628 | // ack append
629 | //---------------------------------------------------------------------
630 | static void ikcp_ack_push(ikcpcb *kcp, IUINT32 sn, IUINT32 ts)
631 | {
632 | size_t newsize = kcp->ackcount + 1;
633 | IUINT32 *ptr;
634 |
635 | if (newsize > kcp->ackblock) {
636 | IUINT32 *acklist;
637 | size_t newblock;
638 |
639 | for (newblock = 8; newblock < newsize; newblock <<= 1);
640 | acklist = (IUINT32*)ikcp_malloc(newblock * sizeof(IUINT32) * 2);
641 |
642 | if (acklist == NULL) {
643 | assert(acklist != NULL);
644 | abort();
645 | }
646 |
647 | if (kcp->acklist != NULL) {
648 | size_t x;
649 | for (x = 0; x < kcp->ackcount; x++) {
650 | acklist[x * 2 + 0] = kcp->acklist[x * 2 + 0];
651 | acklist[x * 2 + 1] = kcp->acklist[x * 2 + 1];
652 | }
653 | ikcp_free(kcp->acklist);
654 | }
655 |
656 | kcp->acklist = acklist;
657 | kcp->ackblock = newblock;
658 | }
659 |
660 | ptr = &kcp->acklist[kcp->ackcount * 2];
661 | ptr[0] = sn;
662 | ptr[1] = ts;
663 | kcp->ackcount++;
664 | }
665 |
666 | static void ikcp_ack_get(const ikcpcb *kcp, int p, IUINT32 *sn, IUINT32 *ts)
667 | {
668 | if (sn) sn[0] = kcp->acklist[p * 2 + 0];
669 | if (ts) ts[0] = kcp->acklist[p * 2 + 1];
670 | }
671 |
672 |
673 | //---------------------------------------------------------------------
674 | // parse data
675 | //---------------------------------------------------------------------
676 | void ikcp_parse_data(ikcpcb *kcp, IKCPSEG *newseg)
677 | {
678 | struct IQUEUEHEAD *p, *prev;
679 | IUINT32 sn = newseg->sn;
680 | int repeat = 0;
681 |
682 | if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) >= 0 ||
683 | _itimediff(sn, kcp->rcv_nxt) < 0) {
684 | ikcp_segment_delete(kcp, newseg);
685 | return;
686 | }
687 |
688 | for (p = kcp->rcv_buf.prev; p != &kcp->rcv_buf; p = prev) {
689 | IKCPSEG *seg = iqueue_entry(p, IKCPSEG, node);
690 | prev = p->prev;
691 | if (seg->sn == sn) {
692 | repeat = 1;
693 | break;
694 | }
695 | if (_itimediff(sn, seg->sn) > 0) {
696 | break;
697 | }
698 | }
699 |
700 | if (repeat == 0) {
701 | iqueue_init(&newseg->node);
702 | iqueue_add(&newseg->node, p);
703 | kcp->nrcv_buf++;
704 | } else {
705 | ikcp_segment_delete(kcp, newseg);
706 | }
707 |
708 | #if 0
709 | ikcp_qprint("rcvbuf", &kcp->rcv_buf);
710 | printf("rcv_nxt=%lu\n", kcp->rcv_nxt);
711 | #endif
712 |
713 | // move available data from rcv_buf -> rcv_queue
714 | while (! iqueue_is_empty(&kcp->rcv_buf)) {
715 | IKCPSEG *seg = iqueue_entry(kcp->rcv_buf.next, IKCPSEG, node);
716 | if (seg->sn == kcp->rcv_nxt && kcp->nrcv_que < kcp->rcv_wnd) {
717 | iqueue_del(&seg->node);
718 | kcp->nrcv_buf--;
719 | iqueue_add_tail(&seg->node, &kcp->rcv_queue);
720 | kcp->nrcv_que++;
721 | kcp->rcv_nxt++;
722 | } else {
723 | break;
724 | }
725 | }
726 |
727 | #if 0
728 | ikcp_qprint("queue", &kcp->rcv_queue);
729 | printf("rcv_nxt=%lu\n", kcp->rcv_nxt);
730 | #endif
731 |
732 | #if 1
733 | // printf("snd(buf=%d, queue=%d)\n", kcp->nsnd_buf, kcp->nsnd_que);
734 | // printf("rcv(buf=%d, queue=%d)\n", kcp->nrcv_buf, kcp->nrcv_que);
735 | #endif
736 | }
737 |
738 |
739 | //---------------------------------------------------------------------
740 | // input data
741 | //---------------------------------------------------------------------
742 | int ikcp_input(ikcpcb *kcp, const char *data, long size)
743 | {
744 | IUINT32 una = kcp->snd_una;
745 | IUINT32 maxack = 0;
746 | int flag = 0;
747 |
748 | if (ikcp_canlog(kcp, IKCP_LOG_INPUT)) {
749 | ikcp_log(kcp, IKCP_LOG_INPUT, "[RI] %d bytes", size);
750 | }
751 |
752 | if (data == NULL || size < 24) return -1;
753 |
754 | while (1) {
755 | IUINT32 ts, sn, len, una, conv;
756 | IUINT16 wnd;
757 | IUINT8 cmd, frg;
758 | IKCPSEG *seg;
759 |
760 | if (size < (int)IKCP_OVERHEAD) break;
761 |
762 | data = ikcp_decode32u(data, &conv);
763 | if (conv != kcp->conv) return -1;
764 |
765 | data = ikcp_decode8u(data, &cmd);
766 | data = ikcp_decode8u(data, &frg);
767 | data = ikcp_decode16u(data, &wnd);
768 | data = ikcp_decode32u(data, &ts);
769 | data = ikcp_decode32u(data, &sn);
770 | data = ikcp_decode32u(data, &una);
771 | data = ikcp_decode32u(data, &len);
772 |
773 | size -= IKCP_OVERHEAD;
774 |
775 | if ((long)size < (long)len) return -2;
776 |
777 | if (cmd != IKCP_CMD_PUSH && cmd != IKCP_CMD_ACK &&
778 | cmd != IKCP_CMD_WASK && cmd != IKCP_CMD_WINS)
779 | return -3;
780 |
781 | kcp->rmt_wnd = wnd;
782 | ikcp_parse_una(kcp, una);
783 | ikcp_shrink_buf(kcp);
784 |
785 | if (cmd == IKCP_CMD_ACK) {
786 | if (_itimediff(kcp->current, ts) >= 0) {
787 | ikcp_update_ack(kcp, _itimediff(kcp->current, ts));
788 | }
789 | ikcp_parse_ack(kcp, sn);
790 | ikcp_shrink_buf(kcp);
791 | if (flag == 0) {
792 | flag = 1;
793 | maxack = sn;
794 | } else {
795 | if (_itimediff(sn, maxack) > 0) {
796 | maxack = sn;
797 | }
798 | }
799 | if (ikcp_canlog(kcp, IKCP_LOG_IN_ACK)) {
800 | ikcp_log(kcp, IKCP_LOG_IN_DATA,
801 | "input ack: sn=%lu rtt=%ld rto=%ld", sn,
802 | (long)_itimediff(kcp->current, ts),
803 | (long)kcp->rx_rto);
804 | }
805 | }
806 | else if (cmd == IKCP_CMD_PUSH) {
807 | if (ikcp_canlog(kcp, IKCP_LOG_IN_DATA)) {
808 | ikcp_log(kcp, IKCP_LOG_IN_DATA,
809 | "input psh: sn=%lu ts=%lu", sn, ts);
810 | }
811 | if (_itimediff(sn, kcp->rcv_nxt + kcp->rcv_wnd) < 0) {
812 | ikcp_ack_push(kcp, sn, ts);
813 | if (_itimediff(sn, kcp->rcv_nxt) >= 0) {
814 | seg = ikcp_segment_new(kcp, len);
815 | seg->conv = conv;
816 | seg->cmd = cmd;
817 | seg->frg = frg;
818 | seg->wnd = wnd;
819 | seg->ts = ts;
820 | seg->sn = sn;
821 | seg->una = una;
822 | seg->len = len;
823 |
824 | if (len > 0) {
825 | memcpy(seg->data, data, len);
826 | }
827 |
828 | ikcp_parse_data(kcp, seg);
829 | }
830 | }
831 | }
832 | else if (cmd == IKCP_CMD_WASK) {
833 | // ready to send back IKCP_CMD_WINS in ikcp_flush
834 | // tell remote my window size
835 | kcp->probe |= IKCP_ASK_TELL;
836 | if (ikcp_canlog(kcp, IKCP_LOG_IN_PROBE)) {
837 | ikcp_log(kcp, IKCP_LOG_IN_PROBE, "input probe");
838 | }
839 | }
840 | else if (cmd == IKCP_CMD_WINS) {
841 | // do nothing
842 | if (ikcp_canlog(kcp, IKCP_LOG_IN_WINS)) {
843 | ikcp_log(kcp, IKCP_LOG_IN_WINS,
844 | "input wins: %lu", (IUINT32)(wnd));
845 | }
846 | }
847 | else {
848 | return -3;
849 | }
850 |
851 | data += len;
852 | size -= len;
853 | }
854 |
855 | if (flag != 0) {
856 | ikcp_parse_fastack(kcp, maxack);
857 | }
858 |
859 | if (_itimediff(kcp->snd_una, una) > 0) {
860 | if (kcp->cwnd < kcp->rmt_wnd) {
861 | IUINT32 mss = kcp->mss;
862 | if (kcp->cwnd < kcp->ssthresh) {
863 | kcp->cwnd++;
864 | kcp->incr += mss;
865 | } else {
866 | if (kcp->incr < mss) kcp->incr = mss;
867 | kcp->incr += (mss * mss) / kcp->incr + (mss / 16);
868 | if ((kcp->cwnd + 1) * mss <= kcp->incr) {
869 | kcp->cwnd++;
870 | }
871 | }
872 | if (kcp->cwnd > kcp->rmt_wnd) {
873 | kcp->cwnd = kcp->rmt_wnd;
874 | kcp->incr = kcp->rmt_wnd * mss;
875 | }
876 | }
877 | }
878 |
879 | return 0;
880 | }
881 |
882 |
883 | //---------------------------------------------------------------------
884 | // ikcp_encode_seg
885 | //---------------------------------------------------------------------
886 | static char *ikcp_encode_seg(char *ptr, const IKCPSEG *seg)
887 | {
888 | ptr = ikcp_encode32u(ptr, seg->conv);
889 | ptr = ikcp_encode8u(ptr, (IUINT8)seg->cmd);
890 | ptr = ikcp_encode8u(ptr, (IUINT8)seg->frg);
891 | ptr = ikcp_encode16u(ptr, (IUINT16)seg->wnd);
892 | ptr = ikcp_encode32u(ptr, seg->ts);
893 | ptr = ikcp_encode32u(ptr, seg->sn);
894 | ptr = ikcp_encode32u(ptr, seg->una);
895 | ptr = ikcp_encode32u(ptr, seg->len);
896 | return ptr;
897 | }
898 |
899 | static int ikcp_wnd_unused(const ikcpcb *kcp)
900 | {
901 | if (kcp->nrcv_que < kcp->rcv_wnd) {
902 | return kcp->rcv_wnd - kcp->nrcv_que;
903 | }
904 | return 0;
905 | }
906 |
907 |
908 | //---------------------------------------------------------------------
909 | // ikcp_flush
910 | //---------------------------------------------------------------------
911 | void ikcp_flush(ikcpcb *kcp)
912 | {
913 | IUINT32 current = kcp->current;
914 | char *buffer = kcp->buffer;
915 | char *ptr = buffer;
916 | int count, size, i;
917 | IUINT32 resent, cwnd;
918 | IUINT32 rtomin;
919 | struct IQUEUEHEAD *p;
920 | int change = 0;
921 | int lost = 0;
922 | IKCPSEG seg;
923 |
924 | // 'ikcp_update' haven't been called.
925 | // if (kcp->updated == 0) return;
926 | // NOTICE: controlled by frame ticker
927 |
928 | seg.conv = kcp->conv;
929 | seg.cmd = IKCP_CMD_ACK;
930 | seg.frg = 0;
931 | seg.wnd = ikcp_wnd_unused(kcp);
932 | seg.una = kcp->rcv_nxt;
933 | seg.len = 0;
934 | seg.sn = 0;
935 | seg.ts = 0;
936 |
937 | // flush acknowledges
938 | count = kcp->ackcount;
939 | for (i = 0; i < count; i++) {
940 | size = (int)(ptr - buffer);
941 | if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) {
942 | ikcp_output(kcp, buffer, size);
943 | ptr = buffer;
944 | }
945 | ikcp_ack_get(kcp, i, &seg.sn, &seg.ts);
946 | ptr = ikcp_encode_seg(ptr, &seg);
947 | }
948 |
949 | kcp->ackcount = 0;
950 |
951 | // probe window size (if remote window size equals zero)
952 | if (kcp->rmt_wnd == 0) {
953 | if (kcp->probe_wait == 0) {
954 | kcp->probe_wait = IKCP_PROBE_INIT;
955 | kcp->ts_probe = kcp->current + kcp->probe_wait;
956 | }
957 | else {
958 | if (_itimediff(kcp->current, kcp->ts_probe) >= 0) {
959 | if (kcp->probe_wait < IKCP_PROBE_INIT)
960 | kcp->probe_wait = IKCP_PROBE_INIT;
961 | kcp->probe_wait += kcp->probe_wait / 2;
962 | if (kcp->probe_wait > IKCP_PROBE_LIMIT)
963 | kcp->probe_wait = IKCP_PROBE_LIMIT;
964 | kcp->ts_probe = kcp->current + kcp->probe_wait;
965 | kcp->probe |= IKCP_ASK_SEND;
966 | }
967 | }
968 | } else {
969 | kcp->ts_probe = 0;
970 | kcp->probe_wait = 0;
971 | }
972 |
973 | // flush window probing commands
974 | if (kcp->probe & IKCP_ASK_SEND) {
975 | seg.cmd = IKCP_CMD_WASK;
976 | size = (int)(ptr - buffer);
977 | if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) {
978 | ikcp_output(kcp, buffer, size);
979 | ptr = buffer;
980 | }
981 | ptr = ikcp_encode_seg(ptr, &seg);
982 | }
983 |
984 | // flush window probing commands
985 | if (kcp->probe & IKCP_ASK_TELL) {
986 | seg.cmd = IKCP_CMD_WINS;
987 | size = (int)(ptr - buffer);
988 | if (size + (int)IKCP_OVERHEAD > (int)kcp->mtu) {
989 | ikcp_output(kcp, buffer, size);
990 | ptr = buffer;
991 | }
992 | ptr = ikcp_encode_seg(ptr, &seg);
993 | }
994 |
995 | kcp->probe = 0;
996 |
997 | // calculate window size
998 | cwnd = _imin_(kcp->snd_wnd, kcp->rmt_wnd);
999 | if (kcp->nocwnd == 0) cwnd = _imin_(kcp->cwnd, cwnd);
1000 |
1001 | // move data from snd_queue to snd_buf
1002 | while (_itimediff(kcp->snd_nxt, kcp->snd_una + cwnd) < 0) {
1003 | IKCPSEG *newseg;
1004 | if (iqueue_is_empty(&kcp->snd_queue)) break;
1005 |
1006 | newseg = iqueue_entry(kcp->snd_queue.next, IKCPSEG, node);
1007 |
1008 | iqueue_del(&newseg->node);
1009 | iqueue_add_tail(&newseg->node, &kcp->snd_buf);
1010 | kcp->nsnd_que--;
1011 | kcp->nsnd_buf++;
1012 |
1013 | newseg->conv = kcp->conv;
1014 | newseg->cmd = IKCP_CMD_PUSH;
1015 | newseg->wnd = seg.wnd;
1016 | newseg->ts = current;
1017 | newseg->sn = kcp->snd_nxt++;
1018 | newseg->una = kcp->rcv_nxt;
1019 | newseg->resendts = current;
1020 | newseg->rto = kcp->rx_rto;
1021 | newseg->fastack = 0;
1022 | newseg->xmit = 0;
1023 | }
1024 |
1025 | // calculate resent
1026 | resent = (kcp->fastresend > 0)? (IUINT32)kcp->fastresend : 0xffffffff;
1027 | rtomin = (kcp->nodelay == 0)? (kcp->rx_rto >> 3) : 0;
1028 |
1029 | // flush data segments
1030 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) {
1031 | IKCPSEG *segment = iqueue_entry(p, IKCPSEG, node);
1032 | int needsend = 0;
1033 | if (segment->xmit == 0) {
1034 | needsend = 1;
1035 | segment->xmit++;
1036 | segment->rto = kcp->rx_rto;
1037 | segment->resendts = current + segment->rto + rtomin;
1038 | }
1039 | else if (_itimediff(current, segment->resendts) >= 0) {
1040 | needsend = 1;
1041 | segment->xmit++;
1042 | kcp->xmit++;
1043 | if (kcp->nodelay == 0) {
1044 | segment->rto += kcp->rx_rto;
1045 | } else {
1046 | segment->rto += kcp->rx_rto / 2;
1047 | }
1048 | segment->resendts = current + segment->rto;
1049 | lost = 1;
1050 | }
1051 | else if (segment->fastack >= resent) {
1052 | needsend = 1;
1053 | segment->xmit++;
1054 | segment->fastack = 0;
1055 | segment->resendts = current + segment->rto;
1056 | change++;
1057 | }
1058 |
1059 | if (needsend) {
1060 | int size, need;
1061 | segment->ts = current;
1062 | segment->wnd = seg.wnd;
1063 | segment->una = kcp->rcv_nxt;
1064 |
1065 | size = (int)(ptr - buffer);
1066 | need = IKCP_OVERHEAD + segment->len;
1067 |
1068 | if (size + need > (int)kcp->mtu) {
1069 | ikcp_output(kcp, buffer, size);
1070 | ptr = buffer;
1071 | }
1072 |
1073 | ptr = ikcp_encode_seg(ptr, segment);
1074 |
1075 | if (segment->len > 0) {
1076 | memcpy(ptr, segment->data, segment->len);
1077 | ptr += segment->len;
1078 | }
1079 |
1080 | if (segment->xmit >= kcp->dead_link) {
1081 | kcp->state = -1;
1082 | }
1083 | }
1084 | }
1085 |
1086 | // flash remain segments
1087 | size = (int)(ptr - buffer);
1088 | if (size > 0) {
1089 | ikcp_output(kcp, buffer, size);
1090 | }
1091 |
1092 | // update ssthresh
1093 | if (change) {
1094 | IUINT32 inflight = kcp->snd_nxt - kcp->snd_una;
1095 | kcp->ssthresh = inflight / 2;
1096 | if (kcp->ssthresh < IKCP_THRESH_MIN)
1097 | kcp->ssthresh = IKCP_THRESH_MIN;
1098 | kcp->cwnd = kcp->ssthresh + resent;
1099 | kcp->incr = kcp->cwnd * kcp->mss;
1100 | }
1101 |
1102 | if (lost) {
1103 | kcp->ssthresh = cwnd / 2;
1104 | if (kcp->ssthresh < IKCP_THRESH_MIN)
1105 | kcp->ssthresh = IKCP_THRESH_MIN;
1106 | kcp->cwnd = 1;
1107 | kcp->incr = kcp->mss;
1108 | }
1109 |
1110 | if (kcp->cwnd < 1) {
1111 | kcp->cwnd = 1;
1112 | kcp->incr = kcp->mss;
1113 | }
1114 | }
1115 |
1116 |
1117 | //---------------------------------------------------------------------
1118 | // update state (call it repeatedly, every 10ms-100ms), or you can ask
1119 | // ikcp_check when to call it again (without ikcp_input/_send calling).
1120 | // 'current' - current timestamp in millisec.
1121 | //---------------------------------------------------------------------
1122 | void ikcp_update(ikcpcb *kcp, IUINT32 current)
1123 | {
1124 | IINT32 slap;
1125 |
1126 | kcp->current = current;
1127 |
1128 | if (kcp->updated == 0) {
1129 | kcp->updated = 1;
1130 | kcp->ts_flush = kcp->current;
1131 | }
1132 |
1133 | slap = _itimediff(kcp->current, kcp->ts_flush);
1134 |
1135 | if (slap >= 10000 || slap < -10000) {
1136 | kcp->ts_flush = kcp->current;
1137 | slap = 0;
1138 | }
1139 |
1140 | if (slap >= 0) {
1141 | kcp->ts_flush += kcp->interval;
1142 | if (_itimediff(kcp->current, kcp->ts_flush) >= 0) {
1143 | kcp->ts_flush = kcp->current + kcp->interval;
1144 | }
1145 | ikcp_flush(kcp);
1146 | }
1147 | }
1148 |
1149 |
1150 | //---------------------------------------------------------------------
1151 | // Determine when should you invoke ikcp_update:
1152 | // returns when you should invoke ikcp_update in millisec, if there
1153 | // is no ikcp_input/_send calling. you can call ikcp_update in that
1154 | // time, instead of call update repeatly.
1155 | // Important to reduce unnacessary ikcp_update invoking. use it to
1156 | // schedule ikcp_update (eg. implementing an epoll-like mechanism,
1157 | // or optimize ikcp_update when handling massive kcp connections)
1158 | //---------------------------------------------------------------------
1159 | IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current)
1160 | {
1161 | IUINT32 ts_flush = kcp->ts_flush;
1162 | IINT32 tm_flush = 0x7fffffff;
1163 | IINT32 tm_packet = 0x7fffffff;
1164 | IUINT32 minimal = 0;
1165 | struct IQUEUEHEAD *p;
1166 |
1167 | if (kcp->updated == 0) {
1168 | return current;
1169 | }
1170 |
1171 | if (_itimediff(current, ts_flush) >= 10000 ||
1172 | _itimediff(current, ts_flush) < -10000) {
1173 | ts_flush = current;
1174 | }
1175 |
1176 | if (_itimediff(current, ts_flush) >= 0) {
1177 | return current;
1178 | }
1179 |
1180 | tm_flush = _itimediff(ts_flush, current);
1181 |
1182 | for (p = kcp->snd_buf.next; p != &kcp->snd_buf; p = p->next) {
1183 | const IKCPSEG *seg = iqueue_entry(p, const IKCPSEG, node);
1184 | IINT32 diff = _itimediff(seg->resendts, current);
1185 | if (diff <= 0) {
1186 | return current;
1187 | }
1188 | if (diff < tm_packet) tm_packet = diff;
1189 | }
1190 |
1191 | minimal = (IUINT32)(tm_packet < tm_flush ? tm_packet : tm_flush);
1192 | if (minimal >= kcp->interval) minimal = kcp->interval;
1193 |
1194 | return current + minimal;
1195 | }
1196 |
1197 |
1198 |
1199 | int ikcp_setmtu(ikcpcb *kcp, int mtu)
1200 | {
1201 | char *buffer;
1202 | if (mtu < 50 || mtu < (int)IKCP_OVERHEAD)
1203 | return -1;
1204 | buffer = (char*)ikcp_malloc((mtu + IKCP_OVERHEAD) * 3);
1205 | if (buffer == NULL)
1206 | return -2;
1207 | kcp->mtu = mtu;
1208 | kcp->mss = kcp->mtu - IKCP_OVERHEAD;
1209 | ikcp_free(kcp->buffer);
1210 | kcp->buffer = buffer;
1211 | return 0;
1212 | }
1213 |
1214 | int ikcp_interval(ikcpcb *kcp, int interval)
1215 | {
1216 | if (interval > 5000) interval = 5000;
1217 | else if (interval < 10) interval = 10;
1218 | kcp->interval = interval;
1219 | return 0;
1220 | }
1221 |
1222 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc)
1223 | {
1224 | if (nodelay >= 0) {
1225 | kcp->nodelay = nodelay;
1226 | if (nodelay) {
1227 | kcp->rx_minrto = IKCP_RTO_NDL;
1228 | }
1229 | else {
1230 | kcp->rx_minrto = IKCP_RTO_MIN;
1231 | }
1232 | }
1233 | if (interval >= 0) {
1234 | if (interval > 5000) interval = 5000;
1235 | else if (interval < 10) interval = 10;
1236 | kcp->interval = interval;
1237 | }
1238 | if (resend >= 0) {
1239 | kcp->fastresend = resend;
1240 | }
1241 | if (nc >= 0) {
1242 | kcp->nocwnd = nc;
1243 | }
1244 | return 0;
1245 | }
1246 |
1247 |
1248 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd)
1249 | {
1250 | if (kcp) {
1251 | if (sndwnd > 0) {
1252 | kcp->snd_wnd = sndwnd;
1253 | }
1254 | if (rcvwnd > 0) {
1255 | kcp->rcv_wnd = rcvwnd;
1256 | }
1257 | }
1258 | return 0;
1259 | }
1260 |
1261 | int ikcp_waitsnd(const ikcpcb *kcp)
1262 | {
1263 | return kcp->nsnd_buf + kcp->nsnd_que;
1264 | }
1265 |
1266 |
1267 | // read conv
1268 | IUINT32 ikcp_getconv(const void *ptr)
1269 | {
1270 | IUINT32 conv;
1271 | ikcp_decode32u((const char*)ptr, &conv);
1272 | return conv;
1273 | }
1274 |
1275 |
1276 |
--------------------------------------------------------------------------------
/ikcp.h:
--------------------------------------------------------------------------------
1 | //=====================================================================
2 | //
3 | // KCP - A Better ARQ Protocol Implementation
4 | // skywind3000 (at) gmail.com, 2010-2011
5 | //
6 | // Features:
7 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp.
8 | // + Maximum RTT reduce three times vs tcp.
9 | // + Lightweight, distributed as a single source file.
10 | //
11 | //=====================================================================
12 | #ifndef __IKCP_H__
13 | #define __IKCP_H__
14 |
15 | #include
16 | #include
17 | #include
18 |
19 |
20 | //=====================================================================
21 | // 32BIT INTEGER DEFINITION
22 | //=====================================================================
23 | #ifndef __INTEGER_32_BITS__
24 | #define __INTEGER_32_BITS__
25 | #if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \
26 | defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \
27 | defined(_M_AMD64)
28 | typedef unsigned int ISTDUINT32;
29 | typedef int ISTDINT32;
30 | #elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \
31 | defined(__i386) || defined(_M_X86)
32 | typedef unsigned long ISTDUINT32;
33 | typedef long ISTDINT32;
34 | #elif defined(__MACOS__)
35 | typedef UInt32 ISTDUINT32;
36 | typedef SInt32 ISTDINT32;
37 | #elif defined(__APPLE__) && defined(__MACH__)
38 | #include
39 | typedef u_int32_t ISTDUINT32;
40 | typedef int32_t ISTDINT32;
41 | #elif defined(__BEOS__)
42 | #include
43 | typedef u_int32_t ISTDUINT32;
44 | typedef int32_t ISTDINT32;
45 | #elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__))
46 | typedef unsigned __int32 ISTDUINT32;
47 | typedef __int32 ISTDINT32;
48 | #elif defined(__GNUC__)
49 | #include
50 | typedef uint32_t ISTDUINT32;
51 | typedef int32_t ISTDINT32;
52 | #else
53 | typedef unsigned long ISTDUINT32;
54 | typedef long ISTDINT32;
55 | #endif
56 | #endif
57 |
58 |
59 | //=====================================================================
60 | // Integer Definition
61 | //=====================================================================
62 | #ifndef __IINT8_DEFINED
63 | #define __IINT8_DEFINED
64 | typedef char IINT8;
65 | #endif
66 |
67 | #ifndef __IUINT8_DEFINED
68 | #define __IUINT8_DEFINED
69 | typedef unsigned char IUINT8;
70 | #endif
71 |
72 | #ifndef __IUINT16_DEFINED
73 | #define __IUINT16_DEFINED
74 | typedef unsigned short IUINT16;
75 | #endif
76 |
77 | #ifndef __IINT16_DEFINED
78 | #define __IINT16_DEFINED
79 | typedef short IINT16;
80 | #endif
81 |
82 | #ifndef __IINT32_DEFINED
83 | #define __IINT32_DEFINED
84 | typedef ISTDINT32 IINT32;
85 | #endif
86 |
87 | #ifndef __IUINT32_DEFINED
88 | #define __IUINT32_DEFINED
89 | typedef ISTDUINT32 IUINT32;
90 | #endif
91 |
92 | #ifndef __IINT64_DEFINED
93 | #define __IINT64_DEFINED
94 | #if defined(_MSC_VER) || defined(__BORLANDC__)
95 | typedef __int64 IINT64;
96 | #else
97 | typedef long long IINT64;
98 | #endif
99 | #endif
100 |
101 | #ifndef __IUINT64_DEFINED
102 | #define __IUINT64_DEFINED
103 | #if defined(_MSC_VER) || defined(__BORLANDC__)
104 | typedef unsigned __int64 IUINT64;
105 | #else
106 | typedef unsigned long long IUINT64;
107 | #endif
108 | #endif
109 |
110 | #ifndef INLINE
111 | #if defined(__GNUC__)
112 |
113 | #if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1))
114 | #define INLINE __inline__ __attribute__((always_inline))
115 | #else
116 | #define INLINE __inline__
117 | #endif
118 |
119 | #elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__))
120 | #define INLINE __inline
121 | #else
122 | #define INLINE
123 | #endif
124 | #endif
125 |
126 | #if (!defined(__cplusplus)) && (!defined(inline))
127 | #define inline INLINE
128 | #endif
129 |
130 |
131 | //=====================================================================
132 | // QUEUE DEFINITION
133 | //=====================================================================
134 | #ifndef __IQUEUE_DEF__
135 | #define __IQUEUE_DEF__
136 |
137 | struct IQUEUEHEAD {
138 | struct IQUEUEHEAD *next, *prev;
139 | };
140 |
141 | typedef struct IQUEUEHEAD iqueue_head;
142 |
143 |
144 | //---------------------------------------------------------------------
145 | // queue init
146 | //---------------------------------------------------------------------
147 | #define IQUEUE_HEAD_INIT(name) { &(name), &(name) }
148 | #define IQUEUE_HEAD(name) \
149 | struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name)
150 |
151 | #define IQUEUE_INIT(ptr) ( \
152 | (ptr)->next = (ptr), (ptr)->prev = (ptr))
153 |
154 | #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
155 |
156 | #define ICONTAINEROF(ptr, type, member) ( \
157 | (type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) )
158 |
159 | #define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member)
160 |
161 |
162 | //---------------------------------------------------------------------
163 | // queue operation
164 | //---------------------------------------------------------------------
165 | #define IQUEUE_ADD(node, head) ( \
166 | (node)->prev = (head), (node)->next = (head)->next, \
167 | (head)->next->prev = (node), (head)->next = (node))
168 |
169 | #define IQUEUE_ADD_TAIL(node, head) ( \
170 | (node)->prev = (head)->prev, (node)->next = (head), \
171 | (head)->prev->next = (node), (head)->prev = (node))
172 |
173 | #define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n))
174 |
175 | #define IQUEUE_DEL(entry) (\
176 | (entry)->next->prev = (entry)->prev, \
177 | (entry)->prev->next = (entry)->next, \
178 | (entry)->next = 0, (entry)->prev = 0)
179 |
180 | #define IQUEUE_DEL_INIT(entry) do { \
181 | IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0)
182 |
183 | #define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next)
184 |
185 | #define iqueue_init IQUEUE_INIT
186 | #define iqueue_entry IQUEUE_ENTRY
187 | #define iqueue_add IQUEUE_ADD
188 | #define iqueue_add_tail IQUEUE_ADD_TAIL
189 | #define iqueue_del IQUEUE_DEL
190 | #define iqueue_del_init IQUEUE_DEL_INIT
191 | #define iqueue_is_empty IQUEUE_IS_EMPTY
192 |
193 | #define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \
194 | for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \
195 | &((iterator)->MEMBER) != (head); \
196 | (iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER))
197 |
198 | #define iqueue_foreach(iterator, head, TYPE, MEMBER) \
199 | IQUEUE_FOREACH(iterator, head, TYPE, MEMBER)
200 |
201 | #define iqueue_foreach_entry(pos, head) \
202 | for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next )
203 |
204 |
205 | #define __iqueue_splice(list, head) do { \
206 | iqueue_head *first = (list)->next, *last = (list)->prev; \
207 | iqueue_head *at = (head)->next; \
208 | (first)->prev = (head), (head)->next = (first); \
209 | (last)->next = (at), (at)->prev = (last); } while (0)
210 |
211 | #define iqueue_splice(list, head) do { \
212 | if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0)
213 |
214 | #define iqueue_splice_init(list, head) do { \
215 | iqueue_splice(list, head); iqueue_init(list); } while (0)
216 |
217 |
218 | #ifdef _MSC_VER
219 | #pragma warning(disable:4311)
220 | #pragma warning(disable:4312)
221 | #pragma warning(disable:4996)
222 | #endif
223 |
224 | #endif
225 |
226 |
227 | //---------------------------------------------------------------------
228 | // WORD ORDER
229 | //---------------------------------------------------------------------
230 | #ifndef IWORDS_BIG_ENDIAN
231 | #ifdef _BIG_ENDIAN_
232 | #if _BIG_ENDIAN_
233 | #define IWORDS_BIG_ENDIAN 1
234 | #endif
235 | #endif
236 | #ifndef IWORDS_BIG_ENDIAN
237 | #if defined(__hppa__) || \
238 | defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \
239 | (defined(__MIPS__) && defined(__MISPEB__)) || \
240 | defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
241 | defined(__sparc__) || defined(__powerpc__) || \
242 | defined(__mc68000__) || defined(__s390x__) || defined(__s390__)
243 | #define IWORDS_BIG_ENDIAN 1
244 | #endif
245 | #endif
246 | #ifndef IWORDS_BIG_ENDIAN
247 | #define IWORDS_BIG_ENDIAN 0
248 | #endif
249 | #endif
250 |
251 |
252 |
253 | //=====================================================================
254 | // SEGMENT
255 | //=====================================================================
256 | struct IKCPSEG
257 | {
258 | struct IQUEUEHEAD node;
259 | IUINT32 conv;
260 | IUINT32 cmd;
261 | IUINT32 frg;
262 | IUINT32 wnd;
263 | IUINT32 ts;
264 | IUINT32 sn;
265 | IUINT32 una;
266 | IUINT32 len;
267 | IUINT32 resendts;
268 | IUINT32 rto;
269 | IUINT32 fastack;
270 | IUINT32 xmit;
271 | char data[1];
272 | };
273 |
274 |
275 | //---------------------------------------------------------------------
276 | // IKCPCB
277 | //---------------------------------------------------------------------
278 | struct IKCPCB
279 | {
280 | IUINT32 conv, mtu, mss, state;
281 | IUINT32 snd_una, snd_nxt, rcv_nxt;
282 | IUINT32 ts_recent, ts_lastack, ssthresh;
283 | IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto;
284 | IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe;
285 | IUINT32 current, interval, ts_flush, xmit;
286 | IUINT32 nrcv_buf, nsnd_buf;
287 | IUINT32 nrcv_que, nsnd_que;
288 | IUINT32 nodelay, updated;
289 | IUINT32 ts_probe, probe_wait;
290 | IUINT32 dead_link, incr;
291 | struct IQUEUEHEAD snd_queue;
292 | struct IQUEUEHEAD rcv_queue;
293 | struct IQUEUEHEAD snd_buf;
294 | struct IQUEUEHEAD rcv_buf;
295 | IUINT32 *acklist;
296 | IUINT32 ackcount;
297 | IUINT32 ackblock;
298 | void *user;
299 | char *buffer;
300 | int fastresend;
301 | int nocwnd, stream;
302 | int logmask;
303 | int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user);
304 | void (*writelog)(const char *log, struct IKCPCB *kcp, void *user);
305 | };
306 |
307 |
308 | typedef struct IKCPCB ikcpcb;
309 |
310 | #define IKCP_LOG_OUTPUT 1
311 | #define IKCP_LOG_INPUT 2
312 | #define IKCP_LOG_SEND 4
313 | #define IKCP_LOG_RECV 8
314 | #define IKCP_LOG_IN_DATA 16
315 | #define IKCP_LOG_IN_ACK 32
316 | #define IKCP_LOG_IN_PROBE 64
317 | #define IKCP_LOG_IN_WINS 128
318 | #define IKCP_LOG_OUT_DATA 256
319 | #define IKCP_LOG_OUT_ACK 512
320 | #define IKCP_LOG_OUT_PROBE 1024
321 | #define IKCP_LOG_OUT_WINS 2048
322 |
323 | #ifdef __cplusplus
324 | extern "C" {
325 | #endif
326 |
327 | //---------------------------------------------------------------------
328 | // interface
329 | //---------------------------------------------------------------------
330 |
331 | // create a new kcp control object, 'conv' must equal in two endpoint
332 | // from the same connection. 'user' will be passed to the output callback
333 | // output callback can be setup like this: 'kcp->output = my_udp_output'
334 | ikcpcb* ikcp_create(IUINT32 conv, void *user);
335 |
336 | // release kcp control object
337 | void ikcp_release(ikcpcb *kcp);
338 |
339 | // set output callback, which will be invoked by kcp
340 | void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len,
341 | ikcpcb *kcp, void *user));
342 |
343 | // user/upper level recv: returns size, returns below zero for EAGAIN
344 | int ikcp_recv(ikcpcb *kcp, char *buffer, int len);
345 |
346 | // user/upper level send, returns below zero for error
347 | int ikcp_send(ikcpcb *kcp, const char *buffer, int len);
348 |
349 | // update state (call it repeatedly, every 10ms-100ms), or you can ask
350 | // ikcp_check when to call it again (without ikcp_input/_send calling).
351 | // 'current' - current timestamp in millisec.
352 | void ikcp_update(ikcpcb *kcp, IUINT32 current);
353 |
354 | // Determine when should you invoke ikcp_update:
355 | // returns when you should invoke ikcp_update in millisec, if there
356 | // is no ikcp_input/_send calling. you can call ikcp_update in that
357 | // time, instead of call update repeatly.
358 | // Important to reduce unnacessary ikcp_update invoking. use it to
359 | // schedule ikcp_update (eg. implementing an epoll-like mechanism,
360 | // or optimize ikcp_update when handling massive kcp connections)
361 | IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current);
362 |
363 | // when you received a low level packet (eg. UDP packet), call it
364 | int ikcp_input(ikcpcb *kcp, const char *data, long size);
365 |
366 | // flush pending data
367 | void ikcp_flush(ikcpcb *kcp);
368 |
369 | // check the size of next message in the recv queue
370 | int ikcp_peeksize(const ikcpcb *kcp);
371 |
372 | // change MTU size, default is 1400
373 | int ikcp_setmtu(ikcpcb *kcp, int mtu);
374 |
375 | // set maximum window size: sndwnd=32, rcvwnd=32 by default
376 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd);
377 |
378 | // get how many packet is waiting to be sent
379 | int ikcp_waitsnd(const ikcpcb *kcp);
380 |
381 | // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1)
382 | // nodelay: 0:disable(default), 1:enable
383 | // interval: internal update timer interval in millisec, default is 100ms
384 | // resend: 0:disable fast resend(default), 1:enable fast resend
385 | // nc: 0:normal congestion control(default), 1:disable congestion control
386 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc);
387 |
388 | int ikcp_rcvbuf_count(const ikcpcb *kcp);
389 | int ikcp_sndbuf_count(const ikcpcb *kcp);
390 |
391 | void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...);
392 |
393 | // setup allocator
394 | void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*));
395 |
396 | // read conv
397 | IUINT32 ikcp_getconv(const void *ptr);
398 |
399 |
400 | #ifdef __cplusplus
401 | }
402 | #endif
403 |
404 | #endif
405 |
406 |
407 |
--------------------------------------------------------------------------------
/inversion_tree.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2017/1/1.
3 | //
4 |
5 | #include
6 | #include "inversion_tree.h"
7 |
8 | inversionTree inversionTree::newInversionTree(int dataShards, int parityShards) {
9 | inversionTree tree;
10 | tree.m_root.m_children.resize(dataShards + parityShards, nullptr);
11 | tree.m_root.m_matrix = matrix::identityMatrix(dataShards);
12 | return tree;
13 | }
14 |
15 |
16 | matrix
17 | inversionTree::GetInvertedMatrix(std::vector &invalidIndices) {
18 | if (invalidIndices.size() == 0) {
19 | return m_root.m_matrix;
20 | }
21 |
22 | return m_root.getInvertedMatrix(invalidIndices, 0);
23 | }
24 |
25 | int
26 | inversionTree::InsertInvertedMatrix(std::vector &invalidIndices, matrix &matrix, int shards) {
27 | // If no invalid indices were given then we are done because the
28 | // m_root node is already set with the identity matrix.
29 | if (invalidIndices.size() == 0) {
30 | return -1;
31 | }
32 |
33 | if (!matrix.IsSquare()) {
34 | return -2;
35 | }
36 |
37 | // Recursively create nodes for the inverted matrix in the tree until
38 | // we reach the node to insert the matrix to. We start by passing in
39 | // 0 as the parent index as we start at the m_root of the tree.
40 | m_root.insertInvertedMatrix(invalidIndices, matrix, shards, 0);
41 |
42 | return 0;
43 | }
44 |
45 | matrix
46 | inversionNode::getInvertedMatrix(std::vector &invalidIndices, int parent) {
47 | // Get the child node to search next from the list of m_children. The
48 | // list of m_children starts relative to the parent index passed in
49 | // because the indices of invalid rows is sorted (by default). As we
50 | // search recursively, the first invalid index gets popped off the list,
51 | // so when searching through the list of m_children, use that first invalid
52 | // index to find the child node.
53 | int firstIndex = invalidIndices[0];
54 | auto node = m_children[firstIndex - parent];
55 |
56 | // If the child node doesn't exist in the list yet, fail fast by
57 | // returning, so we can construct and insert the proper inverted matrix.
58 | if (node == nullptr) {
59 | return matrix{};
60 | }
61 |
62 | // If there's more than one invalid index left in the list we should
63 | // keep searching recursively.
64 | if (invalidIndices.size() > 1) {
65 | // Search recursively on the child node by passing in the invalid indices
66 | // with the first index popped off the front. Also the parent index to
67 | // pass down is the first index plus one.
68 | std::vector v(invalidIndices.begin() + 1, invalidIndices.end());
69 | return node->getInvertedMatrix(v, firstIndex + 1);
70 | }
71 |
72 | // If there aren't any more invalid indices to search, we've found our
73 | // node. Return it, however keep in mind that the matrix could still be
74 | // nil because intermediary nodes in the tree are created sometimes with
75 | // their inversion matrices uninitialized.
76 | // std::cout << "return cached matrix:" << std::endl;
77 | return node->m_matrix;
78 | }
79 |
80 | void
81 | inversionNode::insertInvertedMatrix(
82 | std::vector &invalidIndices,
83 | struct matrix &matrix,
84 | int shards,
85 | int parent) {
86 | // As above, get the child node to search next from the list of m_children.
87 | // The list of m_children starts relative to the parent index passed in
88 | // because the indices of invalid rows is sorted (by default). As we
89 | // search recursively, the first invalid index gets popped off the list,
90 | // so when searching through the list of m_children, use that first invalid
91 | // index to find the child node.
92 | int firstIndex = invalidIndices[0];
93 | auto node = m_children[firstIndex - parent];
94 |
95 | // If the child node doesn't exist in the list yet, create a new
96 | // node because we have the writer lock and add it to the list
97 | // of m_children.
98 | if (node == nullptr) {
99 | // Make the length of the list of m_children equal to the number
100 | // of shards minus the first invalid index because the list of
101 | // invalid indices is sorted, so only this length of errors
102 | // are possible in the tree.
103 | node = std::make_shared();
104 | node->m_children.resize(shards - firstIndex, nullptr);
105 | m_children[firstIndex - parent] = node;
106 | }
107 |
108 |
109 | // If there's more than one invalid index left in the list we should
110 | // keep searching recursively in order to find the node to add our
111 | // matrix.
112 | if (invalidIndices.size() > 1) {
113 | // As above, search recursively on the child node by passing in
114 | // the invalid indices with the first index popped off the front.
115 | // Also the total number of shards and parent index are passed down
116 | // which is equal to the first index plus one.
117 | std::vector v(invalidIndices.begin() + 1, invalidIndices.end());
118 | node->insertInvertedMatrix(v, matrix, shards, firstIndex + 1);
119 | } else {
120 | node->m_matrix = matrix;
121 | }
122 | }
--------------------------------------------------------------------------------
/inversion_tree.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2017/1/1.
3 | //
4 |
5 | #ifndef KCP_INVERSION_TREE_H
6 | #define KCP_INVERSION_TREE_H
7 |
8 | #include
9 | #include "matrix.h"
10 |
11 | struct inversionNode {
12 | struct matrix m_matrix;
13 | std::vector> m_children;
14 | struct matrix getInvertedMatrix(std::vector & invalidIndices, int parent);
15 |
16 | void insertInvertedMatrix(std::vector &invalidIndices, struct matrix &matrix, int shards, int parent);
17 | };
18 |
19 | class inversionTree {
20 | public:
21 | // newInversionTree initializes a tree for storing inverted matrices.
22 | // Note that the m_root node is the identity matrix as it implies
23 | // there were no errors with the original data.
24 | static inversionTree newInversionTree(int dataShards, int parityShards);
25 |
26 | // GetInvertedMatrix returns the cached inverted matrix or nil if it
27 | // is not found in the tree keyed on the indices of invalid rows.
28 | matrix GetInvertedMatrix(std::vector & invalidIndices);
29 |
30 | // InsertInvertedMatrix inserts a new inverted matrix into the tree
31 | // keyed by the indices of invalid rows. The total number of shards
32 | // is required for creating the proper length lists of child nodes for
33 | // each node.
34 | int InsertInvertedMatrix(std::vector & invalidIndices, struct matrix &matrix, int shards);
35 |
36 | private:
37 | inversionNode m_root;
38 | };
39 |
40 |
41 |
42 | #endif //KCP_INVERSION_TREE_H
43 |
--------------------------------------------------------------------------------
/kcp_test.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "sess.h"
6 |
7 | IUINT32 iclock();
8 |
9 | int main() {
10 | struct timeval time;
11 | gettimeofday(&time, NULL);
12 | srand((time.tv_sec * 1000) + (time.tv_usec / 1000));
13 |
14 | UDPSession *sess = UDPSession::DialWithOptions("127.0.0.1", 9999, 2,2);
15 | sess->NoDelay(1, 20, 2, 1);
16 | sess->WndSize(128, 128);
17 | sess->SetMtu(1400);
18 | sess->SetStreamMode(true);
19 | sess->SetDSCP(46);
20 |
21 | assert(sess != nullptr);
22 | ssize_t nsent = 0;
23 | ssize_t nrecv = 0;
24 | char *buf = (char *) malloc(128);
25 |
26 | for (int i = 0; i < 10; i++) {
27 | sprintf(buf, "message:%d", i);
28 | auto sz = strlen(buf);
29 | sess->Write(buf, sz);
30 | sess->Update(iclock());
31 | memset(buf, 0, 128);
32 | ssize_t n = 0;
33 | do {
34 | n = sess->Read(buf, 128);
35 | if (n > 0) { printf("%s\n", buf); }
36 | usleep(33000);
37 | sess->Update(iclock());
38 | } while(n==0);
39 | }
40 |
41 | UDPSession::Destroy(sess);
42 | }
43 |
44 |
45 | void
46 | itimeofday(long *sec, long *usec) {
47 | struct timeval time;
48 | gettimeofday(&time, NULL);
49 | if (sec) *sec = time.tv_sec;
50 | if (usec) *usec = time.tv_usec;
51 | }
52 |
53 | IUINT64 iclock64(void) {
54 | long s, u;
55 | IUINT64 value;
56 | itimeofday(&s, &u);
57 | value = ((IUINT64) s) * 1000 + (u / 1000);
58 | return value;
59 | }
60 |
61 | IUINT32 iclock() {
62 | return (IUINT32) (iclock64() & 0xfffffffful);
63 | }
64 |
65 |
66 |
--------------------------------------------------------------------------------
/kcpserver.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/xtaci/kcp-go"
7 | )
8 |
9 | const port = ":9999"
10 |
11 | func ListenTest() (*kcp.Listener, error) {
12 | return kcp.ListenWithOptions(port, nil, 2, 2)
13 | }
14 |
15 | func server() {
16 | l, err := ListenTest()
17 | if err != nil {
18 | panic(err)
19 | }
20 | l.SetDSCP(46)
21 | for {
22 | s, err := l.AcceptKCP()
23 | if err != nil {
24 | panic(err)
25 | }
26 |
27 | go handle_client(s)
28 | }
29 | }
30 | func handle_client(conn *kcp.UDPSession) {
31 | conn.SetWindowSize(1024, 1024)
32 | conn.SetNoDelay(1, 20, 2, 1)
33 | conn.SetStreamMode(false)
34 | fmt.Println("new client", conn.RemoteAddr())
35 | buf := make([]byte, 65536)
36 | count := 0
37 | for {
38 | n, err := conn.Read(buf)
39 | if err != nil {
40 | panic(err)
41 | }
42 | count++
43 | fmt.Println("received:", string(buf[:n]))
44 | conn.Write(buf[:n])
45 | }
46 | }
47 |
48 | func main() {
49 | server()
50 | }
51 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xtaci/libkcp/f56813bf394762bd2b03aba9f5d5b941a87b0b2b/logo.png
--------------------------------------------------------------------------------
/matrix.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2016/12/30.
3 | //
4 |
5 | #include "galois.h"
6 | #include "matrix.h"
7 | #include
8 |
9 | matrix
10 | matrix::newMatrix(int rows, int cols) {
11 | if (rows <= 0 || cols <= 0) {
12 | throw std::invalid_argument("invalid arguments");
13 | }
14 |
15 | matrix m;
16 | m.rows = rows;
17 | m.cols = cols;
18 | m.data.resize(rows, nullptr);
19 | for (auto i = 0; i < rows; i++) {
20 | m.data[i] = std::make_shared>(cols);
21 | }
22 | return m;
23 | }
24 |
25 | matrix
26 | matrix::identityMatrix(int size) {
27 | matrix m = matrix::newMatrix(size, size);
28 | for (int i = 0; i < size; i++) {
29 | m.at(i, i) = 1;
30 | }
31 | return m;
32 | }
33 |
34 |
35 | matrix
36 | matrix::Multiply(matrix &right) {
37 | if (cols != right.rows) {
38 | return matrix{};
39 | }
40 |
41 | matrix result = matrix::newMatrix(rows, right.cols);
42 |
43 | for (int r = 0; r < result.rows; r++) {
44 | for (int c = 0; c < result.cols; c++) {
45 | byte value = 0;
46 | for (int i = 0; i < this->cols; i++) {
47 | value ^= galMultiply(at(r, i), right.at(i, c));
48 | }
49 | result.at(r, c) = value;
50 | }
51 | }
52 | return result;
53 | }
54 |
55 | matrix
56 | matrix::Augment(matrix &right) {
57 | matrix result = matrix::newMatrix(this->rows, this->cols + right.cols);
58 |
59 | for (int r = 0; r < this->rows; r++) {
60 | for (int c = 0; c < this->cols; c++) {
61 | result.at(r, c) = at(r, c);
62 | }
63 | auto cols = this->cols;
64 | for (int c = 0; c < right.cols; c++) {
65 | result.at(r, cols + c) = right.at(r, c);
66 | }
67 | }
68 | return result;
69 | }
70 |
71 | matrix
72 | matrix::SubMatrix(int rmin, int cmin, int rmax, int cmax) {
73 | matrix result = matrix::newMatrix(rmax - rmin, cmax - cmin);
74 | for (int r = rmin; r < rmax; r++) {
75 | for (int c = cmin; c < cmax; c++) {
76 | result.at(r - rmin, c - cmin) = at(r, c);
77 | }
78 | }
79 | return result;
80 | }
81 |
82 | // SwapRows Exchanges two rows in the matrix.
83 | int
84 | matrix::SwapRows(int r1, int r2) {
85 | if (r1 < 0 || rows <= r1 || r2 < 0 || rows <= r2) {
86 | return -1;
87 | }
88 |
89 | std::swap(data[r1], data[r2]);
90 | return 0;
91 | }
92 |
93 | bool
94 | matrix::IsSquare() {
95 | return this->rows == this->cols;
96 | }
97 |
98 | matrix
99 | matrix::Invert() {
100 | if (!IsSquare()) {
101 | return matrix{};
102 | }
103 | auto work = matrix::identityMatrix(rows);
104 | work = matrix::Augment(work);
105 |
106 | auto ret = work.gaussianElimination();
107 | if (ret != 0) {
108 | return matrix{};
109 | }
110 |
111 | return work.SubMatrix(0, rows, rows, rows * 2);
112 | }
113 |
114 | int
115 | matrix::gaussianElimination() {
116 | auto rows = this->rows;
117 | auto columns = this->cols;
118 | // Clear out the part below the main diagonal and scale the main
119 | // diagonal to be 1.
120 | for (int r = 0; r < rows; r++) {
121 | // If the element on the diagonal is 0, find a row below
122 | // that has a non-zero and swap them.
123 | if (at(r, r) == 0) {
124 | for (int rowBelow = r + 1; rowBelow < rows; rowBelow++) {
125 | if (at(rowBelow, r) != 0) {
126 | this->SwapRows(r, rowBelow);
127 | break;
128 | }
129 | }
130 | }
131 |
132 | // If we couldn't find one, the matrix is singular.
133 | if (at(r, r) == 0) {
134 | return -1;
135 | }
136 |
137 | // Scale to 1.
138 | if (at(r, r) != 1) {
139 | byte scale = galDivide(1, at(r, r));
140 | for (int c = 0; c < columns; c++) {
141 | at(r, c) = galMultiply(at(r, c), scale);
142 | }
143 | }
144 |
145 | // Make everything below the 1 be a 0 by subtracting
146 | // a multiple of it. (Subtraction and addition are
147 | // both exclusive or in the Galois field.)
148 | for (int rowBelow = r + 1; rowBelow < rows; rowBelow++) {
149 | if (at(rowBelow, r) != 0) {
150 | byte scale = at(rowBelow, r);
151 | for (int c = 0; c < columns; c++) {
152 | at(rowBelow, c) ^= galMultiply(scale, at(r, c));
153 | }
154 | }
155 | }
156 | }
157 |
158 | // Now clear the part above the main diagonal.
159 | for (int d = 0; d < rows; d++) {
160 | for (int rowAbove = 0; rowAbove < d; rowAbove++) {
161 | if (at(rowAbove, d) != 0) {
162 | byte scale = at(rowAbove, d);
163 | for (int c = 0; c < columns; c++) {
164 | at(rowAbove, c) ^= galMultiply(scale, at(d, c));
165 | }
166 | }
167 | }
168 | }
169 | return 0;
170 | }
171 |
172 | matrix
173 | matrix::vandermonde(int rows, int cols) {
174 | matrix result = matrix::newMatrix(rows, cols);
175 | for (int r = 0; r < rows; r++) {
176 | for (int c = 0; c < cols; c++) {
177 | result.at(r, c) = galExp(byte(r), byte(c));
178 | }
179 | }
180 | return result;
181 | }
182 |
--------------------------------------------------------------------------------
/matrix.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2016/12/30.
3 | //
4 |
5 | #ifndef KCP_MATRIX_H
6 | #define KCP_MATRIX_H
7 |
8 | #include
9 | #include
10 | #include "galois.h"
11 |
12 | using row_type = std::shared_ptr>;
13 |
14 | struct matrix {
15 | // newMatrix returns a matrix of zeros.
16 | static matrix newMatrix(int rows, int cols);
17 |
18 | // IdentityMatrix returns an identity matrix of the given empty.
19 | static matrix identityMatrix(int size);
20 |
21 | // Create a Vandermonde matrix, which is guaranteed to have the
22 | // property that any subset of rows that forms a square matrix
23 | // is invertible.
24 | static matrix vandermonde(int rows, int cols);
25 |
26 | // Multiply multiplies this matrix (the one on the left) by another
27 | // matrix (the one on the right) and returns a new matrix with the result.
28 | matrix Multiply(matrix &right);
29 |
30 | // Augment returns the concatenation of this matrix and the matrix on the right.
31 | matrix Augment(matrix &right);
32 |
33 | // Returns a part of this matrix. Data is copied.
34 | matrix SubMatrix(int rmin, int cmin, int rmax, int cmax);
35 |
36 | // IsSquare will return true if the matrix is square
37 | bool IsSquare();
38 |
39 | // SwapRows Exchanges two rows in the matrix.
40 | int SwapRows(int r1, int r2);
41 |
42 | // Invert returns the inverse of this matrix.
43 | // Returns ErrSingular when the matrix is singular and doesn't have an inverse.
44 | // The matrix must be square, otherwise ErrNotSquare is returned.
45 | matrix Invert();
46 |
47 | // Gaussian elimination (also known as row reduction)
48 | int gaussianElimination();
49 |
50 | std::vector data;
51 | int rows{0}, cols{0};
52 |
53 | inline byte &at(int row, int col) { return (*(data[row]))[col]; }
54 |
55 | inline bool empty() { return (rows == 0 || cols == 0); }
56 | };
57 |
58 |
59 | #endif //KCP_MATRIX_H
60 |
--------------------------------------------------------------------------------
/reedsolomon.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2017/1/1.
3 | //
4 |
5 | #include
6 | #include
7 | #include
8 | #include "reedsolomon.h"
9 | #include "galois_noasm.h"
10 |
11 | ReedSolomon::ReedSolomon(int dataShards, int parityShards) :
12 | m_dataShards(dataShards),
13 | m_parityShards(parityShards),
14 | m_totalShards(dataShards + parityShards) {
15 | tree = inversionTree::newInversionTree(dataShards, parityShards);
16 | }
17 |
18 | ReedSolomon
19 | ReedSolomon::New(int dataShards, int parityShards) {
20 | if (dataShards <= 0 || parityShards <= 0) {
21 | throw std::invalid_argument("cannot create Encoder with zero or less data/parity shards");
22 | }
23 |
24 | if (dataShards + parityShards > 255) {
25 | throw std::invalid_argument("cannot create Encoder with 255 or more data+parity shards");
26 | }
27 |
28 | ReedSolomon r(dataShards, parityShards);
29 |
30 | // Start with a Vandermonde matrix. This matrix would work,
31 | // in theory, but doesn't have the property that the data
32 | // shards are unchanged after encoding.
33 | matrix vm = matrix::vandermonde(r.m_totalShards, r.m_dataShards);
34 |
35 | // Multiply by the inverse of the top square of the matrix.
36 | // This will make the top square be the identity matrix, but
37 | // preserve the property that any square subset of rows is
38 | // invertible.
39 | auto top = vm.SubMatrix(0, 0, dataShards, dataShards);
40 | top = top.Invert();
41 | r.m = vm.Multiply(top);
42 |
43 | // Inverted matrices are cached in a tree keyed by the indices
44 | // of the invalid rows of the data to reconstruct.
45 | // The inversion m_root node will have the identity matrix as
46 | // its inversion matrix because it implies there are no errors
47 | // with the original data.
48 | r.tree = inversionTree::newInversionTree(dataShards, parityShards);
49 |
50 | r.parity = std::vector(parityShards);
51 | for (int i = 0; i < parityShards; i++) {
52 | r.parity[i] = r.m.data[dataShards + i];
53 | }
54 | return r;
55 | }
56 |
57 | void
58 | ReedSolomon::Encode(std::vector &shards) {
59 | if (shards.size() != m_totalShards) {
60 | throw std::invalid_argument("too few shards given");
61 | }
62 |
63 | checkShards(shards, false);
64 |
65 | // Get the slice of output buffers.
66 | std::vector output(shards.begin() + m_dataShards, shards.end());
67 |
68 | // Do the coding.
69 | std::vector input(shards.begin(), shards.begin() + m_dataShards);
70 | codeSomeShards(parity, input, output, m_parityShards);
71 | };
72 |
73 |
74 | void
75 | ReedSolomon::codeSomeShards(std::vector &matrixRows, std::vector &inputs,
76 | std::vector &outputs, int outputCount) {
77 | for (int c = 0; c < m_dataShards; c++) {
78 | auto in = inputs[c];
79 | for (int iRow = 0; iRow < outputCount; iRow++) {
80 | if (c == 0) {
81 | galMulSlice((*matrixRows[iRow])[c], in, outputs[iRow]);
82 | } else {
83 | galMulSliceXor((*matrixRows[iRow])[c], in, outputs[iRow]);
84 | }
85 | }
86 | }
87 | }
88 |
89 | void
90 | ReedSolomon::Reconstruct(std::vector &shards) {
91 | if (shards.size() != m_totalShards) {
92 | throw std::invalid_argument("too few shards given");
93 | }
94 |
95 | // Check arguments
96 | checkShards(shards,true);
97 |
98 | auto shardSize = this->shardSize(shards);
99 |
100 | // Quick check: are all of the shards present? If so, there's
101 | // nothing to do.
102 | int numberPresent = 0;
103 | for (int i = 0; i < m_totalShards; i++) {
104 | if (shards[i] != nullptr) {
105 | numberPresent++;
106 | }
107 | }
108 |
109 | if (numberPresent == m_totalShards) {
110 | // Cool. All of the shards data data. We don't
111 | // need to do anything.
112 | return;
113 | }
114 |
115 | // More complete sanity check
116 | if (numberPresent < m_dataShards) {
117 | throw std::invalid_argument("too few shards given");
118 | }
119 |
120 | // Pull out an array holding just the shards that
121 | // correspond to the rows of the submatrix. These shards
122 | // will be the Input to the decoding process that re-creates
123 | // the missing data shards.
124 | //
125 | // Also, create an array of indices of the valid rows we do have
126 | // and the invalid rows we don't have up until we have enough valid rows.
127 | std::vector subShards(m_dataShards);
128 | std::vector validIndices(m_dataShards, 0);
129 | std::vector invalidIndices;
130 | int subMatrixRow = 0;
131 |
132 | for (int matrixRow = 0; matrixRow < m_totalShards && subMatrixRow < m_dataShards; matrixRow++) {
133 | if (shards[matrixRow] != nullptr) {
134 | subShards[subMatrixRow] = shards[matrixRow];
135 | validIndices[subMatrixRow] = matrixRow;
136 | subMatrixRow++;
137 | } else {
138 | invalidIndices.push_back(matrixRow);
139 | }
140 | }
141 |
142 | // Attempt to get the cached inverted matrix out of the tree
143 | // based on the indices of the invalid rows.
144 | auto dataDecodeMatrix = tree.GetInvertedMatrix(invalidIndices);
145 |
146 | // If the inverted matrix isn't cached in the tree yet we must
147 | // construct it ourselves and insert it into the tree for the
148 | // future. In this way the inversion tree is lazily loaded.
149 | if (dataDecodeMatrix.empty()) {
150 | // Pull out the rows of the matrix that correspond to the
151 | // shards that we have and build a square matrix. This
152 | // matrix could be used to generate the shards that we have
153 | // from the original data.
154 | auto subMatrix = matrix::newMatrix(m_dataShards, m_dataShards);
155 | for (subMatrixRow = 0; subMatrixRow < validIndices.size(); subMatrixRow++) {
156 | for (int c = 0; c < m_dataShards; c++) {
157 | subMatrix.at(subMatrixRow, c) = m.at(validIndices[subMatrixRow], c);
158 | };
159 | }
160 |
161 | // Invert the matrix, so we can go from the encoded shards
162 | // back to the original data. Then pull out the row that
163 | // generates the shard that we want to Decode. Note that
164 | // since this matrix maps back to the original data, it can
165 | // be used to create a data shard, but not a parity shard.
166 | dataDecodeMatrix = subMatrix.Invert();
167 | if (dataDecodeMatrix.empty()) {
168 | throw std::runtime_error("cannot get matrix invert");
169 | }
170 |
171 | // Cache the inverted matrix in the tree for future use keyed on the
172 | // indices of the invalid rows.
173 | int ret = tree.InsertInvertedMatrix(invalidIndices, dataDecodeMatrix, m_totalShards);
174 | if (ret != 0) {
175 | throw std::runtime_error("cannot insert matrix invert");
176 | }
177 | }
178 |
179 | // Re-create any data shards that were missing.
180 | //
181 | // The Input to the coding is all of the shards we actually
182 | // have, and the output is the missing data shards. The computation
183 | // is done using the special Decode matrix we just built.
184 | std::vector outputs(m_parityShards);
185 | std::vector matrixRows(m_parityShards);
186 | int outputCount = 0;
187 |
188 | for (int iShard = 0; iShard < m_dataShards; iShard++) {
189 | if (shards[iShard] == nullptr) {
190 | shards[iShard] = std::make_shared>(shardSize);
191 | outputs[outputCount] = shards[iShard];
192 | matrixRows[outputCount] = dataDecodeMatrix.data[iShard];
193 | outputCount++;
194 | }
195 | }
196 | codeSomeShards(matrixRows, subShards, outputs, outputCount);
197 |
198 | // Now that we have all of the data shards intact, we can
199 | // compute any of the parity that is missing.
200 | //
201 | // The Input to the coding is ALL of the data shards, including
202 | // any that we just calculated. The output is whichever of the
203 | // data shards were missing.
204 | outputCount = 0;
205 | for (int iShard = m_dataShards; iShard < m_totalShards; iShard++) {
206 | if (shards[iShard] == nullptr) {
207 | shards[iShard] = std::make_shared>(shardSize);
208 | outputs[outputCount] = shards[iShard];
209 | matrixRows[outputCount] = parity[iShard - m_dataShards];
210 | outputCount++;
211 | }
212 | }
213 | codeSomeShards(matrixRows, shards, outputs, outputCount);
214 | }
215 |
216 | void
217 | ReedSolomon::checkShards(std::vector &shards, bool nilok) {
218 | auto size = shardSize(shards);
219 | if (size == 0) {
220 | throw std::invalid_argument("no shard data");
221 | }
222 |
223 | for (int i = 0; i < shards.size(); i++) {
224 | if (shards[i] == nullptr) {
225 | if (!nilok) {
226 | throw std::invalid_argument("shard sizes does not match");
227 | }
228 | } else if (shards[i]->size() != size) {
229 | throw std::invalid_argument("shard sizes does not match");
230 | }
231 | }
232 | }
233 |
234 |
235 | int
236 | ReedSolomon::shardSize(std::vector &shards) {
237 | for (int i = 0; i < shards.size(); i++) {
238 | if (shards[i] != nullptr) {
239 | return shards[i]->size();
240 | }
241 | }
242 |
243 | return 0;
244 | }
245 |
--------------------------------------------------------------------------------
/reedsolomon.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by 理 傅 on 2017/1/1.
3 | //
4 |
5 | #ifndef KCP_REEDSOLOMON_H
6 | #define KCP_REEDSOLOMON_H
7 |
8 | #include "matrix.h"
9 | #include "inversion_tree.h"
10 | #include "galois.h"
11 |
12 | class ReedSolomon {
13 | public:
14 | ReedSolomon() = default;
15 |
16 | ReedSolomon(int dataShards, int parityShards);
17 |
18 | // New creates a new encoder and initializes it to
19 | // the number of data shards and parity shards that
20 | // you want to use. You can reuse this encoder.
21 | // Note that the maximum number of data shards is 256.
22 | static ReedSolomon New(int dataShards, int parityShards);
23 |
24 | // Encodes parity for a set of data shards.
25 | // An array 'shards' containing data shards followed by parity shards.
26 | // The number of shards must match the number given to New.
27 | // Each shard is a byte array, and they must all be the same empty.
28 | // The parity shards will always be overwritten and the data shards
29 | // will remain the same.
30 | void Encode(std::vector &shards);
31 |
32 | // Reconstruct will recreate the missing shards, if possible.
33 | //
34 | // Given a list of shards, some of which contain data, fills in the
35 | // ones that don't have data.
36 | //
37 | // The length of the array must be equal to Shards.
38 | // You indicate that a shard is missing by setting it to nil.
39 | //
40 | // If there are too few shards to reconstruct the missing
41 | // ones, ErrTooFewShards will be returned.
42 | //
43 | // The reconstructed shard set is complete, but integrity is not verified.
44 | // Use the Verify function to check if data set is ok.
45 | void Reconstruct(std::vector &shards);
46 |
47 | private:
48 | int m_dataShards; // Number of data shards, should not be modified.
49 | int m_parityShards; // Number of parity shards, should not be modified.
50 | int m_totalShards; // Total number of shards. Calculated, and should not be modified.
51 |
52 | matrix m;
53 | inversionTree tree;
54 | std::vector parity;
55 |
56 | int shardSize(std::vector &shards);
57 |
58 |
59 | // Multiplies a subset of rows from a coding matrix by a full set of
60 | // Input shards to produce some output shards.
61 | // 'matrixRows' is The rows from the matrix to use.
62 | // 'inputs' An array of byte arrays, each of which is one Input shard.
63 | // The number of inputs used is determined by the length of each matrix row.
64 | // outputs Byte arrays where the computed shards are stored.
65 | // The number of outputs computed, and the
66 | // number of matrix rows used, is determined by
67 | // outputCount, which is the number of outputs to compute.
68 | void
69 | codeSomeShards(std::vector &matrixRows, std::vector &inputs, std::vector &outputs,
70 | int outputCount);
71 |
72 | // checkShards will check if shards are the same size
73 | // or 0, if allowed. An error is returned if this fails.
74 | // An error is also returned if all shards are size 0.
75 | void checkShards(std::vector &shards, bool nilok) ;
76 | };
77 |
78 |
79 | #endif //KCP_REEDSOLOMON_H
80 |
--------------------------------------------------------------------------------
/sess.cpp:
--------------------------------------------------------------------------------
1 | #include "sess.h"
2 | #include "encoding.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | UDPSession *
11 | UDPSession::Dial(const char *ip, uint16_t port) {
12 | struct sockaddr_in saddr;
13 | memset(&saddr, 0, sizeof(saddr));
14 | saddr.sin_family = AF_INET;
15 | saddr.sin_port = htons(port);
16 | int ret = inet_pton(AF_INET, ip, &(saddr.sin_addr));
17 |
18 | if (ret == 1) { // do nothing
19 | } else if (ret == 0) { // try ipv6
20 | return UDPSession::dialIPv6(ip, port);
21 | } else if (ret == -1) {
22 | return nullptr;
23 | }
24 |
25 | int sockfd = socket(PF_INET, SOCK_DGRAM, 0);
26 | if (sockfd == -1) {
27 | return nullptr;
28 | }
29 | if (connect(sockfd, (struct sockaddr *) &saddr, sizeof(struct sockaddr)) < 0) {
30 | close(sockfd);
31 | return nullptr;
32 | }
33 |
34 | return UDPSession::createSession(sockfd);
35 | }
36 |
37 | UDPSession *
38 | UDPSession::DialWithOptions(const char *ip, uint16_t port, size_t dataShards, size_t parityShards) {
39 | auto sess = UDPSession::Dial(ip, port);
40 | if (sess == nullptr) {
41 | return nullptr;
42 | }
43 |
44 | if (dataShards > 0 && parityShards > 0) {
45 | sess->fec = FEC::New(3 * (dataShards + parityShards), dataShards, parityShards);
46 | sess->shards.resize(dataShards + parityShards, nullptr);
47 | sess->dataShards = dataShards;
48 | sess->parityShards = parityShards;
49 | }
50 | return sess;
51 | };
52 |
53 |
54 | UDPSession *
55 | UDPSession::dialIPv6(const char *ip, uint16_t port) {
56 | struct sockaddr_in6 saddr;
57 | memset(&saddr, 0, sizeof(saddr));
58 | saddr.sin6_family = AF_INET6;
59 | saddr.sin6_port = htons(port);
60 | if (inet_pton(AF_INET6, ip, &(saddr.sin6_addr)) != 1) {
61 | return nullptr;
62 | }
63 |
64 | int sockfd = socket(PF_INET6, SOCK_DGRAM, 0);
65 | if (sockfd == -1) {
66 | return nullptr;
67 | }
68 | if (connect(sockfd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_in6)) < 0) {
69 | close(sockfd);
70 | return nullptr;
71 | }
72 |
73 | return UDPSession::createSession(sockfd);
74 | }
75 |
76 | UDPSession *
77 | UDPSession::createSession(int sockfd) {
78 | int flags = fcntl(sockfd, F_GETFL, 0);
79 | if (flags < 0) {
80 | return nullptr;
81 | }
82 |
83 | if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) < 0) {
84 | return nullptr;
85 | }
86 |
87 | UDPSession *sess = new(UDPSession);
88 | sess->m_sockfd = sockfd;
89 | sess->m_kcp = ikcp_create(IUINT32(rand()), sess);
90 | sess->m_kcp->output = sess->out_wrapper;
91 | return sess;
92 | }
93 |
94 |
95 | void
96 | UDPSession::Update(uint32_t current) noexcept {
97 | for (;;) {
98 | ssize_t n = recv(m_sockfd, m_buf, sizeof(m_buf), 0);
99 | if (n > 0) {
100 | if (fec.isEnabled()) {
101 | // decode FEC packet
102 | auto pkt = fec.Decode(m_buf, static_cast(n));
103 | if (pkt.flag == typeData) {
104 | auto ptr = pkt.data->data();
105 | // we have 2B size, ignore for typeData
106 | ikcp_input(m_kcp, (char *) (ptr + 2), pkt.data->size() - 2);
107 | }
108 |
109 | // allow FEC packet processing with correct flags.
110 | if (pkt.flag == typeData || pkt.flag == typeFEC) {
111 | // input to FEC, and see if we can recover data.
112 | auto recovered = fec.Input(pkt);
113 |
114 | // we have some data recovered.
115 | for (auto &r : recovered) {
116 | // recovered data has at least 2B size.
117 | if (r->size() > 2) {
118 | auto ptr = r->data();
119 | // decode packet size, which is also recovered.
120 | uint16_t sz;
121 | decode16u(ptr, &sz);
122 |
123 | // the recovered packet size must be in the correct range.
124 | if (sz >= 2 && sz <= r->size()) {
125 | // input proper data to kcp
126 | ikcp_input(m_kcp, (char *) (ptr + 2), sz - 2);
127 | // std::cout << "sz:" << sz << std::endl;
128 | }
129 | }
130 | }
131 | }
132 | } else { // fec disabled
133 | ikcp_input(m_kcp, (char *) (m_buf), n);
134 | }
135 | } else {
136 | break;
137 | }
138 | }
139 | m_kcp->current = current;
140 | ikcp_flush(m_kcp);
141 | }
142 |
143 | void
144 | UDPSession::Destroy(UDPSession *sess) {
145 | if (nullptr == sess) return;
146 | if (0 != sess->m_sockfd) { close(sess->m_sockfd); }
147 | if (nullptr != sess->m_kcp) { ikcp_release(sess->m_kcp); }
148 | delete sess;
149 | }
150 |
151 | ssize_t
152 | UDPSession::Read(char *buf, size_t sz) noexcept {
153 | if (m_streambufsiz > 0) {
154 | size_t n = m_streambufsiz;
155 | if (n > sz) {
156 | n = sz;
157 | }
158 | memcpy(buf, m_streambuf, n);
159 |
160 | m_streambufsiz -= n;
161 | if (m_streambufsiz != 0) {
162 | memmove(m_streambuf, m_streambuf + n, m_streambufsiz);
163 | }
164 | return n;
165 | }
166 |
167 | int psz = ikcp_peeksize(m_kcp);
168 | if (psz <= 0) {
169 | return 0;
170 | }
171 |
172 | if (psz <= sz) {
173 | return (ssize_t) ikcp_recv(m_kcp, buf, int(sz));
174 | } else {
175 | ikcp_recv(m_kcp, (char *) m_streambuf, sizeof(m_streambuf));
176 | memcpy(buf, m_streambuf, sz);
177 | m_streambufsiz = psz - sz;
178 | memmove(m_streambuf, m_streambuf + sz, psz - sz);
179 | return sz;
180 | }
181 | }
182 |
183 | ssize_t
184 | UDPSession::Write(const char *buf, size_t sz) noexcept {
185 | int n = ikcp_send(m_kcp, const_cast(buf), int(sz));
186 | if (n == 0) {
187 | return sz;
188 | } else return n;
189 | }
190 |
191 | int
192 | UDPSession::SetDSCP(int iptos) noexcept {
193 | iptos = (iptos << 2) & 0xFF;
194 | return setsockopt(this->m_sockfd, IPPROTO_IP, IP_TOS, &iptos, sizeof(iptos));
195 | }
196 |
197 | void
198 | UDPSession::SetStreamMode(bool enable) noexcept {
199 | if (enable) {
200 | this->m_kcp->stream = 1;
201 | } else {
202 | this->m_kcp->stream = 0;
203 | }
204 | }
205 |
206 | int
207 | UDPSession::out_wrapper(const char *buf, int len, struct IKCPCB *, void *user) {
208 | assert(user != nullptr);
209 | UDPSession *sess = static_cast(user);
210 |
211 | if (sess->fec.isEnabled()) { // append FEC header
212 | // extend to len + fecHeaderSizePlus2
213 | // i.e. 4B seqid + 2B flag + 2B size
214 | memcpy(sess->m_buf + fecHeaderSizePlus2, buf, static_cast(len));
215 | sess->fec.MarkData(sess->m_buf, static_cast(len));
216 | sess->output(sess->m_buf, len + fecHeaderSizePlus2);
217 |
218 | // FEC calculation
219 | // copy "2B size + data" to shards
220 | auto slen = len + 2;
221 | sess->shards[sess->pkt_idx] =
222 | std::make_shared>(&sess->m_buf[fecHeaderSize], &sess->m_buf[fecHeaderSize + slen]);
223 |
224 | // count number of data shards
225 | sess->pkt_idx++;
226 | if (sess->pkt_idx == sess->dataShards) { // we've collected enough data shards
227 | sess->fec.Encode(sess->shards);
228 | // send parity shards
229 | for (size_t i = sess->dataShards; i < sess->dataShards + sess->parityShards; i++) {
230 | // append header to parity shards
231 | // i.e. fecHeaderSize + data(2B size included)
232 | memcpy(sess->m_buf + fecHeaderSize, sess->shards[i]->data(), sess->shards[i]->size());
233 | sess->fec.MarkFEC(sess->m_buf);
234 | sess->output(sess->m_buf, sess->shards[i]->size() + fecHeaderSize);
235 | }
236 |
237 | // reset indexing
238 | sess->pkt_idx = 0;
239 | }
240 | } else { // No FEC, just send raw bytes,
241 | sess->output(buf, static_cast(len));
242 | }
243 | return 0;
244 | }
245 |
246 | ssize_t
247 | UDPSession::output(const void *buffer, size_t length) {
248 | ssize_t n = send(m_sockfd, buffer, length, 0);
249 | return n;
250 | }
251 |
--------------------------------------------------------------------------------
/sess.h:
--------------------------------------------------------------------------------
1 | #ifndef KCP_SESS_H
2 | #define KCP_SESS_H
3 |
4 | #include "ikcp.h"
5 | #include "fec.h"
6 | #include
7 | #include
8 |
9 | class UDPSession {
10 | private:
11 | int m_sockfd{0};
12 | ikcpcb *m_kcp{nullptr};
13 | byte m_buf[2048];
14 | byte m_streambuf[65535];
15 | size_t m_streambufsiz{0};
16 |
17 | FEC fec;
18 | uint32_t pkt_idx{0};
19 | std::vector shards;
20 | size_t dataShards{0};
21 | size_t parityShards{0};
22 | public:
23 | UDPSession(const UDPSession &) = delete;
24 |
25 | UDPSession &operator=(const UDPSession &) = delete;
26 |
27 | // Dial connects to the remote server and returns UDPSession.
28 | static UDPSession *Dial(const char *ip, uint16_t port);
29 |
30 | // DialWithOptions connects to the remote address "raddr" on the network "udp" with packet encryption
31 | static UDPSession *DialWithOptions(const char *ip, uint16_t port, size_t dataShards, size_t parityShards);
32 |
33 | // Update will try reading/writing udp packet, pass current unix millisecond
34 | void Update(uint32_t current) noexcept;
35 |
36 | // Destroy release all resource related.
37 | static void Destroy(UDPSession *sess);
38 |
39 | // Read reads from kcp with buffer empty sz.
40 | ssize_t Read(char *buf, size_t sz) noexcept;
41 |
42 | // Write writes into kcp with buffer empty sz.
43 | ssize_t Write(const char *buf, size_t sz) noexcept;
44 |
45 | // Set DSCP value
46 | int SetDSCP(int dscp) noexcept;
47 |
48 | // SetStreamMode toggles the stream mode on/off
49 | void SetStreamMode(bool enable) noexcept;
50 |
51 | // Wrappers for kcp control
52 | inline int NoDelay(int nodelay, int interval, int resend, int nc) {
53 | return ikcp_nodelay(m_kcp, nodelay, interval, resend, nc);
54 | }
55 |
56 | inline int WndSize(int sndwnd, int rcvwnd) { return ikcp_wndsize(m_kcp, sndwnd, rcvwnd); }
57 |
58 | inline int SetMtu(int mtu) { return ikcp_setmtu(m_kcp, mtu); }
59 |
60 | private:
61 | UDPSession() = default;
62 |
63 | ~UDPSession() = default;
64 |
65 | // DialIPv6 is the ipv6 version of Dial.
66 | static UDPSession *dialIPv6(const char *ip, uint16_t port);
67 |
68 | // out_wrapper
69 | static int out_wrapper(const char *buf, int len, struct IKCPCB *kcp, void *user);
70 |
71 | // output udp packet
72 | ssize_t output(const void *buffer, size_t length);
73 |
74 | static UDPSession *createSession(int sockfd);
75 |
76 |
77 | };
78 |
79 | inline uint32_t currentMs() {
80 | struct timeval time;
81 | gettimeofday(&time, NULL);
82 | return uint32_t((time.tv_sec * 1000) + (time.tv_usec / 1000));
83 | }
84 |
85 |
86 | #endif //KCP_SESS_H
87 |
--------------------------------------------------------------------------------