Flutter局域网客户端服务器通信插件client_server_lan的使用

Flutter局域网客户端服务器通信插件client_server_lan的使用

插件简介

client_server_lan 是一个基于 Flutter 的局域网通信插件,支持客户端与服务器之间的双向通信。它移除了 Node Commander 中的一些复杂功能(如指挥节点),专注于简单的客户端与服务器通信。


使用方法

1. 配置 Android 文件

android/app/AndroidManifest.xml 文件中添加以下配置:

<application ...
  android:networkSecurityConfig="@xml/network_security_config" >
  
  <meta-data android:name="io.flutter.network-policy"
         android:resource="@xml/network_security_config"/>
</application>

然后在 android/app/src/main/res/xml/ 目录下创建 network_security_config.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <base-config cleartextTrafficPermitted="true">
     <trust-anchors>
        <certificates src="system" />
    </trust-anchors>
  </base-config>
</network-security-config>

2. 启动服务器节点

以下是启动服务器节点的代码示例:

import 'dart:async';
import 'package:client_server_lan/client_server_lan.dart';

ServerNode server;

void startServer() async {
    server = ServerNode(
      name: "server_name", // 任意文本名称
      verbose: true,       // 调试输出
      onDispose: onDispose, // 服务器释放时执行的回调
      clientDispose: clientDispose, // 客户端释放时执行的回调
    );

    await server.init(); // 初始化服务器
    await server.onReady; // 等待服务器准备就绪

    // 更新状态
    setState(() {
      serverStatus = "Server ready on ${server.host}:${server.port}";
    });

    // 监听数据接收
    server.dataResponse.listen((DataPacket data) {
      setState(() {
        String dataReceived = data.payload;
      });
    });
}

3. 启动客户端节点

以下是启动客户端节点的代码示例:

import 'dart:async';
import 'package:client_server_lan/client_server_lan.dart';

ClientNode client;

void startClient() async {
    client = ClientNode(
      name: "client_name", // 任意文本名称
      verbose: true,       // 调试输出
      onDispose: onDispose, // 客户端释放时执行的回调
    );

    await client.init(); // 初始化客户端
    await client.onReady; // 等待客户端准备就绪

    // 更新状态
    setState(() {
      clientStatus = "Client ready on ${client.host}:${client.port}";
    });

    // 监听数据接收
    client.dataResponse.listen((DataPacket data) {
      setState(() {
        String dataReceived = data.payload;
      });
    });
}

4. 扫描客户端节点

服务器可以通过以下方式扫描在线的客户端节点:

void findClients() async {
    server.discoverNodes(); // 开始扫描客户端
    await Future.delayed(const Duration(seconds: 2)); // 等待扫描完成

    // 更新状态并列出所有已连接的客户端
    setState(() {
      clientIPs = "";
    });
    for (final s in server.clientsConnected) {
      setState(() {
        clientIPs += "id=${s.name},IP=${s.address}\n";
      });
    }
}

5. 数据传输

从客户端发送数据到服务器

void clientToServer(String dataToSend) async {
    await client.sendData(dataToSend, "userInfo"); // 发送数据
}

从服务器发送数据到客户端

void serverToClient(String dataToSend, String clientName) async {
    final String client = server.clientUri(clientName); // 获取客户端地址
    await server.sendData(dataToSend, "userInfo", client); // 发送数据
}

示例代码

