UNPKG

@diva.exchange/i2p-sam

Version:

I2P SAM: peer-to-peer communication between applications over I2P

168 lines (148 loc) 6.21 kB
/** * Copyright 2021-2023 diva.exchange * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Author/Maintainer: DIVA.EXCHANGE Association, https://diva.exchange */ import { Configuration, createRaw, I2pSamRaw } from '../../lib/index.js'; import fs from 'fs'; import path from 'path'; import crypto from 'crypto'; const SAM_HOST: string = process.env.SAM_HOST || '172.19.74.11'; const SAM_PORT_TCP: number = Number(process.env.SAM_PORT_TCP) || 7656; const SAM_PORT_UDP: number = Number(process.env.SAM_PORT_UDP) || 7655; const SAM_LISTEN_ADDRESS: string = process.env.SAM_LISTEN_ADDRESS || '0.0.0.0'; const SAM_LISTEN_PORT: number = Number(process.env.SAM_LISTEN_PORT) || 20222; const SAM_LISTEN_FORWARD: string = process.env.SAM_LISTEN_FORWARD || '172.19.74.1'; const DATA_BYTES: number = 8 * 1024; class TestRawLargeSizeBenchmark { private readonly minutes: number; private readonly interval: number; // ms private readonly listenPort: number; private readonly optionsSession: string; private readonly pathCSV: string; constructor( minutes: number = 3, interval: number = 100, listenPort: number = SAM_LISTEN_PORT, optionsSession: string = '' ) { this.minutes = minutes; this.interval = interval; this.listenPort = listenPort; this.optionsSession = optionsSession; const now: Date = new Date(); this.pathCSV = fs.realpathSync(path.dirname(import.meta.url.replace(/^file:\/\//, '')) + '/../data/') + '/'; this.pathCSV += `${now.toISOString().replace(/[.\-:]/g, '')}-${listenPort}-LargeSizeRawBenchmark.csv`; } async run() { let messageReceived = 0; const arrayPerformance: Array<number> = []; let arrayCSV: Array<Array<number>> = []; let destinationSender = ''; let destinationRecipient = ''; console.log(`${this.listenPort} / Creating Sender...`); const config: Configuration = { sam: { host: SAM_HOST, portTCP: SAM_PORT_TCP, portUDP: SAM_PORT_UDP }, listen: { address: SAM_LISTEN_ADDRESS, port: this.listenPort, hostForward: SAM_LISTEN_FORWARD, }, }; if (this.optionsSession) { config.session = { options: this.optionsSession }; } const i2pSender: I2pSamRaw = (await createRaw(config)).on('data', (msg: Buffer) => { messageReceived++; const n: number = Date.now(); const l: number = n - Number(msg.toString().substring(0, msg.toString().indexOf('//'))); arrayPerformance.push(l); arrayCSV.push([n, l]); }); destinationSender = i2pSender.getPublicKey(); console.log(`${this.listenPort} / Creating Recipient...`); config.listen?.port && (config.listen.port = this.listenPort + 1); const i2pRecipient: I2pSamRaw = (await createRaw(config)).on('data', (msg: Buffer) => { messageReceived++; const n: number = Date.now(); const l: number = n - Number(msg.toString().substring(0, msg.toString().indexOf('//'))); arrayPerformance.push(l); arrayCSV.push([n, l]); }); destinationRecipient = i2pRecipient.getPublicKey(); console.log(`${this.listenPort} / Starting & running for ${this.minutes}mins / ${Date.now()}`); let sentMsgs: number = 0; const intervalSender = setInterval(async () => { i2pSender.send( destinationRecipient, Buffer.concat([Buffer.from(Date.now().toString() + '//'), crypto.randomFillSync(Buffer.alloc(DATA_BYTES))]) ); sentMsgs++; }, this.interval); await TestRawLargeSizeBenchmark.wait(Math.floor(this.interval / 2.1)); const intervalRecipient = setInterval(async () => { i2pRecipient.send( destinationSender, Buffer.concat([Buffer.from(Date.now().toString() + '//'), crypto.randomFillSync(Buffer.alloc(DATA_BYTES))]) ); sentMsgs++; }, this.interval); let n: number = 0; const fCSV = fs.openSync(this.pathCSV, 'a'); fs.writeSync(fCSV, `Params: ${this.minutes}/${this.interval}/${DATA_BYTES}/${this.optionsSession}\r\n`); while (n++ < this.minutes) { await TestRawLargeSizeBenchmark.wait(60000); // write csv arrayCSV.forEach((a) => { fs.writeSync(fCSV, a.join(',') + '\r\n'); }); arrayCSV = []; console.log(`${this.listenPort} / ${n} / sent ${sentMsgs} messages`); } fs.closeSync(fCSV); clearInterval(intervalSender); clearInterval(intervalRecipient); i2pSender.close(); i2pRecipient.close(); console.log(`${this.listenPort} / sent ${sentMsgs} messages / ${Date.now()}`); console.log(`${this.listenPort} / arrived: ${((messageReceived / sentMsgs) * 100).toFixed(1)}%`); // latency distribution const a: Array<number> = arrayPerformance.sort((a: number, b: number) => a - b); console.log(`${this.listenPort} / Latency, min: ${a[0]}`); console.log(`${this.listenPort} / Latency, 1st quartile: ${a[Math.floor(a.length / 4)]}`); console.log(`${this.listenPort} / Latency, median: ${a[Math.floor(a.length / 2)]}`); console.log(`${this.listenPort} / Latency, 3rd quartile: ${a[Math.floor((a.length / 4) * 3)]}`); console.log(`${this.listenPort} / Latency, max: ${a[a.length - 1]}`); } static async wait(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } } const jobsDone: Array<boolean> = []; const jobs: Array<Function> = [ async (): Promise<void> => { await new TestRawLargeSizeBenchmark( 15, 500, 20221, 'inbound.lengthVariance=2 outbound.lengthVariance=2 shouldBundleReplyInfo=false' ).run(); jobsDone.push(true); }, ]; jobs.forEach((f) => f()); while (jobsDone.length < jobs.length) { await TestRawLargeSizeBenchmark.wait(1000); }