Home > other >  Flutter: TCP socket connection fails on real device
Flutter: TCP socket connection fails on real device

Time:10-02

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. 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
  • Send Radar Distance button sends a message formatted like {"distance": _distance, "angle": _angle} on the socket.
  • Status and Message fields show info about the socket status and eventually useful infos.

enter image description here

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
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
        one rror: (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,
    );
  }
}

CodePudding user response:

Turns out I had to give the application Internet permissions, as stated in Flutter docs about Networking. So I added the following line to AndroidManifest.xml:

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

NB: AndroidManifest.xml manifest is located in the following location:

android > app > src > main > AndroidManifest.xml
  • Related