├── .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 | libkcp 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 | --------------------------------------------------------------------------------