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.
403 - Forbiddendoesn't have anything to do with sockets closing themselves. It is an authorization problem.Authorizationandauthorizationand see if you get different results. Which, if any, of theprintmessages 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.onDonemethod is called automatically after the little timeout. I completely agree the403 - Forbiddenhas 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.authorizationheader, 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 tocentipede, feel free to try it out but the server allows only one connection per IP, which is my case. I have updated the questionrequestmsg += "Authorization: Basic $encoded";torequestmsg += "Authorization: Basic $encoded\r\n";