Pattern for async request/response over TCP socket - sockets

I'm trying to implement a Dart client for the mpd protocol. mpd communicates over a TCP connection by exchanging text messages. A command is sent and terminated by \n. The server replies with one or multiple lines delimited by \n and ends a response with a OK or ACK message.
I'm struggling to implement a request / response approach due the async nature of Dart and the Socket class. I'm coming from Java & Go and the async approach is not natural to me.
This is how I want a caller to use the client:
MpdClient c = MpdClient('hostname', 6600);
await c.connect();
String response = await c.command('status');
print(response);
await c.close();
In the client, connecting is no issue:
Socket socket = await Socket.connect(hostname, port);
Then I'm not sure what to do with the socket and how to implement the Future<string> command(String cmd) function. I am able to listen() on the Socket and decode the response, but I don't see how I can tie that to a Future<string> returned by c.command(...).
I also tried to use the stream methods on Socket (which works well via .takeWhile() and .fold() to decode a response). Something like this:
Future<String> command(String cmd) {
String last = "";
return _stream
.map((event) => event.toList())
.transform(utf8.decoder)
.takeWhile((event) {
if (last.startsWith("OK") || last.startsWith("ACK")) {
return false;
} else {
last = event;
return true;
}
})
.fold("", (previous, element) => previous + element);
}
But the Socket stream can only be subscribed once, so this method cannot be called multiples times.
Is there a way to achieve what I want from the caller side with the Socket class? Or would I be better off using RawSocket and its read() method which offers a more low-level / controlled way to read the response?

Related

Function returns before its finished

I'm trying to implement a socket connection between a python server and a flutter client,
everything works well, but when I want to get the response back the function returns null and then prints the response.
I'm receiving the response but after the function returns.
String rcev_response() {
String response = "";
socket.listen((event) {
response = utf8.decode(event);
print(response);
});
return response;
}
Any idea what's happening here and how can I fix it?
EDIT:
After a little bit of debugging, I found out that socket.listen() doesn't actually stop. so the function returns the value before its assigned
and I added a 1ms delay before the return and now it's working properly.
Timer(Duration(seconds: 1), onceAtTheEndOfTheBatch);
this is still not a solution. any help would be appreciated.
try to add async
Future<String> rcev_response() async {
String response = "";
await socket.listen((event) {
response = utf8.decode(event);
print(response);
});
return response;}
maybe this help you :)
The anonymous function that you are passing to listen is going to be called multiple times with chunks of the response. It's up to you to concatenate them. As you need a string you can use the stream join convenience function.
Future<String> rcev_response() async {
Socket s = await Socket.connect(ipAddr, port);
s.add(utf8.encode(request));
String result = await utf8.decoder.bind(s).join();
await s.close(); // probably need to close the socket
return result;
}
This assumes that the server will close the socket when it's finished sending.
A solution but still not optimal.
I found out that it works properly if I close the socket from the server side and re-initialize it every time I send a request from the client side.
For example - Server side process:
# accept connection
client_socket, client_address = self.server_socket.accept()
# receive the message
client_socket.recv()
# send response
client_socket.send()
# close socket
client_socket.close()
But I still want to keep the connection alive as a full duplex connection, if anyone can help.

listen to events on the client side only when a change has occurred Dart?

Is there some way to use a listener that listens for changes on the server and sends the changes to UI as soon as those changes happen? I'm using a websocket (singalr package to be exact) and I need to set up an event listener so that the client side captures all changes. I read that I need to use streams instead of futures and I can also subscribe to the event there, but is this suitable for the application to poll the server every time only when it actually changes, and not constantly? For example, I read that in a java script, you can use an event listener that independently "understand" that changes have occurred on the server side and pass them on to the frontend side (I am not a JS developer, but I have heard that JS has such functionality, so if there is something like this in Dart?). Right now I'm using the following code, but I'm not sure if it does exactly what I described
static Stream<String?> getData() =>
Stream.periodic(Duration(seconds: 1)).asyncMap((event) => fetchData());
static Future<String?> fetchData() async {
final hubConnection =
HubConnectionBuilder().withUrl('https://secure/secure').build();
await hubConnection.start();
String? values;
if (hubConnection.state == HubConnectionState.Connected) {
await hubConnection
.invoke('GetData')
.then((value) => values= value as String);
}
print('values');
hubConnection.onclose(({error}) {
throw Exception(error);
});
print(values);
return values;
}

