Socket/Http работает в Java Android, но не в Dart/Flutter.

Я использую клиент Ntrip, написанный на Java, который выполняет запросы, используяSocket.

Это отлично работает с этим кодом:

      public void run() {
        Log.d(TAG, "Run network client with server " + nServer + " and port " + nPort + " and mount point : " + nMountpoint);
        Log.d(TAG, "Running with username : " + nUsername + " password : " + nPassword);
        try {
            //Log.i(NTAG, "Creating socket");
            SocketAddress sockaddr = new InetSocketAddress(nServer, nPort);
            nsocket = new Socket();
            nsocket.connect(sockaddr, 10000); // 10 second connection timeout
            if (nsocket.isConnected()) {
                nsocket.setSoTimeout(20000); // 20 second timeout once data is flowing
                nis = nsocket.getInputStream();
                nos = nsocket.getOutputStream();
                Log.i(TAG, "Socket created, streams assigned");
                handler.sendMessage(handler.obtainMessage(MSG_NETWORK_CONNECTED, "Connected"));
                if (nProtocol.equals("ntripv1")) {
                    // Build request message
                    Log.i(TAG, "This is a NTRIP connection");
                    String requestmsg = "GET /" + nMountpoint + " HTTP/1.0\r\n";
                    requestmsg += "User-Agent: NTRIP LefebureAndroidNTRIPClient/20120614\r\n";
                    requestmsg += "Accept: */*\r\n";
                    requestmsg += "Connection: close\r\n";
                    if (nUsername.length() > 0) {
                        requestmsg += "Authorization: Basic " + ToBase64(nUsername + ":" + nPassword);
                    }
                    requestmsg += "\r\n";
                    nos.write(requestmsg.getBytes());
                    //Log.i("Request", requestmsg);
                    Log.v(TAG, requestmsg);
                } else {
                    Log.i(TAG, "This is a raw TCP/IP connection");
                }

                //Log.i(NTAG, "Waiting for inital data...");
                Log.v(TAG, "Waiting for inital data...");
                byte[] buffer = new byte[4096];
                for(int read = nis.read(buffer, 0, 4096); read != -1; read = nis.read(buffer, 0, 4096)) {
                    byte[] tempdata = new byte[read];
                    System.arraycopy(buffer, 0, tempdata, 0, read);
                    Log.v("NTripService", "Got data: " + new String(tempdata));
                    Log.v("NTripService", "Got data: " + Arrays.toString(tempdata));
                    handler.sendMessage(handler.obtainMessage(101, tempdata));
                }
            }
        } catch (SocketTimeoutException ex) {
            Log.d(TAG, "Time out : " + ex.getMessage());
            handler.sendMessage(handler.obtainMessage(MSG_NETWORK_TIMEOUT));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                nis.close();
                nos.close();
                nsocket.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            Log.i(TAG, "Finished");
            handler.sendMessage(handler.obtainMessage(MSG_NETWORK_FINISHED));
        }
    }

Я хотел бы сделать эквивалент во Flutter без собственного кода, поэтому я попробовал:

      Future<void> connectSocket() async {
    try {
      _socket = await Socket.connect("caster.centipede.fr", 2101, timeout: const Duration(seconds: 3));
      //_socket!.setOption(SocketOption.tcpNoDelay, true);
      _subscription = _socket!.listen(dataHandler,
          onError: errorHandler, onDone: doneHandler, cancelOnError: false);
      print("Socket connected : ${_socket!.address}");
      String requestmsg = "GET /PRSRTCM3_G4 HTTP/1.1\r\n";
      requestmsg += 'Ntrip-Version: Ntrip/2.0\r\n';
      requestmsg += "Accept: */*\r\n";
      requestmsg += 'Connection: close\r\n';
    if (_username.isNotEmpty) {
      String encoded = base64.encode(utf8.encode("$_username:$_password"));
      requestmsg += "Authorization: Basic $encoded";
      }
      requestmsg += "\r\n";
      print("Request msg : $requestmsg");
      _socket!.write(requestmsg);
      print("Socket wrote ");
    } catch (error) {
      print("Could not connect : $error");
    }
  }

  void dataHandler(data) {
    print("data : ${String.fromCharCode(data)}");
  }

  void errorHandler(error, StackTrace trace) {
    print("error : $error");
  }

  void doneHandler() {
    print("Destroying socket");
    _socket?.destroy();
    _subscription?.cancel();
  }

