Flutter局域网广播(UDP通信)与TCP通信

news2024/9/21 11:27:40

前言

现在有一个需求,手机和ESP32通过WIFI进行通信。流程如下:

  • 手机创建TCP服务器
  • 手机向192.168.0.255的1002端口广播自己的ip地址以及TCP服务器的端口号
  • ESP32监听到1002的广播内容后,连接手机的TCP服务器。
  • 最后就是ESP32硬件和TCP服务器进行数据收发

因此我们要了解Flutter如何使用UDP进行广播、如何创建TCP服务器以及通过TCP进行通信

验证工具

假如我们代码写好了,和硬件直接调试发现出现了错误,我们就无法定位是Flutter的代码问题还是硬件的问题。因此需要一个验证工具来验证是哪方面出现了问题

这里我们使用验证工具是“山外多功能调试助手”

模拟TCP服务器

  • 打开山外多功能调试助手
  • 点击网络调试助手,再点击TCP服务器

  • 输入端口号点击监听,硬件根据ip地址和端口号进行连接,在消息区我们可以看到是否有设备连接

  • ESP32能够连接这个软件说明错误问题在手机程序中

模拟TCP客户端

  • 前端创建好了TCP服务端后,可以通过这个软件进行连接测试
  • 打开“山外多功能调试助手”,点击网络调试助手,点击TCP客户端
  • 输入服务器IP地址以及端口号,点击连接后查看消息是否连接成功

如果能够连接成功则说明我们的TCP服务器创建成功

模拟UDP广播

  • 点击网络调试助手——>点击UDP,输入广播的端口号后点击连接
  • 手机程序发送一个广播,在消息区可以看到广播的内容

创建TCP服务器

要在Flutter中创建TCP服务器,您可以使用dart:io库中的ServerSocket类。

下面代码使用ServerSocket.bind()方法绑定服务器的地址和端口。然后,我们使用server.listen()来监听来自客户端的连接。当客户端连接到服务器时,我们打印连接信息并向客户端发送欢迎消息。

接下来,我们通过socket.listen()来监听客户端发送的数据。当接收到数据时,我们打印收到的消息,并向客户端发送回复消息。如果在接收数据时出现错误或客户端断开连接,我们关闭与客户端的连接。

startServer() async {
  try {
    _serverSocket?.close();
    _serverSocket = await ServerSocket.bind(InternetAddress.anyIPv4, 5556);
    print( '服务器已启动,地址:${_serverSocket?.address.address}:${_serverSocket?.port}');
    _serverSocket?.listen(serverOnReceive);
  } catch (e, stackTrace) {
    print('e---------:$e');
  }
}
serverOnReceive(Socket socket) {
  _socket = socket;
  print('客户端已连接: ${socket.remoteAddress}:${socket.remotePort}');
  socket.writeln('欢迎连接到服务器'); // 向客户端发送欢迎消息
 
  _socket?.listen(
    (data) {
      print('接收到客户端消息: $data');
      socket.writeln('服务器收到消息: $data'); // 回复客户端收到的消息
    },
    onError: (error) {
      print('接收数据时出现错误: $error');
      socket.close(); // 关闭与客户端的连接
    },
    onDone: () {
      print('客户端已断开连接');
      socket.close(); // 关闭与客户端的连接
    },
  );
}

局域网广播

权限设置

android

在Flutter中发送广播消息所涉及的权限取决于您的目标平台和网络环境。以下是一些常见的权限,您可能需要在Flutter应用程序中配置以发送广播消息:

对于Android平台:

  • android.permission.INTERNET:用于访问互联网。
  • android.permission.ACCESS_NETWORK_STATE:用于访问网络状态信息。
  • android.permission.ACCESS_WIFI_STATE:用于访问Wi-Fi状态信息。
  • android.permission.CHANGE_WIFI_MULTICAST_STATE:用于更改Wi-Fi多播状态。
