Blockchain is een innovatieve technologie die belooft veel gebieden van het menselijk leven te verbeteren. Het brengt echte processen en producten naar de digitale ruimte, garandeert snelheid en betrouwbaarheid van financiële transacties, verlaagt hun kosten en maakt het mogelijk om moderne DAPP-applicaties te creëren met behulp van slimme contracten in gedecentraliseerde netwerken.
Gezien de vele voordelen en diverse toepassingsgebieden van blockchain, lijkt het vreemd dat deze veelbelovende technologie nog niet in alle sectoren is doorgedrongen. Het probleem is dat moderne gedecentraliseerde blockchains niet genoeg schaalbaarheid bieden. Ethereum verwerkt ongeveer 20 transacties per seconde, wat niet voldoende is om te voldoen aan de behoeften van moderne dynamische bedrijven. Tegelijkertijd aarzelen bedrijven die blockchain-technologie gebruiken om Ethereum te verlaten vanwege de hoge mate van bescherming tegen hacks en netwerkstoringen.
Om decentralisatie, veiligheid en schaalbaarheid in blockchain te waarborgen en zo de Schaalbaarheidsdilemma op te lossen, heeft het ontwikkelingsteam Plasma Cash gecreëerd — een zijketen die bestaat uit een slim contract en een privénetwerk op basis van Node.js, dat periodiek zijn status naar de hoofdketen (Ethereum) doorgeeft.

De sleutelprocessen in Plasma Cash
1. De gebruiker roept de functie van het slimme contract `deposit` aan en geeft het bedrag in ETH op dat hij/zij in de Plasma Cash-token wil storten. De functie van het slimme contract creëert de token en genereert daar een gebeurtenis voor.
2. Plasma Cash-nodes die zijn ingeschreven op de gebeurtenissen van het slimme contract, ontvangen de gebeurtenis van de creatie van de storting en voegen de transactie voor de creatie van de token toe aan de pool.
3. Periodiek nemen speciale Plasma Cash-nodes alle transacties uit de pool (tot 1 miljoen) en vormen daar een blok van, berekenen de Merkle-boom en dus de hash. Dit blok wordt naar andere nodes verzonden voor verificatie. Nodes controleren of de Merkle-hash geldig is, of de transacties geldig zijn (bijvoorbeeld of de afzender van de token de eigenaar ervan is). Na verificatie van het blok roept de node de functie `submitBlock` van het slimme contract aan, die het nummer en de Merkle-hash van het blok in de hoofdketen opslaat. Het slimme contract genereert een gebeurtenis over het succesvolle toevoegen van het blok. Transacties worden uit de pool verwijderd.
4. Nodes that received the submit event of the block begin to apply the transactions that were added to the block.
5. At some point, the owner (or non-owner) of the token wants to withdraw it from Plasma Cash. To do this, they call the `startExit` function, passing in information about the last 2 transactions for the token that confirm they are indeed the owner of the token. The smart contract, using the Merkle hash, verifies the presence of the transactions in the blocks and sends the token for withdrawal, which will occur in two weeks.
6. If the token withdrawal operation occurred with violations (the token was spent after the withdrawal procedure began or the token was already not owned before withdrawal), the owner of the token can dispute the withdrawal within two weeks.

Privacy is achieved in two ways.
1. The root chain knows nothing about the transactions that are formed and transmitted within the child chain. Public remains the information about who deposited and withdrew ETH to/from Plasma Cash.
2. The child chain allows for anonymous transactions using zk-SNARKs.
Technologische stack
- NodeJS
- Redis
- Ethereum
- Solid
Testen
In developing Plasma Cash, we tested the system’s performance and received the following results:
- up to 35,000 transactions per second are added to the pool;
- up to 1,000,000 transactions can be stored in a block.
Tests were conducted on the following 3 servers:
1. Intel Core i7-6700 Quad-Core Skylake incl. NVMe SSD — 512 GB, 64 GB DDR4 RAM
3 validating Plasma Cash nodes were launched.
2. AMD Ryzen 7 1700X Octa-Core 'Summit Ridge' (Zen), SATA SSD — 500 GB, 64 GB DDR4 RAM
A Ropsten testnet ETH node was launched.
3 validating Plasma Cash nodes were launched.
3. Intel Core i9-9900K Octa-Core incl. NVMe SSD — 1 TB, 64 GB DDR4 RAM
1 submit Plasma Cash node was launched.
3 validating Plasma Cash nodes were launched.
A test was run to add transactions to the Plasma Cash network.
Kortom: 10 Plasma Cash nodes in a private network.
Test 1
There is a limit of 1 million transactions per block. Therefore, 1 million transactions go into 2 blocks (since the system can capture some transactions and submit them while they are being sent).

