Commit 8b2ebc9c authored by 神楽坂玲奈's avatar 神楽坂玲奈

plan

parent ea6805ab
{ {
"port": 500, "port": 500,
"timeout": 10, "timeout": 10,
"interval": 1000 "interval": 1000,
"throttle": 10
} }
[
{
"id": 1800,
"routers": [
1,
3
]
}
]
...@@ -16,8 +16,8 @@ export interface UploadMessage { ...@@ -16,8 +16,8 @@ export interface UploadMessage {
// 中心服务器向路由器发送的消息 // 中心服务器向路由器发送的消息
export interface DownloadMessage { export interface DownloadMessage {
seq: number, seq: number,
to: number, via: Record<number, number>,
via: number; plan: Record<number, number>
} }
// 路由器向路由器发送的消息 // 路由器向路由器发送的消息
......
import { RemoteInfo } from 'dgram'; import { RemoteInfo, Socket } from 'dgram';
import { PeerQuality } from '../protocol'; import { DownloadMessage, PeerQuality, UploadMessage } from '../protocol';
import routers from '../config/routers.json'; import routers from '../config/routers.json';
import plans from '../config/plans.json';
import assert from 'assert'; import assert from 'assert';
import { Quality } from './Quality'; import { Quality } from './Quality';
import _ from 'lodash';
import config from '../config/config.json';
export class Router { export class Router {
static all: Router[] = routers.map(s => Object.assign(new Router(), s)); static updating?: Router;
static updating_timer: any;
static timeout_timer: any;
static all: Router[] = routers.map(s => new Router(s));
// config // config
id!: number; id!: number;
...@@ -15,15 +22,24 @@ export class Router { ...@@ -15,15 +22,24 @@ export class Router {
seq = 0; seq = 0;
peers: Record<number, PeerQuality> = {}; peers: Record<number, PeerQuality> = {};
via: Map<Router, Router> = new Map(); via: Map<Router, Router> = new Map();
plan: Record<number, number> = {};
rinfo?: RemoteInfo; rinfo?: RemoteInfo;
constructor(config: Partial<Router>) {
Object.assign(this, config);
this.reset();
}
reset() { reset() {
this.seq = 0; this.seq = 0;
this.peers = {}; this.peers = {};
for (const router of Router.all.filter(r => r.id !== this.id)) { for (const router of Router.all.filter(r => r.id !== this.id)) {
this.via.set(router, router); this.via.set(router, router);
} }
for (const plan of plans.filter(plan => !plan.routers.includes(this.id))) {
this.plan[plan.id] = Router.all.find(r => r.id == plan.routers[0])!.id;
}
} }
lost() { lost() {
...@@ -32,8 +48,8 @@ export class Router { ...@@ -32,8 +48,8 @@ export class Router {
return console.log(`router ${this.id} lost connection.`); return console.log(`router ${this.id} lost connection.`);
} }
route_quality(to: Router, via: Router) { route_quality(to: Router, via: Router = this.via.get(to)!) {
assert(via != null); assert(via !== undefined);
assert(this !== via); assert(this !== via);
assert(this !== to); assert(this !== to);
...@@ -61,8 +77,66 @@ export class Router { ...@@ -61,8 +77,66 @@ export class Router {
} }
} }
} }
}
for (const router of Router.all) { onMessage(hello: UploadMessage) {
router.reset(); if (hello.ack === 0) this.reset();
if (hello.peers) this.peers = hello.peers;
if (Router.updating === this && hello.ack === this.seq + 1) {
clearInterval(Router.updating_timer);
clearTimeout(Router.timeout_timer);
Router.updating = undefined;
this.seq++;
}
}
update(socket: Socket) {
const changedVia: Record<number, number> = {};
const changedPlan: Record<number, number> = {};
const metric: Record<number, number> = {};
for (const to of Router.all.filter(r => r.id !== this.id)) {
// 计算最优下一跳
const items: [Router, number][] = Router.all.filter(r => r.id !== this.id).map(r => [r, this.route_quality(to, r).metric()]);
const [currentRoute, currentMetric] = items.find(([v, m]) => v === this.via.get(to))!;
const [bestRoute, bestMetric] = _.minBy(items, (([v, m]) => m))!;
// 变更
if (currentRoute !== bestRoute && bestMetric + config.throttle < currentMetric) {
changedVia[to.id] = bestRoute.id;
this.via.set(to, bestRoute);
metric[to.id] = bestMetric;
// console.log(`change: this ${this.id} to ${to.id} current ${this.via.get(to)!.id} quality ${JSON.stringify(this.route_quality(to, this.via.get(to)!))} metric ${this.route_quality(to, this.via.get(to)!).metric()} best ${best_route.id} quality ${JSON.stringify(this.route_quality(to, best_route))} metric ${this.route_quality(to, best_route).metric()}`);
} else {
metric[to.id] = currentMetric;
}
}
for (const plan of plans.filter(plan => !plan.routers.includes(this.id))) {
const currentPlan = this.plan[plan.id];
const bestPlan = _.minBy(plan.routers, to => metric[to])!;
if (currentPlan !== bestPlan) {
changedPlan[plan.id] = bestPlan;
}
}
if (!_.isEmpty(changedVia) || !_.isEmpty(changedPlan)) {
Router.updating = this;
Router.updating_timer = setInterval(() => this.send(socket, changedVia, changedPlan), config.interval);
this.send(socket, changedVia, changedPlan);
Router.timeout_timer = setTimeout(() => {
this.lost();
clearInterval(Router.updating_timer);
Router.updating = undefined;
}, config.timeout * config.interval);
}
}
send(socket: Socket, via: Record<number, number>, plan: Record<number, number>) {
if (!this.rinfo) return;
const message: DownloadMessage = { seq: this.seq, via, plan };
console.log(this.id, message);
return socket.send(JSON.stringify(message), this.rinfo.port, this.rinfo.address);
}
} }
import dgram from 'dgram'; import dgram from 'dgram';
import http from 'http'; import http from 'http';
import { DownloadMessage, UploadMessage } from '../protocol'; import { UploadMessage } from '../protocol';
import assert from 'assert'; import assert from 'assert';
import { Router } from './Router'; import { Router } from './Router';
import _ from 'lodash';
import config from '../config/config.json'; import config from '../config/config.json';
let updating: any = null;
let timeout_timer: any = null;
export function send_route(from: Router, to: Router, via: Router) {
if (!from.rinfo) return;
const message: DownloadMessage = { seq: from.seq, to: to.id, via: via.id };
console.log(from.id, message);
return socket.send(JSON.stringify(message), from.rinfo.port, from.rinfo.address);
}
const socket = dgram const socket = dgram
.createSocket('udp4') .createSocket('udp4')
.on('listening', () => { .on('listening', () => {
...@@ -33,46 +21,11 @@ const socket = dgram ...@@ -33,46 +21,11 @@ const socket = dgram
assert(router); assert(router);
router.rinfo = rinfo; router.rinfo = rinfo;
router.onMessage(hello);
if (hello.ack === 0) {
router.reset();
}
if (hello.peers) router.peers = hello.peers;
if (updating) {
if (hello.ack === router.seq + 1) {
clearInterval(updating);
clearTimeout(timeout_timer);
updating = null;
router.seq++;
}
return;
}
} catch (e) { } catch (e) {
console.warn(e); console.warn(e);
} }
for (const from of Router.all) {
for (const to of Router.all.filter(r => r.id !== from.id)) {
// 计算最优下一跳
const best_route = _.minBy(Router.all.filter(router => router.id !== from.id), (router) => from.route_quality(to, router).metric())!;
// 变更
if (from.via.get(to) !== best_route && from.route_quality(to, best_route).metric() + 1 < from.route_quality(to, from.via.get(to)!).metric()) {
console.log(`change: from ${from.id} to ${to.id} current ${from.via.get(to)!.id} quality ${JSON.stringify(from.route_quality(to, from.via.get(to)!))} metric ${from.route_quality(to, from.via.get(to)!).metric()} best ${best_route.id} quality ${JSON.stringify(from.route_quality(to, best_route))} metric ${from.route_quality(to, best_route).metric()}`);
from.via.set(to, best_route);
updating = setInterval(() => send_route(from, to, best_route), config.interval);
send_route(from, to, best_route);
timeout_timer = setTimeout(function() {
from.lost();
clearInterval(updating);
updating = null;
}, config.timeout * config.interval);
return;
}
}
}
}); });
socket.bind(config.port); socket.bind(config.port);
...@@ -93,3 +46,10 @@ http ...@@ -93,3 +46,10 @@ http
console.log(`http listening ${address.address}:${address.port}`); console.log(`http listening ${address.address}:${address.port}`);
}) })
.listen(config.port); .listen(config.port);
setInterval(() => {
for (const from of Router.all) {
from.update(socket);
if (Router.updating) return;
}
}, config.interval);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment