从任何网络Flutter访问HTTP服务器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从任何网络Flutter访问HTTP服务器相关的知识,希望对你有一定的参考价值。

我正在尝试创建一个可远程控制某些灯光的应用。就目前而言,一切正常,我可以通过按我在应用程序上设计的按钮来控制控制灯的继电器的状态,而与安装该应用程序的设备所连接的网络无关。

我想要的是:多个设备将安装此应用程序并将其连接到同一位置(我的意思是将控制同一灯)。因此,当一个灯光状态更改时,应使用新的灯光状态更新该应用程序的所有其他实例(进行更改的一个实例)。

我曾想通过在应用程序内部放置一台服务器来实现此目的,以便在发生更改时,托管在控制一堆中继的Raspberry上的真实服务器可以向所有应用程序发送消息并进行更新。每次打开应用程序或更改IP时,应用程序都会发送其IP和存储在服务器上的ID。

我面临的问题是:在本地工作时,一切都很好,但是当安装了该应用程序的设备连接到另一个网络时,它将不再收到更新。当然,我知道这是由于以下事实:由于我使用的Flutter插件,我只能获取设备的本地IP,因此无法通过Internet到达它。我可以同时检索设备的公共IP地址和私有IP地址,但是然后我不知道如何向“拥有”公共IP的路由器“询问”,以便我可以使用私有IP来访问设备而无需进行端口转发(无法进行端口转发,因为我正在使用移动设备)。

我的问题:有没有办法实现我想要的?还是有发生变化时动态更新应用程序的其他方法?还是同时获得“外部”和本地IP,然后使用该IP调用应用程序的方法?预先感谢。

pubspec.yaml依赖项:

dependencies:
  flutter:
    sdk: flutter
  get_ip: ^0.3.0
  shared_preferences: ^0.4.3

androidManifest.xml权限:

<uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Flutter服务器和IP发送方法:

void listenForUpdates()
  {
    HttpServer.bind(InternetAddress.anyIPv4, int.parse(port)).then((server)
    {
      server.listen((HttpRequest request)
      {
        MapEntry<String, String> parameter = request.uri.queryParameters.entries.single;
        print(parameter);
        setState(() {
          lightsOn[int.parse(parameter.key) - 1] = parameter.value.contains('true');
          noResponse = false;
        });
        request.response.close();
      });
    });
  }

void initPlatformState(bool resync) async
  {
    String ipAddress;
    ipAddress = await GetIp.ipAddress;
    HttpClient client = new HttpClient();
    SharedPreferences prefs = await SharedPreferences.getInstance();
    setState((){ deviceId = resync ? "new" : (prefs.getString('deviceId') ?? "new"); });

    client.postUrl(Uri.parse('http://NameOfTheServer:Port/?q=device&ip=' + ipAddress + ':' + port + '&id=' + deviceId))
    .catchError((onError)
    {
      setState((){ noResponse = true; });
    })
    .then((HttpClientRequest request)
    {
      request.headers.set(HttpHeaders.userAgentHeader, 'LumenApp - Dart:Flutter');
      return request.close();
    })
    .then((HttpClientResponse response)
    {
      response.transform(utf8.decoder).listen((contents) async
      {
        if(response.statusCode == 200)
        {
          setState((){
            deviceId = contents.split('|')[1];
            noResponse = false;
          });
          await prefs.setString('deviceId', deviceId);

        }
        else
          setState((){ noResponse = true; });
      });
    });
}

处理来自应用程序(Raspberry,Java)的请求的类的方法:

