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.введите сюда описание изображения