简体   繁体   English

Flutter:TCP 套接字连接在真实设备上失败

[英]Flutter: TCP socket connection fails on real device

I'm posting this even though I already solved the issue, because I spent more than an hour trying to figure out what was causing it, and might help someone else.即使我已经解决了这个问题,我也会发布这个,因为我花了一个多小时试图找出导致它的原因,并且可能会帮助其他人。


I have a simple application that connects to a server through a TCP socket .我有一个简单的应用程序,它通过TCP 套接字连接到服务器。 It works fine inside the debugger with the device emulator, but when I deploy it on a real device it fails to connect immediately .它在带有设备模拟器的调试器中运行良好,但是当我将它部署在真实设备上时,它无法立即连接
Further investigations led me to finding out that the socket was actually throwing the following exception:进一步的调查使我发现套接字实际上抛出了以下异常:

SocketException: Connection failed (OS Error: Operation not permitted, errno = 1), address = 192.168.1.46, port = 40001

Code sample代码示例

  • Connect button opens a socket on _host:_port连接按钮在_host:_port上打开一个套接字
  • Send Radar Distance button sends a message formatted like {"distance": _distance, "angle": _angle} on the socket.发送雷达距离按钮在套接字上发送格式为{"distance": _distance, "angle": _angle}的消息。
  • Status and Message fields show info about the socket status and eventually useful infos.状态消息字段显示有关套接字状态的信息和最终有用的信息。

在此处输入图像描述

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

import 'views/view_test.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const ViewTest(),
    );
  }
}
views/view_test.dart意见/view_test.dart
import 'dart:io';
import 'dart:typed_data';

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

const List<String> materialTypes = <String>['PLASTIC', 'GLASS'];

class ViewTest extends StatefulWidget {
  const ViewTest({Key? key}) : super(key: key);

  @override
  State<ViewTest> createState() => _ViewTestState();
}

class _ViewTestState extends State<ViewTest> {
  String _distance = "";
  String _angle = "";
  String _status = "";
  String _message = "";

  Socket? _socket;
  final String _host = "192.168.1.46";
  final int _port = 4001;

  Future<void> _sendMessage(String message) async {
    print("Sent message $message");
    _socket!.write(message);
    await Future.delayed(const Duration(seconds: 1));
  }

  void _connect(String ip, int port) async {
    Socket? sock;

    try {
      sock =
          await Socket.connect(ip, port, timeout: const Duration(seconds: 3));

      _socket = sock;
      setState(() {
        _status = "Connected to $ip:$port.";
        _message = "";
      });

      // listen for responses from the server
      _socket!.listen(
        // handle data from the server
        (Uint8List data) {
          final serverResponse = String.fromCharCodes(data);
          setState(() {
            _message = serverResponse;
          });
          print(serverResponse);
        },

        // handle errors
        onError: (error) {
          setState(() {
            _status = "Disconnected.";
            _message = "Error: $error";
          });
          print("Error: $error");
          _socket!.destroy();
          _socket = null;
        },

        // handle server ending connection
        onDone: () {
          setState(() {
            _status = "Disconnected.";
            _message = 'Server left.';
          });
          print('Server left.');
          _socket!.destroy();
          _socket = null;
        },
      );
    } catch (e) {
      setState(() {
        _status = "Connection failed.";
        _message = e.toString();
      });
      print("Error: ${e.toString()}");
    }
  }

  void _disconnect() {
    if (_socket != null) _socket!.destroy();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Center(child: Text("Radar Test")),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 50.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    _status = "";
                    _message = "";
                  });
                  _disconnect();
                  _connect(_host, _port);
                },
                child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Text(
                    _socket == null ? 'Connect' : 'Reconnect',
                    style: const TextStyle(fontSize: 22.0),
                  ),
                ),
              ),
              const SizedBox(height: 50.0),
              TextField(
                onChanged: (text) {
                  _distance = text;
                },
                keyboardType:
                    const TextInputType.numberWithOptions(decimal: false),
                inputFormatters: <TextInputFormatter>[
                  FilteringTextInputFormatter.allow(
                    RegExp(r'[0-9]+'), // this regex allows only decimal numbers
                  )
                ],
                decoration: const InputDecoration(
                  hintText: '100',
                  border: UnderlineInputBorder(),
                  labelText: 'Distance',
                ),
              ),
              TextField(
                onChanged: (text) {
                  _angle = text;
                },
                keyboardType:
                    const TextInputType.numberWithOptions(decimal: false),
                inputFormatters: <TextInputFormatter>[
                  FilteringTextInputFormatter.allow(
                    RegExp(r'[0-9]+'), // this regex allows only decimal numbers
                  )
                ],
                decoration: const InputDecoration(
                  hintText: '90',
                  border: UnderlineInputBorder(),
                  labelText: 'Angle',
                ),
              ),
              const SizedBox(height: 50.0),
              Text("Status: $_status"),
              Text("Message: $_message"),
            ],
          ),
        ),
      ),
      floatingActionButton: ElevatedButton(
        onPressed: _socket == null
            ? null
            : () {
                // test
                _sendMessage(
                    '{"distance": ${_distance.isEmpty ? 100 : int.parse(_distance)}, "angle": ${_angle.isEmpty ? 90 : int.parse(_angle)}}');
              },
        child: const Padding(
          padding: EdgeInsets.all(8.0),
          child: Text(
            'Send Radar Distance',
            style: TextStyle(fontSize: 22.0),
          ),
        ),
      ),
      floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
    );
  }
}

Turns out I had to give the application Internet permissions, as stated in Flutter docs about Networking .事实证明,我必须授予应用程序 Internet 权限,如Flutter docs about Networking中所述。 So I added the following line to AndroidManifest.xml :所以我将以下行添加到AndroidManifest.xml

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

NB: AndroidManifest.xml manifest is located in the following location:注意: AndroidManifest.xml清单位于以下位置:

android > app > src > main > AndroidManifest.xml

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM