【发布时间】:2019-08-15 00:56:18
【问题描述】:
我正在尝试使用套接字创建服务器-客户端连接。服务器只是一个回声服务器。我想在那里发送不同类型的数据。我从图像开始。我想要实现的是:
- 将存储在资产文件夹中的图像解析为适当的数据类型
- 发送到 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。只是想检查一下。
【问题讨论】:
-
我不知道您的问题的答案,但我会首先记录您发送的数据并返回并比较它们(发送时base64编码之后,base64解码之前接收 - 可能使用尽可能小的图像以使字符串更短以进行测试)。
-
所以我尝试了非常小的图像(64x64 像素)并且我的代码有效。我还添加了检查,它比较发送到服务器和从服务器接收的字节(作为字符串)。对于小图像,它是正确的,显示图像。在更大的图像(225x225)上面写的错误被抛出,我从服务器收到的字符串(字节)完全不同。
-
我比较了大图,服务器上接收到的字符串。它完全一样,因为它是从客户端发送的。但是将其发送回客户端会以完全不同的方式解决。
标签: sockets dart flutter base64 ktor