对于ios平台
  • NSLocalNetworkUsageDescription:描述应用程序使用本地网络的目的,添加该键值对到应用程序的Info.plist文件中。

示例Info.plist文件中的键值对配置部分:

<key>NSLocalNetworkUsageDescription</key>

<string>需要使用本地网络以发送广播消息。</string>
具体代码

在Flutter中,可以使用dart:io库中的RawDatagramSocket类来发送广播消息。以下是一个简单的示例代码,演示如何在Flutter中发送广播消息:

void sendBroadcastMessage() {
  String message = 'BB192.168.0.174:5556';
  print(utf8.encode(message));
  RawDatagramSocket.bind(InternetAddress.anyIPv4, 0)
      .then((RawDatagramSocket socket) {
    rawDatagramSocket = socket;
    var broadcastAddr = InternetAddress('255.255.255.255');
    var port = 1002; // 广播目标端口
 
    socket.broadcastEnabled = true;
 
    socket.send(utf8.encode(message), broadcastAddr, port);
 
    socket.close();
  }).catchError((error) {
    print('发送广播消息时出现错误: $error');
  });
}

全部代码

import 'dart:convert';
import 'dart:io';
 
import 'package:flutter/material.dart';
 
ServerSocket? _serverSocket;
Socket? _socket;
RawDatagramSocket? rawDatagramSocket;
 
class TCPServe extends StatefulWidget {
  const TCPServe({super.key});
 
  @override
  State<TCPServe> createState() => _TCPServeState();
}
 
class _TCPServeState extends State<TCPServe> {
  startServer() async {
    print("InternetAddress.anyIPv4:${InternetAddress.loopbackIPv4}");
    try {
      _serverSocket?.close();
      // _serverSocket = await ServerSocket.bind("localhost", 1234);
      _serverSocket = await ServerSocket.bind(InternetAddress.anyIPv4, 5556);
 
      print(
          '服务器已启动,地址: ${_serverSocket?.address.address}:${_serverSocket?.port}');
      _serverSocket?.listen(serverOnReceive);
    } catch (e, stackTrace) {
      print('e---------:$e');
    }
  }
 
  serverOnReceive(Socket socket) {
    _socket = socket;
    print('客户端已连接: ${socket.remoteAddress}:${socket.remotePort}');
    socket.writeln('欢迎连接到服务器'); // 向客户端发送欢迎消息
 
    _socket?.listen(
      (data) {
        print('接收到客户端消息: $data');
        socket.writeln('服务器收到消息: $data'); // 回复客户端收到的消息
      },
      onError: (error) {
        print('接收数据时出现错误: $error');
        socket.close(); // 关闭与客户端的连接
      },
      onDone: () {
        print('客户端已断开连接');
        socket.close(); // 关闭与客户端的连接
      },
    );
  }
 
  clean() async {
    await _socket?.close();
    print("_socket关闭");
    await _serverSocket?.close();
    print("_serverSocket关闭");
  }
 
  void sendBroadcastMessage() {
    String message = 'BB192.168.0.174:5556';
    print(utf8.encode(message));
    RawDatagramSocket.bind(InternetAddress.anyIPv4, 0)
        .then((RawDatagramSocket socket) {
      rawDatagramSocket = socket;
      var broadcastAddr = InternetAddress('255.255.255.255');
      var port = 1002; // 广播目标端口
 
      socket.broadcastEnabled = true;
 
      socket.send(utf8.encode(message), broadcastAddr, port);
 
      socket.close();
    }).catchError((error) {
      print('发送广播消息时出现错误: $error');
    });
  }
 