Why can not I read bytes from the TcpClient in C#?

Why can not I read bytes from the TcpClient in C#?
Here is the error I am getting:
Unable to read data from the transport connection: An established connection was aborted by the software in your host machine.
Here is how I start my TcpClient:
public static async void Start()
{
TcpListener server = null;
try
{
server = new TcpListener(IPAddress.Loopback, 13000);
server.Start();
var client = await server.AcceptTcpClientAsync();
var stream = client.GetStream();
var bytes = Convert.FromBase64String("ABCD");
await stream.WriteAsync(bytes, 0, bytes.Length);
client.Close();
}
catch (Exception e)
{
throw;
}
finally
{
if(server != null)
{
server.Stop();
}
}
}
Here is how I run a request to the TcpClient:
try {
var response = (new HttpClient()).GetByteArrayAsync("http://localhost:13000").Result;
return Convert.ToBase64String(response);
} catch(Exception e) {
throw;
}
The return Convert.ToBase64String(response); line is never reached. While I see the quoted above error message inside the Exception e if I hit a breakpoint on the throw line.
Also, during debug the Start() method completes just fine. I.e. it starts, then wait for a request, gets a request, writes to the TclClient and at the end runs the server.Stop(); command.
I am expecting my code to work, because I took it and modified from the official documentation over here.
I tried to check out a few resources which would tackle my exception, but none of them did help.
E.g. I tried to use the question.
First answer tells nothing useful actually, but just plays around with words and at the end states that one can do nothing about the exception (please, correct me if I am missing a point in the answer).
And the second answer tells an impossible in my case problem. Because, I am sure there is nothing running on the 13000 port.
Your client code is using HttpClient, which sends an HTTP request and expects an HTTP response. But your server is not an HTTP server, it is just a plain TCP server, so the client is likely to fail and forcibly close the connection when it doesn't receive a properly formatted HTTP response.
The "official documentation" whose example you modified is not using HttpClient at all, it is using TcpClient instead.
If you want to use HttpClient in your client, then you should use HttpListener instead of TcpListener in your server.

Cancelling a StreamSubscription in flutter

I have the following stream subscription
Stream<Uint8List> stream = await USBSerialSingleton.instance.inputStream;
usbStream = stream.listen(onDataReceivedFromUSBSerial);
The inputStream is a broadcast stream exposed like this.
Future<Stream<Uint8List>> get inputStream async {
final UsbPort port = await this.port;
return port.inputStream.asBroadcastStream();
}
I would like to stop listening to the stream, so I am calling
usbStream.cancel();
But I keep receiving messages on my onDataReceivedFromUSBSerial method. I keep getting messages on the onDataReceivedFromUSBSerial even if I close the dialogue that all this is implemented on.
Question: How do I stop listening to my usbStream?
You have to await for the subscription cancelation so do:
await usbStream.cancel();

Netty: when does writeAndFlush channel future listener callback get executed?

I am new to netty and trying to understand how the channel future for writeAndFlush works. Consider the following code running on a netty client:
final ChannelFuture writeFuture = abacaChannel.writeAndFlush("Test");
writeFuture.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
if (writeFuture.isSuccess()) {
LOGGER.debug("Write successful");
} else {
LOGGER.error("Error writing message to Abaca host");
}
}
});
When does this writeFuture operationComplete callback executed?
After netty hands over the data to the OS send buffers (or)
After the OS writes the data to the network socket. (or)
After this data is actually received by the server.
TIA
1. After netty hands over the data to the OS send buffers (or)
Listener will be notified after data is removed from ChannelOutboundBuffer (netty's send buffer)