#sockets #dart #flutter #base64 #ktor
#сокеты #dart #flutter #base64 #ktor
Вопрос:
Я пытаюсь создать соединение сервер-клиент с использованием сокетов. Сервер — это просто эхо-сервер. Я хочу отправлять туда разные типы данных. Я начал с изображений. Чего я хочу добиться, так это :
- Проанализируйте изображение, хранящееся в папке assets, для соответствующего типа данных
- Отправьте его на сервер Echo
- Получение данных обратно на мобильный (клиентский) сайт
- Отображение изображения, отправляемого таким образом (чтобы убедиться, что данные были отправлены правильно)
Я реализовал как клиент, так и сервер. Клиент находится в Flutter, сервер в Ktor. Реализация сервера была скопирована из руководства:https://ktor.io/servers/raw-sockets.html . Что я вижу, так это то, что мой сервер получает изображение и отправляет его обратно правильно, но я не могу его показать.
Серверный код :
fun main() {
runBlocking {
val server = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().bind(InetSocketAddress("localhost", 8080))
println("Started echo telnet server at ${server.localAddress}")
while (true) {
val socket = server.accept()
launch {
println("Socket accepted: ${socket.remoteAddress}")
val input = socket.openReadChannel()
val output = socket.openWriteChannel(autoFlush = true)
try {
while (true) {
val line = input.readUTF8Line()
line?.let {
println("Client sent: $line")
output.writeStringUtf8(it)
}
}
} catch (e: Throwable) {
println("Closing socket")
e.printStackTrace()
socket.close()
}
}
}
}
}
и клиент:
class MyHomePage extends StatefulWidget {
final String title;
MyHomePage({Key key, @required this.title}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Socket _socket;
List<int> _connectionTimes = [];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: const Text('Connect to socket'),
color: Theme.of(context).accentColor,
elevation: 4.0,
onPressed: () {
closeSocket();
_connectToSocket().then((createdSocket) {
setState(() {
_socket = createdSocket;
});
});
},
),
RaisedButton(
child: const Text('Send to socket'),
color: Theme.of(context).accentColor,
elevation: 4.0,
onPressed: () {
_sendMessage();
},
),
StreamBuilder(
stream: _socket,
builder: (context, snapshot) {
if(snapshot.hasData) {
final bytes = base64Decode(utf8.decode(snapshot.data));
return Image.memory(bytes);
} else {
return Text("no image");
}
},
),
],
)),
);
}
Future<Socket> _connectToSocket() async {
final stopwatch = Stopwatch()..start();
Socket sock = await Socket.connect('10.0.2.2', 8080);
print("Connection time was ${stopwatch.elapsedMilliseconds}");
return sock;
}
void _sendMessage() async{
final imageBytes = await rootBundle.load('assets/images/dog.jpeg');
final bytesAsString = base64Encode(imageBytes.buffer.asUint8List(imageBytes.offsetInBytes, imageBytes.lengthInBytes));
print(bytesAsString);
_socket.write(bytesAsString "n");
}
void closeSocket() {
if (_socket != null) {
_socket.close();
}
}
@override
void dispose() {
_socket.close();
super.dispose();
}
}
Ошибка, которую я получаю, это :
E/flutter ( 8235): [ERROR:flutter/lib/ui/painting/codec.cc(97)] Failed decoding image. Data is either invalid, or it is encoded using an unsupported format.
I/flutter ( 8235): ══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
I/flutter ( 8235): The following _Exception was thrown resolving an image codec:
I/flutter ( 8235): Exception: operation failed
I/flutter ( 8235): ════════════════════════════════════════════════════════════════════════════════════════════════════
И, кроме того, у меня есть еще несколько вопросов:
-
Есть ли лучший способ проанализировать изображение из ресурсов во Flutter?
-
Есть ли способ отправить эти данные без добавления
n
в конце данных изображения? -
Возможно ли, что изображение будет слишком большим, и я не смогу отправить его в одном запросе? Если да, что мне следует изменить в коде, чтобы это заработало? Разделить его на несколько вызовов и использовать буфер как на клиенте, так и на сервере?
-
Что я должен изменить в коде сервера, что позволило бы мне запускать его один раз, а также подключаться и отключаться от одного клиента несколько раз? (Раздражает, что при каждом изменении, которое я вношу в код Flutter, мне приходится перезапускать сервер, чтобы он работал правильно?
Я открыт для изменения реализации сервера на другой фреймворк / язык. Я хочу использовать Flutter, но это не обязательно должен быть Ktor на сайте сервера. Просто хотел проверить это в действии.
Комментарии:
1. Я не знаю ответов на ваши вопросы, но я бы начал с регистрации данных, которые вы отправляете, получения их обратно и сравнения (после кодирования base64 при отправке и перед декодированием base64 при получении — вероятно, с минимально возможным изображением, чтобы сделать строки короче для тестирования).
2. Итак, я попробовал действительно маленькое изображение (64×64 пикселей), и мой код работает. Я также добавил проверку, которая сравнивает байты (в виде строки), которые были отправлены на сервер, и которые были получены от него. Для маленького изображения это правильно, изображение отображается. На увеличенном изображении (225×225) выдается ошибка, написанная выше, и строка (байты) Я получил с сервера совершенно другое.
3. Я сравнил с большим изображением строку, полученную на сервере. Это точно то же самое, что и при отправке с клиента. Но отправка его обратно клиенту решает что-то совершенно другое.
Ответ №1:
Я могу использовать следующий код для чтения изображения в формате jpg из фотогалереи, отправки в node.js сервер с помощью socket.io, затем передача другому клиенту (при этом сохраните изображение в mysql, точно так же, как WhatsApp отправляет изображение)
String base64Image1 = '';
// strImage1 is the path of the photo gallery retrieved by the plugin path_provider, plus the file name of the image.
String strImage1 = gv.strHomeImageFileWithPath '_01.jpg';
var filImage1 = new File(strImage1);
List<int> imageBytes1 = filImage1.readAsBytesSync();
// use the following line if another client wants to display this image in html
// base64Image1 = 'data:image/jpg;base64,' base64Encode(imageBytes1);
// Or, use the following line if another client wants to display this image in flutter
base64Image1 = base64Encode(imageBytes1);
// Send the b64 image string to the server
gv.socket.emit('PIBRequestPhotoClassify', [base64Image1]);
Кажется, что мои коды для декодирования и отображения строки изображения b64 такие же, как у вас, но коды для кодирования изображения отличаются, пожалуйста. попробуйте.
Комментарии:
1. Если я поместил файл под
assets/images/dog.jpeg
что я должен ввести какfilename of an image
? Я пытался делать:final appPath = await getApplicationDocumentsDirectory(); String strImage1 = appPath.path '/assets/images/dog_small.jpeg';
И это приводит к ошибке:[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: FileSystemException: Cannot open file, path = '/data/user/0/com.example.flutterclient/app_flutter/assets/images/dog_small.jpeg'