Flutter远程连接管理插件telnet的使用

发布于 1周前 作者 phonegap100 来自 Flutter

Flutter远程连接管理插件telnet的使用

使用 Dart 语言实现的 Telnet 客户端。

功能特点

  • 支持选项协商、子选项协商和文本消息传输
  • 支持 TLS 安全传输
  • 消息事件侦听
  • 枚举了所有的 Telnet 命令码和选项码

使用方法

创建一个 Telnet 连接任务:

// 常规方式
final task = TelnetClient.startConnect(
  host: host, 
  port: port, 
  timeout: timeout,
  onError: onError,
  onDone: onDone,
  onEvent: onEvent,
);

// 使用 TLS 安全传输
final task = TelnetClient.startSecureConnect(
  host: host,
  port: port,
  timeout: timeout,
  onError: onError,
  onDone: onDone,
  onEvent: onEvent,
  securityContext: securityContext,
  supportedProtocols: supportedProtocols,
  onBadCertificate: onBadCertificate,
);

等待连接任务结束,然后获取 TelnetClient 实例对象:

// 同步方式
await task.waitDone();
final client = task.client;
final connected = client != null;

// 异步方式
task.onDone = (client) {
  final connected = client != null;
}

取消连接任务:

task.cancel();

关闭 Telnet 连接:

client.terminate();

侦听并处理消息事件:

final task = TelnetClient.startConnect(
  host: host, 
  port: port, 
  onEvent: (client, event) {
    final eventType = event.type;
    final eventMsg = event.msg;
    
    if (eventType == TLMsgEventType.write) {
      print("这是一个写事件,数据由客户端发往服务端。");
    } else if (eventType == TLMsgEventType.read) {
      print("这是一个读事件,数据由服务端发往客户端。");
    }
    
    if (eventMsg is TLOptMsg) {
      // 选项协商
      print("IAC ${eventMsg.cmd.code} ${eventMsg.opt.code}");
    } else if (eventMsg is TLSubMsg) {
      // 子选项协商
      print("IAC SB ${eventMsg.opt.code} ${eventMsg.arg.join(' ')} IAC SE");
    } else if (eventMsg is TLTextMsg) {
      // 文本消息
      print(eventMsg.text);
    }
  },
);

完整使用方法示例

以下是一个完整的示例代码,展示了如何使用 telnet 插件进行远程连接管理。

import 'package:telnet/telnet.dart';

const host = "127.0.0.1";
const port = 23;
const username = "root";
const password = "admin";
const echoEnabled = true;

void main() async {
  // 创建一个 Telnet 连接任务。
  final task = TelnetClient.startConnect(
    host: host,
    port: port,
    onEvent: _onEvent,
    onError: _onError,
    onDone: _onDone,
  );

  // 取消连接任务。
  // task.cancel();

  // 等待连接任务完成。
  await task.waitDone();

  // 获取 `TelnetClient` 实例。如果连接失败,则为 `null`。
  final client = task.client;
  if (client == null) {
    print("无法连接到 $host:$port");
  } else {
    print("成功连接到 $host:$port");
  }

  await Future.delayed(const Duration(seconds: 10));

  // 关闭 Telnet 连接。
  await client?.terminate();
}

var _hasLogin = false;
final _willReplyMap = <TLOpt, List<TLMsg>>{
  TLOpt.echo: [echoEnabled
      ? TLOptMsg(TLCmd.doIt, TLOpt.echo)                      // [IAC DO ECHO]
      : TLOptMsg(TLCmd.doNot, TLOpt.echo)],                   // [IAC DON'T ECHO]
  TLOpt.suppress: [TLOptMsg(TLCmd.doIt, TLOpt.suppress)],     // [IAC DO SUPPRESS_GO_AHEAD]
  TLOpt.logout: [],
};
final _doReplyMap = <TLOpt, List<TLMsg>>{
  TLOpt.echo: [echoEnabled
      ? TLOptMsg(TLCmd.will, TLOpt.echo)                      // [IAC WILL ECHO]
      : TLOptMsg(TLCmd.wont, TLOpt.echo)],                    // [IAC WONT ECHO]
  TLOpt.logout: [],
  TLOpt.tmlType: [
    TLOptMsg(TLCmd.will, TLOpt.tmlType),                      // [IAC WILL TERMINAL_TYPE]
    TLSubMsg(TLOpt.tmlType, [0x00, 0x41, 0x4E, 0x53, 0x49]),  // [IAC SB TERMINAL_TYPE IS ANSI IAC SE]
  ],
  TLOpt.windowSize: [
    TLOptMsg(TLCmd.will, TLOpt.windowSize),                   // [IAC WILL WINDOW_SIZE]
    TLSubMsg(TLOpt.windowSize, [0x00, 0x5A, 0x00, 0x18]),     // [IAC SB WINDOW_SIZE 90 24 IAC SE]
  ],
};