@Override
    public void run()
    {
        System.out.println("Successfully started thread with ThreadId " + this.threadId + ".");

        if(http.res.getCurrentResponse().size() > 0)
        {
            http.res.send();
        }
        else
        {
            http.res.setHeaders(http.req.getHttpVersion(), 200);

            try
            {
                String qParam = http.req.getParameterValueByName("q");
                String deviceId = http.req.getParameterValueByName("id");
                Boolean devPerm = AppUpdater.getDevicesId().contains(deviceId);
                if(!devPerm && !deviceId.equalsIgnoreCase("new"))
                {
                    http.res.setBody("ID EXPIRED");
                }
                else
                {
                    if(qParam.equalsIgnoreCase("device"))
                    {
                        String deviceIp = http.req.getParameterValueByName("ip");
                        String toDelete = http.req.getParameterValueByName("delete");
                        if(toDelete == null)
                        {
                            toDelete = "false";
                        }
                        Integer id = AppUpdater.updateDevicesIp(deviceId, deviceIp, Boolean.parseBoolean(toDelete));
                        http.res.setBody("DeviceId|" + id);
                    }
                    else if(Integer.parseInt(qParam) >= 0 && devPerm)
                    {
                        Integer lParam = Integer.parseInt(http.req.getParameterValueByName("l"));
                        gpioHandler.changeGpiosState(qParam, lParam);
                    }
                    else if(qParam.equals("-1") && devPerm)
                    {
                        HashMap<Integer, Boolean> gpiosState = gpioHandler.getGpiosState();
                        http.res.setBody(qParam);
                        for(HashMap.Entry<Integer, Boolean> gpio : gpiosState.entrySet())
                        {
                            http.res.addBody("|" + gpio.getKey().toString() + "-" + gpio.getValue().toString());
                        }
                    }
                }
            }
            catch(RuntimeException e)
            {
                e.printStackTrace();
                System.err.println("One or more required parameters were missing.");
                http.res.setHeaders(http.req.getHttpVersion(), 400);
            }
            finally
            {
                http.res.send();
            }
        }
public class AppUpdater
{
    static final GpioHandler gpioHandler = new GpioHandler();

    static HashMap<String, String> devices = new HashMap<String, String>();

    public static Integer updateDevicesIp(String deviceId, String deviceIp, boolean toDelete)
    {
        Integer id = 0;
        if(toDelete)
        {
            devices.remove(deviceIp);
        }
        else if(deviceId.equalsIgnoreCase("new"))
        {
            id = devices.size() + 1;
            devices.put(id.toString(), deviceIp);
        }
        else if(devices.containsKey(deviceId))
        {
            devices.replace(deviceId, deviceIp);
            id = Integer.parseInt(deviceId);
        }
        return id;
    }

    public static void notifyApp(String lightIndex, String lightStatus) throws IOException
    {
        for(HashMap.Entry<String, String> device : devices.entrySet())
        {
            String urlParameters = "?" + lightIndex + "=" + lightStatus;
            URL url = new URL("http://" + device.getValue() + urlParameters);
            System.out.println(url);
            url.openStream();
        }
    }

    public static ArrayList<String> getDevicesId()
    {
        ArrayList<String> devicesId = new ArrayList<String>();
        for(HashMap.Entry<String, String> device : devices.entrySet())
        {
            devicesId.add(device.getKey());
        }
        return devicesId;
    }

    public static HashMap<String, String> getDevices()
    {
        return devices;
    }
}
public void gpioListener()
    {
        for(GpioPinDigitalOutput pin : gpios)
        {
            pin.addListener(new GpioPinListenerDigital() {
                @Override
                public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event)
                {
                    String gpioName = event.getPin().getName();
                    System.out.println(" --> GPIO PIN STATE CHANGE: " + gpioName + " = " + event.getState());
                    try
                    {
                        String lightStatus = event.getState().toString().equals("HIGH") ? "true" : "false";
                        String pinIndex = pinIndexes.get(gpioName.substring(gpioName.length() - 1));
                        AppUpdater.notifyApp(pinIndex, lightStatus);
                    }
                    catch(IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
答案

欢迎使用stackoverflow。

以上是关于从任何网络Flutter访问HTTP服务器的主要内容,如果未能解决你的问题,请参考以下文章

无法使用带有工作 api 的 http.get flutter 获取数据

从意图活动访问片段方法

无法使用 Flutter Web 访问 Cloud Firestore

使用Android导航组件时如何从后台获取片段?

实现一个可以从每个片段和活动中访问的按钮

Flutter学习-网络请求