  cleanRawDatagramSocket() {
    rawDatagramSocket?.close();
  }
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("TCP服务端测试")),
      body: Center(
        child: Column(
          children: [
            TextButton(
              onPressed: startServer,
              child: const Text("创建服务"),
            ),
            TextButton(
              onPressed: clean,
              child: const Text("关闭服务"),
            ),
            TextButton(
              onPressed: () {
                _socket?.writeln('REVERSE_LED');
              },
              child: const Text("发送REVERSE_LED"),
            ),
            TextButton(
              onPressed: sendBroadcastMessage,
              child: const Text("开始广播"),
            ),
            TextButton(
              onPressed: cleanRawDatagramSocket,
              child: const Text("关闭广播"),
            ),
          ],
        ),
      ),
    );
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2146881.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++速通LeetCode中等第2题-最长连续序列

方法一&#xff0c;排序后遍历&#xff0c;后减前1&#xff0c;计数&#xff0c; 相等跳过&#xff0c;后减前&#xff01;1就保存。 class Solution { public:int longestConsecutive(vector<int>& nums) {vector<int> ans;int count 1;sort(nums.begin(),n…

GPT代码记录

#include <iostream>// 基类模板 template<typename T> class Base { public:void func() {std::cout << "Base function" << std::endl;} };// 特化的子类 template<typename T> class Derived : public Base<T> { public:void…

收集松果-第15届蓝桥省赛Scratch中级组真题第4题

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第189讲。 如果想持续关注Scratch蓝桥真题解读&#xff0c;可以点击《Scratch蓝桥杯历年真题》并订阅合集&#xff0c;…

我的AI工具箱Tauri版-FunAsr音频转文本

本教程基于自研的AI工具箱Tauri版进行FunAsr音频转文本服务。 FunAsr音频转文本服务 是自研AI工具箱Tauri版中的一个高效模块&#xff0c;专为将音频或视频中的语音内容自动转化为文本或字幕而设计。用户只需简单配置输入、输出路径&#xff0c;即可通过FunAsr工具快速批量处理…

教你把PDF电子画册加背景音乐

​如何让您的PDF电子画册更具吸引力&#xff0c;让人一眼就能爱上它呢&#xff1f;答案就是为画册添加背景音乐&#xff01;添加背景音乐的PDF电子画册相较于普通画册&#xff0c;更能吸引读者的注意力&#xff0c;提升阅读体验。那么&#xff0c;如何为PDF电子画册添加背景音乐…

杭电OJ1009——猫鼠交换问题

杭电的OJ居然通不过<bits/stdc.h>这个头文件&#xff0c;需要小心 提供一个翻译版本 这里给两个版本 简单贪心拿下 //#include<bits/stdc.h> #include<iostream> #include<algorithm> using namespace std; struct Node{double j,f,p;//p是单价 }a[…

29. 查看threejs自带几何体顶点

查看three.js自带几何体顶点结构&#xff0c;基类(父类)BufferGeometry three.js提供的矩形平面PlaneGeometry、长方体BoxGeometry、球体SphereGeometry等各种形状的几何体&#xff0c;他们都有一个共同的父类BufferGeometry。这意味着这些几何体有哪些属性或方法&#xff0c;…

Java进阶之集合框架(Set)

【基本内容】 二、Set接口(接上一章) Set是Java集合框架中不允许有重复元素的无序集合&#xff0c;其典型的实现类是HashSet&#xff0c;它完全是遵循Set接口特性规范实现的&#xff0c;无序且不允许元素重复&#xff1b;而Set接口下的实现类还有LinkedHashSet和TreeSort&#…

Submariner 部署全过程

Submariner 部署全过程 部署集群配置 broker 集群&#xff1a; pod-cidr&#xff1a;11.244.0.0/16 service-cidr 11.96.0.0/12 broker 172.100.0.109 node 172.100.0.108 集群 1&#xff08; pve3 &#xff09;&#xff1a; pod-cidr&#xff1a;10.244.0.0/16 service-…

nodejs 011: nodejs事件驱动编程 EventEmitter 与 IPC

