-1

I am using a Ntrip client wrote in Java that is making requests using Socket.

This works fine with this code :

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));
        }
    }

I would like to do the equivalent in Flutter without the native code, so i have tried :

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();
  }

But this is not working, the socket is closing itself after a very short time (like 2sec) after it's connected and it has nothing to do with the message written (even without it it's closing).

I have also tried using 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");
        }

And i am getting a 403 - - Forbidden message in the logs.

Note : if i use https i have a handshake error and i have also tried the :

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;
      };
  }
}

But this doesn't help either.

The same request is working fine in Java, can you tell me what am i doing wrong please ?

I would like to avoid using native code in my project and especially understand what is wrong with Flutter / dart here ?

Thanks a lot in advance

EDIT :

Here is the log i get in Flutter with the socket :

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

Here is the caster i am trying to reach but there is no public API.

Tom3652
  • 2,540
  • 3
  • 19
  • 45
  • Define "the socket is closing." Sockets don't just close themselves. What symptoms are you observing that led you to that conclusion? And `403 - Forbidden` doesn't have anything to do with sockets closing themselves. It is an authorization problem. – user207421 Jun 20 '23 at 10:46
  • Regarding the 403 error, this may be caused by the server being case-sensitive to header names (incorrectly). Try with Postman and try naming your header `Authorization` and `authorization` and see if you get different results. Which, if any, of the `print` messages get printed in your socket code? Since this is HTTP, use a packet sniffer like wireshark to see who closes the connection - it may be the server. – Richard Heap Jun 20 '23 at 12:24
  • @user207421 The `onDone` method is called automatically after the little timeout. I completely agree the `403 - Forbidden` has nothing to do, but this is the error i am getting with the above code that's why i am so confused and asked a question because it makes no sense. – Tom3652 Jun 20 '23 at 12:33
  • Thanks for the suggestion @RichardHeap, i will try that and update the question. – Tom3652 Jun 20 '23 at 12:33
  • The `authorization` header, with the first letter upper or lower case doesn't change the forbidden error unfortunately (i have also tested from Postman and get the forbidden). The username and password are actually public and both equal to `centipede`, feel free to try it out but the server allows only one connection per IP, which is my case. I have updated the question – Tom3652 Jun 20 '23 at 13:07
  • Try changing `requestmsg += "Authorization: Basic $encoded";` to `requestmsg += "Authorization: Basic $encoded\r\n";` – Richard Heap Jun 20 '23 at 14:04

2 Answers2

1

Strangely, I can't get Postman to work either. But this modified version of your Dart code does. (There are 3 or 4 changes - the User Agent, the line break after the auth header, the encoding and decoding of the bytes.)

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');
}

Update - I got the server to respond to Postman by changing the user agent - or the order of headers, but the response isn't HTTP!!? It starts:

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
Richard Heap
  • 48,344
  • 9
  • 130
  • 112
  • Hey @Richard it is perfect this is the expected response thank you so much for your time. Yes this is a weird system but i am working on it for a client.. Really appreciate your help – Tom3652 Jun 21 '23 at 09:29
  • 1
    Consider simply adding the bytes received in `dataHandler` into a bytes buffer https://api.dart.dev/stable/2.5.2/dart-io/BytesBuilder-class.html When the whole content has been received you can convert it to a string and begin parsing it. FYI, the server seems to have some test that implements `userAgent.startsWithIgnoreCase('NTRIP')` and rejects the request otherwise(!) – Richard Heap Jun 21 '23 at 11:58
  • Thank you ! It's indeed what is done in Java, i will do the equivalent in Flutter but thanks for pointing out the `BytesBuilder`. – Tom3652 Jun 21 '23 at 14:33
0

enter image description hereIt looks like you are requesting a mountpoint. via socket But not the connection to receive RTCM. When you want to get RTCM data too, you have to send GGA to ntrip. Here is an example I made app in flutter.enter image description here

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 28 '23 at 13:57