ip_monitor.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. import debug from "debug"
  2. import net from 'node:net';
  3. const log = {
  4. app: debug('ip_monitor')
  5. };
  6. // IP 监控配置接口
  7. interface IPMonitorConfig {
  8. timeout?: number; // 超时时间(毫秒)
  9. retries?: number; // 重试次数
  10. interval?: number; // 监控间隔(毫秒)
  11. }
  12. // IP 监控结果接口
  13. export interface IPMonitorResult {
  14. success: boolean; // 是否成功
  15. responseTime?: number; // 响应时间(毫秒)
  16. packetLoss?: number; // 丢包率(百分比)
  17. error?: string; // 错误信息
  18. }
  19. // 默认配置
  20. const DEFAULT_CONFIG: IPMonitorConfig = {
  21. timeout: 1000,
  22. retries: 3,
  23. interval: 60000
  24. };
  25. // IP 监控类
  26. export class IPMonitor {
  27. private config: IPMonitorConfig;
  28. private monitoring: Map<string, number> = new Map();
  29. constructor(config: IPMonitorConfig = {}) {
  30. this.config = { ...DEFAULT_CONFIG, ...config };
  31. }
  32. // 开始监控指定 IP
  33. async startMonitor(ip: string, callback: (result: IPMonitorResult) => void) {
  34. if (this.monitoring.has(ip)) {
  35. log.app(`IP ${ip} 已经在监控中`);
  36. return;
  37. }
  38. const monitor = async () => {
  39. try {
  40. const result = await this.ping(ip);
  41. callback(result);
  42. } catch (error) {
  43. log.app(`监控 IP ${ip} 时发生错误:`, error);
  44. callback({
  45. success: false,
  46. error: error instanceof Error ? error.message : '未知错误'
  47. });
  48. }
  49. };
  50. // 立即执行一次
  51. await monitor();
  52. // 设置定时监控
  53. const interval = setInterval(monitor, this.config.interval);
  54. this.monitoring.set(ip, interval);
  55. log.app(`开始监控 IP: ${ip}`);
  56. }
  57. // 停止监控指定 IP
  58. stopMonitor(ip: string) {
  59. const interval = this.monitoring.get(ip);
  60. if (interval) {
  61. clearInterval(interval);
  62. this.monitoring.delete(ip);
  63. log.app(`停止监控 IP: ${ip}`);
  64. }
  65. }
  66. // 停止所有监控
  67. stopAll() {
  68. for (const [ip, interval] of this.monitoring) {
  69. clearInterval(interval);
  70. this.monitoring.delete(ip);
  71. }
  72. log.app('已停止所有 IP 监控');
  73. }
  74. // 执行 ping 操作
  75. private async ping(ip: string): Promise<IPMonitorResult> {
  76. const results: number[] = [];
  77. let successCount = 0;
  78. let totalTime = 0;
  79. for (let i = 0; i < this.config.retries!; i++) {
  80. try {
  81. const startTime = Date.now();
  82. const result = await this.tcpPing(ip);
  83. const endTime = Date.now();
  84. if (result) {
  85. const responseTime = endTime - startTime;
  86. results.push(responseTime);
  87. totalTime += responseTime;
  88. successCount++;
  89. }
  90. } catch (error) {
  91. log.app(`Ping 尝试 ${i + 1} 失败:`, error);
  92. }
  93. }
  94. // 计算平均响应时间和丢包率
  95. const avgResponseTime = successCount > 0 ? totalTime / successCount : undefined;
  96. const packetLoss = ((this.config.retries! - successCount) / this.config.retries!) * 100;
  97. return {
  98. success: successCount > 0,
  99. responseTime: avgResponseTime,
  100. packetLoss: packetLoss
  101. };
  102. }
  103. // TCP ping 实现
  104. private async tcpPing(ip: string): Promise<boolean> {
  105. return new Promise((resolve, reject) => {
  106. const socket = new net.Socket();
  107. let timeout = false;
  108. // 设置超时
  109. const timer = setTimeout(() => {
  110. timeout = true;
  111. socket.destroy();
  112. reject(new Error('连接超时'));
  113. }, this.config.timeout);
  114. socket.on('connect', () => {
  115. clearTimeout(timer);
  116. socket.destroy();
  117. resolve(true);
  118. });
  119. socket.on('error', (error) => {
  120. clearTimeout(timer);
  121. if (!timeout) {
  122. reject(error);
  123. }
  124. });
  125. // 尝试连接
  126. socket.connect(80, ip);
  127. });
  128. }
  129. }
  130. // 导出单例实例
  131. export const ipMonitor = new IPMonitor();