Но это не работает, сокет закрывается через очень короткое время (например, 2 секунды) после подключения и это не имеет никакого отношения к написанному сообщению (даже без него оно закрывается).

Я также пробовал использоватьhttp:

       String encoded = base64.encode(utf8.encode("$_username:$_password"));
          http.Response response =
              await http.get(Uri.parse("http://caster.centipede.fr:2101/PRSRTCM3_G4"), headers: {
            HttpHeaders.authorizationHeader: "Basic $encoded",
          });

          print(
              "Response is : ${response.statusCode} - ${response.body} - ${response.reasonPhrase}");
        } catch (error) {
          print("Error : $error");
        }

И я получаю403 - - Forbiddenсообщение в журналах.

Примечание: если я используюhttpsу меня ошибка рукопожатия, и я также попробовал:

      class DevHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return super.createHttpClient(context)
      ..badCertificateCallback = (X509Certificate cert, String host, int port) {
        print("Bad certificate for host : $host and port : $port");
        return true;
      };
  }
}

Но это тоже не помогает.

Тот же запрос отлично работает на Java. Можете ли вы сказать мне, что я делаю не так, пожалуйста?

Я хотел бы избежать использования нативного кода в своем проекте и особенно понять, что здесь не так с Flutter/dart?

Заранее большое спасибо

РЕДАКТИРОВАТЬ :

Вот журнал, который я получаю во Flutter с сокетом:

      I/flutter (14751): Socket connected : InternetAddress('147.100.179.214', IPv4)
I/flutter (14751): Request msg : GET /PRSRTCM3_G4 HTTP/1.1
I/flutter (14751): Ntrip-Version: Ntrip/2.0
I/flutter (14751): Accept: */*
I/flutter (14751): Connection: close
I/flutter (14751): Authorization: Basic Y2VudGlwZWRlOmNlbnRpcGVkZQ==
I/flutter (14751): Socket wrote 
I/flutter (14751): Destroying socket

Вот заклинатель, к которому я пытаюсь обратиться, но у него нет общедоступного API.

2 ответа

Как ни странно, я тоже не могу заставить работать Postman. Но эта модифицированная версия вашего кода Dart делает это. (Изменений 3 или 4 — пользовательский агент, перенос строки после заголовка аутентификации, кодирование и декодирование байтов.)

      import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

Future<void> main() async {
  final _username = 'centipede';
  final _password = 'centipede';

  final _socket = await Socket.connect(
    'caster.centipede.fr',
    2101,
    timeout: const Duration(seconds: 3),
  );
  final _subscription = _socket.listen(
    dataHandler,
    onError: errorHandler,
    onDone: doneHandler,
    cancelOnError: false,
  );
  print('Socket connected : ${_socket.address}');
  var requestmsg = 'GET /PRSRTCM3_G4 HTTP/1.1\r\n';
  requestmsg += 'User-Agent: NTRIP LefebureAndroidNTRIPClient/20120614';
  requestmsg += 'Accept: */*\r\n';
  requestmsg += 'Connection: close\r\n';
  if (_username.isNotEmpty) {
    final encoded = base64.encode(utf8.encode('$_username:$_password'));
    requestmsg += 'Authorization: Basic $encoded\r\n';
  }
  requestmsg += '\r\n';
  print('Request msg : $requestmsg');
  _socket.add(utf8.encode(requestmsg));
  print('Socket wrote ');
}

void dataHandler(Uint8List data) {
  print('data : ${utf8.decode(data)}');
}

void errorHandler(error, StackTrace trace) {
  print('error : $error');
}

void doneHandler() {
  print('Destroying socket');
}

Обновление. Я заставил сервер ответить Postman, изменив пользовательский агент или порядок заголовков, но ответ не HTTP!!? Это начинается:

      SOURCETABLE 200 OK
Server: NTRIP BKG Caster 2.0.37/2.0
Date: Tue, 20 Jun 2023 19:34:59 GMT
Connection: close
Content-Type: text/plain
Content-Length: 95593

введите здесь описание изображения. Похоже, вы запрашиваете точку монтирования. через сокет Но не соединение для получения RTCM. Если вы также хотите получить данные RTCM, вам необходимо отправить GGA в ntrip. Вот пример, который я создал во Flutter.введите сюда описание изображения

Другие вопросы по тегам