Initial state: last block #7; 1 million transactions and tokens are stored in the database.
00:00 — script for generating transactions started
01:37 — 1 million transactions created and sending to node started
01:46 — the submit node took 240k transactions from the pool and forms block #8. We also see that 320k transactions are added to the pool in 10 seconds.
01:58 — block #8 signed and sent for validation.
02:03 — blok #8 gevalideerd en de functie `submitBlock` van het smart contract aangeroepen met de Merkle-hash en bloknummer.
02:10 — de demo-script is gestopt, dat 1 miljoen transacties in 32 seconden heeft verzonden.
02:33 — knooppunten beginnen informatie te ontvangen dat blok #8 aan de hoofdketen is toegevoegd en starten met het uitvoeren van 240k transacties.
02:40 — 240k transacties zijn uit de pool verwijderd, die al in blok #8 zitten.
02:56 — de submit-knoop heeft de resterende 760k transacties uit de pool genomen en begonnen met het berekenen van de Merkle-hash en het ondertekenen van blok #9.
03:20 — alle knooppunten bevatten 1 miljoen 240k transacties en tokens.
03:35 — blok #9 is ondertekend en wordt ter validatie naar andere knooppunten gestuurd.
03:41 — er is een netwerkfout opgetreden.
04:40 — de wachttijd voor de validatie van blok #9 is vanwege time-out beëindigd.
04:54 — de submit-knoop heeft de resterende 760k transacties uit de pool genomen en begonnen met het berekenen van de Merkle-hash en het ondertekenen van blok #9.
05:32 — blok #9 is ondertekend en wordt ter validatie naar andere knooppunten gestuurd.
05:53 — blok #9 is gevalideerd en verzonden naar de hoofdketen.
06:17 — knooppunten beginnen informatie te ontvangen dat blok #9 aan de hoofdketen is toegevoegd en starten met het uitvoeren van 760k transacties.
06:47 — de pool is opgeschoond van transacties die in blok #9 zitten.
09:06 — alle knooppunten bevatten 2 miljoen transacties en tokens.
Test 2
Er geldt een limiet van 350k per blok. Dit resulteert in 3 blokken.

Initiële staat: laatste blok #9; er zijn 2 miljoen transacties en tokens in de database opgeslagen.
00:00 — het script voor het genereren van transacties is al gestart.
00:44 — 1 miljoen transacties zijn aangemaakt en de verzending naar de knoop is begonnen.
00:56 — de submit-knoop heeft 320k transacties uit de pool genomen en vormt blok #10. We zien ook dat er 320k transacties aan de pool worden toegevoegd in 10 seconden.
01:12 — blok #10 is ondertekend en wordt naar andere knooppunten verzonden voor validatie.
01:18 — de demo-script is gestopt, dat 1 miljoen transacties in 34 seconden heeft verzonden.
01:20 — blok #10 is gevalideerd en verzonden naar de hoofdketen.
01:51 — alle knooppunten hebben informatie ontvangen uit de hoofdketen dat blok #10 is toegevoegd, en beginnen 320k transacties toe te passen.
02:01 — de pool is opgeschoond van 320k transacties die in blok #10 zijn toegevoegd.
02:15 — de submit-knoop heeft 350k transacties uit de pool genomen en vormt blok #11.
02:34 — blok #11 is ondertekend en wordt naar andere knooppunten verzonden voor validatie.
02:51 — blok #11 is gevalideerd en verzonden naar de hoofdketen.
02:55 — de laatste knoop heeft de transacties uit blok #10 uitgevoerd.
10:59 — de transactie met de indienen van blok #9 duurde erg lang in de rootketen, maar deze is afgerond en alle knooppunten hebben hier informatie over ontvangen en begonnen met het verwerken van 350k transacties.
11:05 — de pool is opgeschoond met 320k transacties, die zijn toegevoegd aan blok #11.
12:10 — alle knooppunten bevatten 1 miljoen 670k transacties en tokens.
12:17 — de submit-knooppunt nam 330k transacties uit de pool en vormt blok #12.
12:32 — blok #12 is ondertekend en wordt naar andere knooppunten gestuurd voor validatie.
12:39 — blok #12 is gevalideerd en naar de rootketen gestuurd.
13:44 — alle knooppunten hebben informatie uit de rootketen ontvangen dat blok #12 is toegevoegd en beginnen met het toepassen van 330k transacties.
14:50 — alle knooppunten bevatten 2 miljoen transacties en tokens.
Test 3
Op de eerste en tweede servers is één validatieknooppunt vervangen door een submit-knooppunt.