void _onEvent(TelnetClient? client, TLMsgEvent event) {
  if (event.type == TLMsgEventType.write) {
    print("[WRITE] ${event.msg}");

  } else if (event.type == TLMsgEventType.read) {
    print("[READ] ${event.msg}");

    if (event.msg is TLOptMsg) {
      final cmd = (event.msg as TLOptMsg).cmd; // Telnet Negotiation Command.
      final opt = (event.msg as TLOptMsg).opt; // Telnet Negotiation Option.

      if (cmd == TLCmd.wont) {
        // Write [IAC DO opt].
        client?.write(TLOptMsg(TLCmd.doNot, opt));
      } else if (cmd == TLCmd.doNot) {
        // Write [IAC WON'T opt].
        client?.write(TLOptMsg(TLCmd.wont, opt));
      } else if (cmd == TLCmd.will) {
        if (_willReplyMap.containsKey(opt)) {
          // Reply the option.
          for (var msg in _willReplyMap[opt]!) {
            client?.write(msg);
          }
        } else {
          // Write [IAC DON'T opt].
          client?.write(TLOptMsg(TLCmd.doNot, opt));
        }
      } else if (cmd == TLCmd.doIt) {
        // Reply the option.
        if (_doReplyMap.containsKey(opt)) {
          for (var msg in _doReplyMap[opt]!) {
            client?.write(msg);
          }
        } else {
          // Write [IAC WON'T opt].
          client?.write(TLOptMsg(TLCmd.wont, opt));
        }
      }

    } else if (!_hasLogin && event.msg is TLTextMsg) {

      final text = (event.msg as TLTextMsg).text.toLowerCase();
      if (text.contains("welcome")) {
        _hasLogin = true;
        print("[INFO] 登录成功!");
      } else if (text.contains("login:") || text.contains("username:")) {
        // Write [username].
        client!.write(TLTextMsg("$username\r\n"));
      } else if (text.contains("password:")) {
        // Write [password].
        client!.write(TLTextMsg("$password\r\n"));
      }

    }
  }
}

void _onError(TelnetClient? client, dynamic error) {
  print("[ERROR] $error");
}

void _onDone(TelnetClient? client) {
  print("[DONE]");
}

更多关于Flutter远程连接管理插件telnet的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter远程连接管理插件telnet的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter应用中实现远程连接管理,特别是使用Telnet协议,通常需要借助一些原生平台(如Android和iOS)的代码来实现,因为Flutter本身并不直接支持Telnet协议。不过,可以通过插件或者平台通道来调用原生代码完成这项任务。

以下是一个简化的例子,展示如何在Flutter中通过平台通道调用原生代码来实现Telnet连接。这个例子将分为Flutter前端代码和原生后端代码两部分。

Flutter前端代码

首先,在Flutter项目中创建一个新的插件(或者使用现有的插件)来定义平台通道。这里我们假设已经创建了一个名为telnet_plugin的插件。

// telnet_plugin.dart
import 'package:flutter/services.dart';

class TelnetPlugin {
  static const MethodChannel _channel = const MethodChannel('com.example.telnet_plugin');

  static Future<void> connectToTelnet(String host, int port, String username, String password) async {
    try {
      await _channel.invokeMethod('connectToTelnet', {
        'host': host,
        'port': port,
        'username': username,
        'password': password,
      });
    } on PlatformException catch (e) {
      print("Failed to connect to Telnet: '${e.message}'.");
    }
  }
}

然后,在你的Flutter应用中使用这个插件:

// main.dart
import 'package:flutter/material.dart';
import './telnet_plugin.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Telnet Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              TelnetPlugin.connectToTelnet('your.telnet.server', 23, 'username', 'password');
            },
            child: Text('Connect to Telnet'),
          ),
        ),
      ),
    );
  }
}

原生后端代码

Android部分

android/app/src/main/java/com/example/yourappname/目录下创建一个新的Kotlin/Java类来处理Telnet连接。

// TelnetHandler.kt
package com.example.yourappname

import android.content.Context
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import java.io.*
import java.net.Socket