以下是完整的示例代码,展示了如何使用 client_server_lan 插件实现客户端与服务器通信:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:client_server_lan/client_server_lan.dart';
import 'package:device_info/device_info.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'UDPLANtransfer',
      theme: ThemeData(
        primarySwatch: Colors.green,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  [@override](/user/override)
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool kIsAndroid = false;
  String dropdownValue = 'Server';
  bool isLoading = false;
  String dataReceived = '';
  bool isRunning = false;
  String status = '';

  // Server
  ServerNode server;
  List<ConnectedClientNode> connectedClientNodes = [];

  // Client
  ClientNode client;

  [@override](/user/override)
  Widget build(BuildContext context) {
    kIsAndroid = !kIsWeb && Theme.of(context).platform == TargetPlatform.android;
    return Scaffold(
      appBar: AppBar(
        title: Text('UDPLANtransfer'),
      ),
      body: Container(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            _buildDropdown(),
            Expanded(
              child: dropdownValue == 'Server'
                  ? ServerPage(
                      onStartPressed: startServer,
                      onDisposePressed: disposeServer,
                      connectedClientNodes: connectedClientNodes,
                      onFindClientsPressed: findClients,
                      onSendToClient: serverToClient,
                      dataReceived: dataReceived,
                      isLoading: isLoading,
                      isRunning: isRunning,
                      status: status,
                    )
                  : ClientPage(
                      onStartPressed: startClient,
                      onDisposePressed: disposeClient,
                      onSendToServer: clientToServer,
                      dataReceived: dataReceived,
                      onCheckServerPressed: checkServerExistance,
                      isLoading: isLoading,
                      isRunning: isRunning,
                      status: status,
                    ),
            ),
          ],
        ),
      ),
    );
  }

  DropdownButton<String> _buildDropdown() {
    return DropdownButton<String>(
      value: dropdownValue,
      disabledHint: Text(dropdownValue),
      onChanged: !isRunning
          ? (String newValue) {
              setState(() {
                dropdownValue = newValue;
              });
            }
          : null,
      items: ['Server', 'Client']
          .map<DropdownMenuItem<String>>((String value) {
        return DropdownMenuItem<String>(
          value: value,
          child: Text(value),
        );
      }).toList(),
    );
  }

  void startServer() async {
    var name = 'Server';
    if (kIsAndroid) {
      var deviceInfo = await DeviceInfoPlugin().androidInfo;
      name = 'Server-${deviceInfo.brand}-${deviceInfo.model}';
    }
    setState(() {
      isLoading = true;
      server = ServerNode(
        name: name,
        verbose: true,
        onDispose: onDisposeServer,
        clientDispose: clientDispose,
        onError: onError,
      );
    });

    await server.init();
    await server.onReady;

    setState(() {
      status = 'Server ready on ${server.host}:${server.port} (${server.name})';
      isRunning = true;
      isLoading = false;
    });
    server.dataResponse.listen((DataPacket data) {
      setState(() {
        dataReceived = data.payload.toString();
      });
    });
  }

  void disposeServer() {
    setState(() {
      isLoading = true;
    });
    server.dispose();
  }

  void onDisposeServer() {
    setState(() {
      isRunning = false;
      status = 'Server is not running';
      isLoading = false;
      connectedClientNodes = [];
    });
  }

  void clientDispose(ConnectedClientNode c) async {
    setState(() {
      connectedClientNodes = [];
    });
    for (final s in server.clientsConnected) {
      setState(() {
        connectedClientNodes.add(s);
      });
    }
  }

  void findClients() async {
    await server.discoverNodes();
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      connectedClientNodes = [];
    });
    for (final s in server.clientsConnected) {
      setState(() {
        connectedClientNodes.add(s);
      });
    }
  }

  void serverToClient(String clientName, dynamic message) async {
    final client = server.clientUri(clientName);
    await server.sendData(message, 'userInfo', client);
  }

  void startClient() async {
    var name = 'Client';
    if (kIsAndroid) {
      var deviceInfo = await DeviceInfoPlugin().androidInfo;
      name = 'Client-${deviceInfo.brand}-${deviceInfo.model}';
    }
    setState(() {
      isLoading = true;
      client = ClientNode(
        name: name,
        verbose: true,
        onDispose: onDisposeClient,
        onServerAlreadyExist: onServerAlreadyExist,
        onError: onError,
      );
    });

    await client.init();
    await client.onReady;

    setState(() {
      status = 'Client ready on ${client.host}:${client.port} (${client.name})';
      isRunning = true;
      isLoading = false;
    });

    client.dataResponse.listen((DataPacket data) {
      setState(() {
        if (data.payload.runtimeType == String) {
          dataReceived = data.payload;
        } else {
          dataReceived = City.fromMap(data.payload).toString();
        }
      });
    });
  }

  void disposeClient() {
    client.dispose();
  }

  void onDisposeClient() {
    setState(() {
      isRunning = false;
      status = 'Client is not running';
      isLoading = false;
    });
  }

  Future<void> onServerAlreadyExist(DataPacket dataPacket) async {
    print('Server already exist on ${dataPacket.host} (${dataPacket.name})');
    await showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text('Server Already Exist'),
          content:
              Text('Server ready on ${dataPacket.host} (${dataPacket.name})'),
          actions: [
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: Text('CLOSE'),
            ),
          ],
        );
      },
    );
  }

  Future<void> checkServerExistance() async {
    await client.discoverServerNode();
  }

  void clientToServer(dynamic message) async {
    await client.sendData(message, 'userInfo');
  }

  Future<void> onError(String error) async {
    print('ERROR $error');
    await showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text('Error'),
          content: Text(error),
          actions: [
            ElevatedButton(
              onPressed: () {
                Navigator.pop(context);
              },
              child: Text('CLOSE'),
            ),
          ],
        );
      },
    );
  }
}

更多关于Flutter局域网客户端服务器通信插件client_server_lan的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter局域网客户端服务器通信插件client_server_lan的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


client_server_lan 是一个用于在局域网内进行客户端和服务器通信的 Flutter 插件。它允许你在同一局域网内的设备之间进行数据传输,适用于需要设备间通信的应用场景,如文件共享、聊天应用、多人游戏等。

安装插件

首先,你需要在 pubspec.yaml 文件中添加 client_server_lan 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  client_server_lan: ^0.0.1  # 请检查最新版本

然后运行 flutter pub get 来安装插件。

使用插件

1. 创建服务器

在服务器端,你可以使用 Server 类来创建一个服务器并监听客户端的连接请求。

import 'package:client_server_lan/client_server_lan.dart';

void startServer() async {
  Server server = Server();
  server.onClientConnected.listen((client) {
    print('Client connected: ${client.address}');

    client.onData.listen((data) {
      print('Received from client: $data');
      client.send('Hello from server!');
    });

    client.onDisconnected.listen((_) {
      print('Client disconnected: ${client.address}');
    });
  });

  await server.start(port: 8080);
  print('Server started on port 8080');
}

2. 创建客户端

在客户端,你可以使用 Client 类来连接到服务器并发送数据。

import 'package:client_server_lan/client_server_lan.dart';

void connectToServer() async {
  Client client = Client();
  await client.connect('192.168.1.100', 8080); // 替换为服务器的IP地址

  client.onData.listen((data) {
    print('Received from server: $data');
  });

  client.onDisconnected.listen((_) {
    print('Disconnected from server');
  });

  client.send('Hello from client!');
}

3. 在 Flutter 应用中使用

你可以在 Flutter 应用中使用上述代码来启动服务器或连接到服务器。例如,你可以在一个按钮的 onPressed 回调中调用 startServerconnectToServer 函数。

import 'package:flutter/material.dart';
import 'package:client_server_lan/client_server_lan.dart';

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Client Server LAN Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: startServer,
                child: Text('Start Server'),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: connectToServer,
                child: Text('Connect to Server'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
回到顶部