在 Node.js 和许多 JavaScript 环境中&#xff0c;EventEmitter 是一个非常重要的类&#xff0c;用于处理事件驱动编程。EventEmitter 是一个能够发射&#xff08;emit&#xff09;和监听&#xff08;on&#xff09;事件的对象。它常用于创建和处理事件机制&#xff0c;使得程序…

Dubbo与SpringCloud的区别和优缺点

经常会有同学问我&#xff0c;Dubbo和SpringCloud的选择。甚至也经常会有面试官就这个问题刨根问底。 说实话&#xff0c;其实我不太喜欢回答这个问题&#xff0c;本质上来讲&#xff0c;Dubbo的SpringCloud可以算是完全不同赛道的两种东西&#xff0c;就好像问大家西瓜和土豆我…

【Java】多线程前置知识 初识Thread

多线程前置知识 & 初识Thread 冯诺依曼体系结构初步认识存储设备CPU指令 操作系统初识操作系统内核态和用户态 进程/任务进程是什么进程的管理进程的调度虚拟内存地址进程间的通信 线程线程的出现线程是什么线程可能出现的问题线程与进程的联系和区别 协程初识Thread类Thre…

VSCode引用Eigen库无法识别问题解决

VSCode引用Eigen库无法识别问题解决 问题解决 问题 在Ubuntu下使用vscode开发C/C项目时引用了Eigen库&#xff0c;出现Eigen::Vector3d无法识别的问题&#xff0c;提示"no definition found for Vector3d"。但是程序可以正常编译通过。 解决 将 intelli Sense Engi…

【学习资料】袋中共36个球,红白黑格12个,问能一次抽到3个红4个白5个黑的概率是多少?

1、公式计算 1.1 题目1 袋中共 36 36 36个球&#xff0c; 红 \fcolorbox{red}{#FADADE}{\color{red}{红}} 红​ 白 \fcolorbox{white}{#808080}{\color{white}{白}} 白​ 黑 \fcolorbox{#808080}{#0D0D0D}{\color{#808080}{黑}} 黑​各 12 12 12个&#xff0c;问能一次抽到 3…

事件循环异步代码输出顺序题目【基础考核】

简单的事件循环&#xff0c;一道异步代码执行输出顺序问题? 第一题 setTimeout(() > {console.log("A")Promise.resolve().then(() > { console.log("B"); });}, 1000);Promise.resolve().then(() > { console.log("c"); });new Prom…

JSON.parseArray 内存溢出

实际上我的JSON如下&#xff1a; 如果用以下代码&#xff1a;JVM的内存直接飙到内存溢出&#xff0c;报错OutOfMemoryError: Java heap space Object oo JSON.parseArray(json, TestVo.class) 如果我换成了这样&#xff0c;就没事&#xff1a; Object oo JSON.parseObject(…

1.2 测试基础

欢迎大家订阅【软件测试】 专栏&#xff0c;开启你的软件测试学习之旅&#xff01; 文章目录 前言1 测试分类1.1 按生产阶段划分1.2 按代码可见度划分1.3 其他测试 2 质量模型 前言 在软件开发过程中&#xff0c;测试是确保产品质量的重要环节。本文详细讲解了软件测试分类以及…

Python Email库:发送与接收邮件完整指南!

Python Email库如何集成&#xff1f;怎么优化Python Email库性能&#xff1f; Python作为一种强大的编程语言&#xff0c;提供了丰富的库来处理电子邮件&#xff0c;其中最著名的就是Python Email库。AokSend将深入探讨如何使用Python Email库来发送和接收邮件&#xff0c;帮助…

SpringCloud config native 配置

SpringCloud config native 配置 1.概述 最近项目使用springCloud 框架&#xff0c;使用config搭建git作为配置中心。 在私有化部署中&#xff0c;出现很多比较麻烦的和鸡肋的设计。 每次部署都需要安装gitlab 有些环境安装完gitlab&#xff0c;外面不能访问&#xff0c;不给开…