class TelnetHandler(private val context: Context, private val channel: MethodChannel) : MethodCallHandler, FlutterPlugin, ActivityAware {

    private var activityBinding: ActivityPluginBinding? = null

    override fun onMethodCall(call: MethodCall, result: Result) {
        if (call.method == "connectToTelnet") {
            val arguments = call.arguments as? Map<*, *> ?: return
            val host = arguments["host"] as? String ?: return
            val port = (arguments["port"] as? Number)?.toInt() ?: return
            val username = arguments["username"] as? String ?: return
            val password = arguments["password"] as? String ?: return

            connectToTelnet(host, port, username, password, result)
        } else {
            result.notImplemented()
        }
    }

    private fun connectToTelnet(host: String, port: Int, username: String, password: String, result: Result) {
        try {
            val socket = Socket(host, port)
            val output = PrintWriter(BufferedWriter(OutputStreamWriter(socket.getOutputStream())), true)
            val input = BufferedReader(InputStreamReader(socket.getInputStream()))

            output.println("$username$password") // 简单的登录示例,实际需要根据协议调整

            // 读取响应
            var line: String?
            while (input.readLine().also { line = it } != null) {
                println(line)
            }

            socket.close()
            result.success(null)
        } catch (e: Exception) {
            result.error("TELNET_ERROR", e.message, null)
        }
    }

    override fun onAttachedToEngine(binding: FlutterPluginBinding) {
        channel.setMethodCallHandler(this)
    }

    override fun onDetachedFromEngine(binding: FlutterPluginBinding) {
        channel.setMethodCallHandler(null)
    }

    override fun onAttachedToActivity(binding: ActivityPluginBinding) {
        activityBinding = binding
    }

    override fun onDetachedFromActivityForConfigChanges() {
        activityBinding = null
    }

    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
        activityBinding = binding
    }

    override fun onDetachedFromActivity() {
        activityBinding = null
    }
}

然后,在MainActivity.kt中注册这个插件:

// MainActivity.kt
package com.example.yourappname

import android.os.Bundle
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugins.GeneratedPluginRegistrant

class MainActivity: FlutterActivity() {
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        GeneratedPluginRegistrant.registerWith(flutterEngine)

        val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.telnet_plugin")
        val context = applicationContext
        TelnetHandler(context, channel)
    }
}

iOS部分

对于iOS,你需要使用Swift或Objective-C来实现类似的逻辑。由于篇幅限制,这里只提供一个简单的Swift框架:

// TelnetHandler.swift
import Flutter

public class TelnetHandler: NSObject, FlutterPlugin {
    
    public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "com.example.telnet_plugin", binaryMessenger: registrar.messenger())
        let instance = TelnetHandler()
        channel.setMethodCallHandler(onMethodCall: instance.handle(_:result:))
    }
    
    private func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard call.method == "connectToTelnet" else {
            result(.notImplemented())
            return
        }
        
        guard let arguments = call.arguments as? [String: Any],
              let host = arguments["host"] as? String,
              let port = arguments["port"] as? Int,
              let username = arguments["username"] as? String,
              let password = arguments["password"] as? String else {
            result(.error(withCode: "INVALID_ARGUMENTS", message: "Invalid arguments", details: nil))
            return
        }
        
        connectToTelnet(host: host, port: port, username: username, password: password, result: result)
    }
    
    private func connectToTelnet(host: String, port: Int, username: String, password: String, result: @escaping FlutterResult) {
        var client: NWConnection?
        
        client = NWConnection(host: NWEndpoint.Host(host), port: NWEndpoint.Port(port: UInt16(port)), using: .tcp)
        client?.start(queue: .main) { [weak self] (error) in
            guard let self = self else { return }
            if let error = error {
                result(.error(withCode: "CONNECTION_ERROR", message: error.localizedDescription, details: nil))
                return
            }
            
            let output = "\(username)\(password)\n" // 简单的登录示例,实际需要根据协议调整
            client?.send(content: Data(output.utf8), completion: { (error) in
                if let error = error {
                    result(.error(withCode: "SEND_ERROR", message: error.localizedDescription, details: nil))
                    return
                }
                
                // 读取响应
                client?.receive(minimumIncompleteLength: 1, maximumLength: 1024, completion: { (data, context, isComplete, error) in
                    if let error = error {
                        result(.error(withCode: "RECEIVE_ERROR", message: error.localizedDescription, details: nil))
                        return
                    }
回到顶部