Initiële staat: laatste blok #84; er zijn 0 transacties en tokens in de database opgeslagen.
00:00 — 3 scripts gestart die elk 1 miljoen transacties genereren en verzenden.
01:38 — 1 miljoen transacties zijn aangemaakt en de verzending naar submit-knooppunt #3 is begonnen.
01:50 — submit-knooppunt #3 nam 330k transacties uit de pool en vormt blok #85 (f21). Ook zien we dat er 350k transacties aan de pool worden toegevoegd in 10 seconden.
01:53 — 1 miljoen transacties zijn aangemaakt en de verzending naar submit-knooppunt #1 is begonnen.
01:50 — submit-knooppunt #3 nam 330k transacties uit de pool en vormt blok #85 (f21). Ook zien we dat er 350k transacties aan de pool worden toegevoegd in 10 seconden.
02:01 — submit-knooppunt #1 nam 250k transacties uit de pool en vormt blok #85 (65e).
02:06 — blok #85 (f21) is ondertekend en wordt naar andere knooppunten gestuurd voor validatie.
02:08 — de demo-script op server #3 is gestopt, die 1 miljoen transacties heeft verzonden in 30 seconden.
02:14 — blok #85 (f21) is gevalideerd en naar de rootketen gestuurd.
02:19 — blok #85 (65e) is ondertekend en wordt naar andere knooppunten gestuurd voor validatie.
02:22 — 1 miljoen transacties zijn aangemaakt en de verzending naar submit-knooppunt #2 is begonnen.
02:27 — blok #85 (65e) is gevalideerd en naar de rootketen gestuurd.
02:29 — submit-knooppunt #2 nam 111855 transacties uit de pool en vormt blok #85 (256).
02:36 — blok #85 (256) is ondertekend en wordt naar andere knooppunten gestuurd voor validatie.
02:36 — de demo-script op server #1 is gestopt, die 1 miljoen transacties heeft verzonden in 42,5 seconden.
02:38 — blok #85 (256) is gevalideerd en naar de rootketen gestuurd.
03:08 — de demo-script op server #2 is gestopt, die 1 miljoen transacties heeft verzonden in 47 seconden.
03:38 — alle knooppunten hebben informatie uit de rootketen ontvangen dat blokken #85 (f21), #86 (65e), #87 (256) zijn toegevoegd en beginnen met het toepassen van 330k, 250k, 111855 transacties.
03:49 — de pool is opgeschoond met 330k, 250k, 111855 transacties die zijn toegevoegd aan blokken #85 (f21), #86(65e), #87(256)
03:59 — de submit node #1 heeft 888145 transacties uit de pool genomen en vormt blok #88 (214), de submit node #2 heeft 750k transacties uit de pool genomen en vormt blok #88 (50a), de submit node #3 heeft 670k transacties uit de pool genomen en vormt blok #88 (d3b)
04:44 — blok #88 (d3b) is ondertekend en wordt naar andere nodes gestuurd voor validatie
04:58 — blok #88 (214) is ondertekend en wordt naar andere nodes gestuurd voor validatie
05:11 — blok #88 (50a) is ondertekend en wordt naar andere nodes gestuurd voor validatie
05:11 — blok #85 (d3b) is gevalideerd en naar de wortelketen gestuurd
05:36 — blok #85 (214) is gevalideerd en naar de wortelketen gestuurd
05:43 — alle nodes hebben uit de wortelketen informatie ontvangen dat blokken #88 (d3b), #89(214) zijn toegevoegd en beginnen met het toepassen van 670k, 750k transacties
06:50 — door een verbindingsonderbreking is blok #85 (50a) niet gevalideerd
06:55 — de submit node #2 heeft 888145 transacties uit de pool genomen en vormt blok #90 (50a)
08:14 — blok #90 (50a) is ondertekend en wordt naar andere nodes gestuurd voor validatie
09:04 — blok #90 (50a) is gevalideerd en naar de wortelketen gestuurd
11:23 — alle nodes hebben uit de wortelketen informatie ontvangen dat blok #90 (50a) is toegevoegd, en beginnen met het toepassen van 888145 transacties. Inmiddels heeft server #3 al transacties uit blokken #88 (d3b), #89(214) toegepast
12:11 — alle pools zijn leeg
13:41 — alle nodes van server #3 bevatten 3 miljoen transacties en tokens
14:35 — alle nodes van server #1 bevatten 3 miljoen transacties en tokens
19:24 — alle nodes van server #2 bevatten 3 miljoen transacties en tokens
Obstakels
Tijdens de ontwikkeling van Plasma Cash stuitten we op de volgende problemen, die we geleidelijk hebben opgelost en nog steeds oplossen:
1. Conflict tussen verschillende functies van het systeem. Bijvoorbeeld, de functie om transacties aan de pool toe te voegen blokkeerde de werking van de submit en validatie van blokken, en vice versa, wat leidde tot een vertraging in de snelheid.
2. Het was niet meteen duidelijk hoe een enorme hoeveelheid transacties te verzenden en tegelijkertijd de datakosten te minimaliseren.
3. Het was niet duidelijk hoe en waar gegevens op te slaan om hoge resultaten te bereiken.
4. Het was niet duidelijk hoe het netwerk tussen nodes te organiseren, aangezien de grootte van een blok met 1 miljoen transacties ongeveer 100 MB bedraagt.
5. Werken in een single-threaded modus verstoort de verbinding tussen nodes wanneer er lange berekeningen plaatsvinden (bijvoorbeeld, het bouwen van een Merkle-boom en het berekenen van de hash).
Hoe hebben we dit allemaal aangepakt?
De eerste versie van de Plasma Cash-knooppunt was een soort combinatie die alles tegelijk kon doen: transacties ontvangen, blokken indienen en valideren, en een API bieden voor gegevens toegang. Aangezien NodeJS oorspronkelijk enkelvoudig (single-threaded) is, blokkeerde de zware functie voor het berekenen van de Merkle-boom de functie voor het toevoegen van transacties. We zagen twee mogelijke oplossingen voor dit probleem:
1. Meerdere NodeJS-processen starten, waarbij elk specifieke functies uitvoert.
2. Gebruik maken van worker_threads en een deel van de code naar threads verplaatsen.
Uiteindelijk hebben we beide opties tegelijkertijd gebruikt: we hebben één knooppunt logisch verdeeld in 3 delen die apart kunnen werken maar tegelijkertijd synchroon blijven.
1. Een submissieknooppunt dat transacties in de pool accepteert en verantwoordelijk is voor het creëren van blokken.
2. Een validatieknooppunt dat de geldigheid van knooppunten controleert.
3. API-knooppunt — biedt een API voor gegevens toegang.
Elke knooppunt kan via een unix-socket worden aangesloten via de cli.
Zware bewerkingen, zoals het berekenen van de Merkle-boom, hebben we naar een aparte thread verplaatst.
Op deze manier hebben we ervoor gezorgd dat alle functies van Plasma Cash tegelijkertijd en zonder storingen normaal functioneren.
Zodra het systeem functioneel was, begonnen we met het testen van de snelheid en kregen helaas ontevredenstellende resultaten: 5.000 transacties per seconde en tot 50.000 transacties per blok. We moesten achterhalen wat er verkeerd was geïmplementeerd.
Eerst testten we het communicatie-mechanisme met Plasma Cash om de piekcapaciteit van het systeem te achterhalen. Eerder hebben we vermeld dat het Plasma Cash-knooppunt een unix-socketinterface biedt. Aanvankelijk was het tekstgebaseerd. json-objecten werden verzonden met `JSON.parse()` en `JSON.stringify()`.
{"action": "sendTransaction","payload":{ "prevHash": "0x8a88cc4217745fd0b4eb161f6923235da10593be66b841d47da86b9cd95d93e0","prevBlock": 41,"tokenId": "57570139642005649136210751546585740989890521125187435281313126554130572876445","newOwner": "0x200eabe5b26e547446ae5821622892291632d4f4","type": "pay","data": "","signature": "0xd1107d0c6df15e01e168e631a386363c72206cb75b233f8f3cf883134854967e1cd9b3306cc5c0ce58f0a7397ae9b2487501b56695fe3a3c90ec0f61c7ea4a721c"}}
We hebben de snelheid van de verzending van zulke objecten gemeten en kregen ~ 130k per seconde. We hebben geprobeerd de standaard functies voor json-werking te vervangen, maar de prestaties verbeterden niet. De V8-motor moet goed geoptimaliseerd zijn voor deze operaties.
Werken met transacties, tokens en blokken gebeurden bij ons via klassen. Bij het aanmaken van dergelijke klassen daalde de prestaties met 50%, wat aangeeft dat OOP niet geschikt voor ons is. We moesten alles herschrijven naar een puur functionele aanpak.
Schrijven naar de database
Aanvankelijk werd Redis gekozen voor gegevensopslag als een van de meest performante oplossingen die aan onze eisen voldoet: key-value opslag, werken met hash-tabellen, verzamelingen. We hebben redis-benchmark uitgevoerd en kregen ~80k operaties per seconde in mode van 1 pipelining.
Voor hoge prestaties hebben we Redis finetuned:
- We hebben een unix socket verbinding ingesteld.
- We hebben de opslag van de staat op schijf uitgeschakeld (voor betrouwbaarheid kan er een replica worden ingesteld die de opslag op schijf uitvoert).
In Redis is een pool een hash-tabel, omdat we de mogelijkheid nodig hebben om alle transacties met één verzoek te krijgen en transacties één voor één te verwijderen. We hebben geprobeerd een gewone lijst te gebruiken, maar die werkt trager bij het uitladen van de volledige lijst.
Bij het gebruik van de standaard NodeJS Redis bibliotheek behaalden we 18k transacties per seconde. De snelheid daalde met 9 keer.
Aangezien de benchmark ons duidelijk mogelijkheden van 5 keer meer toonde, begonnen we te optimaliseren. We schakelden over naar de ioredis bibliotheek en behaalden nu al 25k per seconde. We voegden transacties één voor één toe met de `hset` opdracht. Op die manier genereerden we veel verzoeken naar Redis. Het idee ontstond om transacties in batches samen te voegen en ze met één opdracht `hmset` te verzenden. Resultaat — 32k per seconde.
Om verschillende redenen die we hieronder zullen beschrijven, werken we met gegevens door gebruik te maken van `Buffer` en, zoals het bleek, als je het naar tekst (`buffer.toString('hex')`) omzet voordat je het opslaat, kun je extra prestaties krijgen. Op die manier is de snelheid verhoogd tot 35k per seconde. Op dit moment hebben we besloten verder te optimaliseren te pauzeren.
We moesten overschakelen naar een binaire protocol omdat:
1. Het systeem berekent vaak hashes, handtekeningen, enzovoorts, en daarvoor zijn gegevens in `Buffer` nodig.
2. Bij verzending tussen services wegen binaire gegevens minder dan tekst. Bijvoorbeeld, bij het verzenden van een blok met 1 miljoen transacties kunnen de gegevens in tekst meer dan 300 megabyte in beslag nemen.
3. Voortdurende conversie van gegevens heeft invloed op de prestaties.
Daarom hebben we ons eigen binaire gegevensopslag- en -overdrachtsprotocol gebaseerd op de geweldige bibliotheek `binary-data` genomen.
Het resultaat is dat we de volgende datastructuren hebben gekregen:
— Transactie
```json
{
prevHash: BD.types.buffer(20),
prevBlock: BD.types.uint24le,
tokenId: BD.types.string(null),
type: BD.types.uint8,
newOwner: BD.types.buffer(20),
dataLength: BD.types.uint24le,
data: BD.types.buffer(({current}) => current.dataLength),
signature: BD.types.buffer(65),
hash: BD.types.buffer(32),
blockNumber: BD.types.uint24le,
timestamp: BD.types.uint48le,
}
```
— Token
```json
{
id: BD.types.string(null),
owner: BD.types.buffer(20),
block: BD.types.uint24le,
amount: BD.types.string(null),
}
```
— Blok
```json
{
number: BD.types.uint24le,
merkleRootHash: BD.types.buffer(32),
signature: BD.types.buffer(65),
countTx: BD.types.uint24le,
transactions: BD.types.array(Transaction.Protocol, ({current}) => current.countTx),
timestamp: BD.types.uint48le,
}
```
Met de gewone commando's `BD.encode(block, Protocol).slice();` en ` BD.decode(buffer, Protocol)` zetten we de gegevens om naar `Buffer` voor opslag in Redis of verzending naar een andere node en halen we de gegevens weer terug.
We hebben ook 2 binaire protocollen voor gegevensoverdracht tussen diensten:
— Protocol voor interactie met Plasma Node via unix socket
```json
{
type: BD.types.uint8,
messageId: BD.types.uint24le,
error: BD.types.uint8,
length: BD.types.uint24le,
payload: BD.types.buffer(({node}) => node.length)
}
```
waarbij:
- `type` — de actie die moet worden uitgevoerd, bijvoorbeeld, 1 — sendTransaction, 2 — getTransaction;
- `payload` — de gegevens die naar de overeenkomstige functie moeten worden verzonden;
- `messageId` — het id van het bericht, zodat het antwoord kan worden geïdentificeerd.
— Protocol voor interactie tussen nodes
```json
{
code: BD.types.uint8,
versionProtocol: BD.types.uint24le,
seq: BD.types.uint8,
countChunk: BD.types.uint24le,
chunkNumber: BD.types.uint24le,
length: BD.types.uint24le,
payload: BD.types.buffer(({node}) => node.length)
}
```
waarbij:
- `code` — de code van het bericht, bijvoorbeeld 6 — PREPARE_NEW_BLOCK, 7 — BLOCK_VALID, 8 — BLOCK_COMMIT;
- `versionProtocol` — de versie van het protocol, omdat er nodes in het netwerk kunnen zijn met verschillende versies die anders kunnen werken;
- `seq` — de identificatie van het bericht;
- `countChunk` en `chunkNumber` nodig voor het splitsen van grote berichten;
- `length` en `payload` de lengte en de gegevens zelf.
Omdat we de gegevens van tevoren hebben getypeerd, werkt het eind systeem veel sneller dan de `rlp` bibliotheek van Ethereum. Helaas is het ons nog niet gelukt om ervan af te stappen, omdat we het smart contract moeten aanpassen, wat we van plan zijn te doen in de toekomst.
Als we erin geslaagd zijn om snelheid te bereiken 35 000 transacties per seconde, moeten we ze ook binnen een optimale tijd verwerken. Aangezien de geschatte tijd voor het vormen van een blok 30 seconden bedraagt, moeten we opnemen in het blok 1 000 000 transacties, wat betekent dat meer dan 100 mb aan gegevens wordt verzonden.
In het begin gebruikten we de `ethereumjs-devp2p` bibliotheek voor de communicatie tussen nodes, maar deze kon niet omgaan met zo'n hoeveelheid gegevens. Daarom maakten we gebruik van de `ws` bibliotheek en configureerden we de verzending van binaire gegevens via WebSocket. Natuurlijk stuiten we ook op problemen bij het verzenden van grote pakketten gegevens, maar we hebben ze in chunks opgedeeld en nu zijn die problemen er niet meer.
Ook het vormen van de Merkle-boom en het berekenen van de hash 1 000 000 transacties vereist ongeveer 10 seconden aan continue berekening. In die tijd kan de verbinding met alle nodes verbroken raken. Het werd besloten om deze berekening naar een aparte thread te verplaatsen.
Conclusies:
In feite zijn onze conclusies niet nieuw, maar om de een of andere reden vergeten veel specialisten deze tijdens de ontwikkeling.
- Het gebruik van Functionele Programmeren in plaats van Objectgeoriënteerd Programmeren verhoogt de prestaties.
- Een monolith is slechter dan een service-architectuur voor een productieve NodeJS-systeem.
- Het gebruik van `worker_threads` voor zware berekeningen verbetert de responsiviteit van het systeem, vooral bij het werken met I/O-operaties.
- unix-socket is stabieler en sneller dan http-verzoeken.
- Als je snel grote gegevens over het netwerk moet verzenden, is het beter om WebSockets te gebruiken en binaire gegevens te verzenden, opgesplitst in chunks, die opnieuw kunnen worden verzonden als ze niet aankomen, om ze vervolgens weer tot één bericht te combineren.
We nodigen je uit om GitHub van het project:
Dit artikel is mede geschreven door Alexander Nashivan, senior ontwikkelaar bij .
Bron: habr.com
