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.
Related
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?
I'm making an app with Flutter (Dart) and Firebase.
displayName = (await instance.collection('users').doc(ds.id).get())
.data()['displayName'];
When retrieving the Firestore document as described above, for example,
if I cannot get it after waiting 10 seconds,
I want to set the displayName to '' (empty string).
How should I write if I want to do something like that?
try {
Future.delayed(Duration(seconds:5,),(){
throw Exception('eeeeeewwwwwwwww');
});
await Future.delayed(Duration(seconds:10),);
//When I actually execute the code of ↓,
//there are almost no cases where it takes time to acquire (it can be acquired in a short time),
//so I tried to write a pseudo case like ↑ that can not be acquired.
/*
displayName = (await instance.collection('users').doc(ds.id).get())
.data()['displayName'];
url = (await instance.collection('users').doc(ds.id).get())
.data()['photoUrl'];
*/
}catch(e){
displayName='time out';
url=null;
continue;//(← Since it is in a for statement, if it takes 10 seconds or more, I want to skip to the next loop.)
}
The above code came to my mind, and I thought I could do it, but when I actually executed it,
The exception is thrown, but the catch clause is not executed (the exception was not caught).
I'm not sure why this doesn't work, but why not?
Is there a theory method for such cases?
When I googled, it says that when we use Dio, we can handle such timeout error by just setting the number of seconds (because it is in English, we can only understand the rough atmosphere).
However, the reason why people say "Dio looks good" is that if we don't use Dio, we have to write this kind of processing ourself, but with Dio we can easily do it?
You can try something like this although I haven't tested the code:
Future<String> fetchOrSetUser() async {
final String displayName;
try {
final userRef = await instance.collection('users').doc(ds.id).get().timeout(Duration(seconds: 10));
displayName = userRef.data()['displayName'];
} on TimeoutException catch (error) {
await instance.collection('users').doc(ds.id).update({
'displayName': ''
});
displayName = '';
} catch (error, _) {
rethrow;
}
return displayName;
}
It sets a timeout on the async call of 10 seconds and then tries to catch the TimeoutException. If that happens, it updates the userRef's displayName in the on TimeoutException block. I'm not sure if this would also fail if the user has no internet but I think updates can be written without internet... ?
Let me know if it doesn't work
We are in a situation where the production app is facing the following socket exception and not able to perform any other network operation after this.
DioError [DioErrorType.DEFAULT]: SocketException: Failed host lookup: ‘xyz.abc.com’ (OS Error: nodename nor servname provided, or not known, errno = 8)
Note: Encountered repetitively with one user having iPhone X, iOS 14.4
We are using Dio as a network client, with Retrofit, which internally uses the HttpClient from the dart. With Dio the exception is not reproducible with the simulated environment but using HttpClient directly, the same exception can be reproduced with the following code in iOS simulator.
HttpClient userAgent = new HttpClient();
bool run = true;
while (run) {
try {
await userAgent.getUrl(Uri.parse('https://www.google.com'));
print('Number of api executed');
} catch (e) {
print(e);
if (e is SocketException) {
if ((e as SocketException).osError.errorCode == 8)
print('***** Exception Caught *****');
}
}
}
Once the exception was thrown, the HttpClient was not able to recover from that stale state and all other API requests were started failing with the same error.
We were able to recover from that stale state by force closing all the previous connections and opening up a new HttpClient.
HttpClient userAgent = new HttpClient();
bool run = true;
while (run) {
try {
await userAgent.getUrl(Uri.parse('https://www.google.com'));
print('Number of api executed');
} catch (e) {
print(e);
if (e is SocketException) {
if ((e as SocketException).osError.errorCode == 8)
print('***** Exception Caught *****');
}
userAgent.close(force: true);
print('Force closing previous connections');
userAgent = HttpClient();
print('Creating new HttpClient instance');
}
}
One interesting fact is after every 236 requests the exception is raising. It could be because of file descriptors over usage but iOS has a limit of 256. 🙄
With a stable internet connection, this issue reproducible every time in iOS simulator.
Although I am not able to reproduce the issue with Dio client but as in production it is occurring. So I am seeking help to understand the root cause of this issue, also how we can prevent it?
Anyone who has come across this kind of situation and how you have overcome it, please help me.
Thanks in advance.
That's a strange error.
This might not answer your question, but may push us towards figuring out what's going on.
The code snippet (copied from question) will open up a new stream with each .getUrl() call and will not close them. (I'm assuming this is intentional to create the socket exception?)
HttpClient userAgent = new HttpClient();
bool run = true;
while (run) {
try {
await userAgent.getUrl(Uri.parse('https://www.google.com'));
print('Number of api executed');
} catch (e) {
print(e);
if (e is SocketException) {
if ((e as SocketException).osError.errorCode == 8)
print('***** Exception Caught *****');
}
}
}
At some point, a limit (of open streams) is hit. I guess that magic number is 236 in your case.
So at that point, is when you're seeing the nodename or servname provided exception?
(Btw, as an aside, I think that error is coming from the underlying host operating system's DNS service, although I'm not sure if it's due to the request spam, the number of open connections, etc. This may not be relevant info.)
So, if you used the HttpClient in a typical way, making requests & closing those open streams, such as this:
var request = await userAgent.getUrl(Uri.parse('http://example.com/'));
var response = await request.close(); // ← close the stream
var body = await response.transform(utf8.decoder).join();
// ↑ convert results to text
// rinse, repeat...
... Are you still seeing the same nodename or servname provided error pop up?
With this "typical usage" code immediately above, the userAgent can be reused until a userAgent.close() call is made (and the HttpClient is permanently closed.
Trying to use it again would throw a Bad State exception).
I'd be interested to hear if the nodename error still occurs with this modified code.
Re: the second code snippet from the question.
In the catch block, the HttpClient is closed, then a new HttpClient is created. This effectively closes all the open streams that were opened in the try block (and I assume, resetting the limit of open streams.)
If you adjusted the 2nd code example to use:
var req = await userAgent.getUrl(Uri.parse('https://www.google.com'));
userAgent.close(force: true);
userAgent = HttpClient();
print('Number of api executed');
Could you run that indefinitely?
i have same issue resolve with this code:-
Exmaple
//Add This Class
class MyHttpOverrides extends HttpOverrides{
#override
HttpClient createHttpClient(SecurityContext? context){
return super.createHttpClient(context)
..badCertificateCallback = (X509Certificate cert, String host, int port)=> true;
}
}
Future<void> main() async {
HttpOverrides.global = MyHttpOverrides(); //call here
runApp(const MyApp());
}
1:Obtain the current limit of file descriptors
ulimit -n
An example output: “256” or “10032”.
PROTIP: On MacOS, the maximum number that can be specified is 12288.
Obtain the current limit of processes
ulimit -u
An example output: “1418”.
sudo launchctl limit maxfiles 65536 200000
I got exactly the same errors in production, it happens intermittently. Like Baker said, close the connections:
import 'package:http/http.dart' as http;
Future<http.Response> get(String url) async {
var httpClient = http.Client() as http.BaseClient;
Map<String, String> headers = {};
headers['Content-Type'] = 'application/json; charset=UTF-8';
var result = await httpClient
.get(Uri.parse(url), headers: headers)
.timeout(
const Duration(seconds: 60),
onTimeout: () => http.Response('Request Timeout', 408),
);
httpClient.close();
return result;
}
I did 10x Future.Delayeds each doing a loop with 300 get requests at the same time, didn't find any issues.
The future delayeds was done like so:
Future.delayed(const Duration(milliseconds: 10), () async {
for (var i = 0; i < 300; i++) {
var pingResult = await Api.instance.ping();
print('Delayed 1 Result (${i}): ${pingResult.success}');
}
});
Future.delayed(const Duration(milliseconds: 10), () async {
for (var i = 0; i < 300; i++) {
var pingResult = await Api.instance.ping();
print('Delayed 2 Result (${i}): ${pingResult.success}');
}
});
//..
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.
I need to send several commands over telnet to a server. If I try to send them without a time delay between every command, the server freaks out:
var net = require('net');
var conn = net.createConnection(8888, 'localhost');
conn.on('connect', function() {
conn.write(command_1);
conn.write(command_2);
conn.write(command_3);
//...
conn.write(command_n);
})
I guess the server needs some time to respond to command n before I send it command n+1. One way is to write something to the log and fake a "wait":
var net = require('net');
var conn = net.createConnection(8888, 'localhost');
conn.on('connect', function() {
console.log('connected to server');
console.log('I'm about to send command #1');
conn.write(command_1);
console.log('I'm about to send command #2');
conn.write(command_2);
console.log('I'm about to send command #3');
conn.write(command_3);
//...
console.log('I'm about to send command #n');
conn.write(command_n);
})
It might also be the fact that conn.write() is asynchronous, and putting one command after another doesn't guranty the correct order??
Anyway, what is the correct pattern to assure correct order and enough time between two consecutive commands, for the server to respond?
First things first: if this is truly a telnet server, then you should do something with the telnet handshaking (where terminal options are negotiated between the peers, this is the binary data you can see when opening the socket).
If you don't want to get into that (it will depend on your needs), you can ignore the negotiation and go straight to business, but you will have to read this data and ignore it yourself.
Now, in your code, you're sending the data as soon as the server accepts the connection. This may be the cause of your troubles. You're not supposed to "wait" for the response, the response will get to you asynchronously thanks to nodejs :) So you just need to send the commands as soon as you get the "right" response from the server (this is actually useful, because you can see if there were any errors, etc).
I've tried this code (based on yours) against a device I've got at hand that has a telnet server. It will do a login and then a logout. See how the events are dispatched according to the sever's response:
var net = require('net');
var conn = net.createConnection(23, '1.1.1.1');
var commands = [ "logout\n" ];
var i = 0;
conn.setEncoding('ascii');
conn.on('connect', function() {
conn.on('login', function () {
conn.write('myUsername\n');
});
conn.on('password', function () {
conn.write('myPassword\n');
});
conn.on('prompt', function () {
conn.write(commands[i]);
i++;
});
conn.on('data', function(data) {
console.log("got: " + data + "\n");
if (data.indexOf("login") != -1) {
conn.emit('login');
}
if (data.indexOf("password") != -1) {
conn.emit('password');
}
if (data.indexOf(">#") != -1) {
conn.emit('prompt');
}
});
});
See how the commands are in an array, where you can iteratively send them (the prompt event will trigger the next command). So the right response from the server is the next prompt. When the server sends (in this case) the string ># another command is sent.
Hope it helps :)
The order of writes is guaranteed. However:
You must subscribe to data event. conn.on('data', function(data)
{}) will do.
You must check return values of writes - if a write
fails, you must wait for 'drain' event. So you should check if any
write really fails and if it does then fix the problem. If it
doesn't - then you can leave current dirty solution as is.
You
must check if your server supports request piplining (sending
multiple requests without waiting for responses). If it doesn't -
you must not send next request before receiving a data event after
the previous one.
You must ensure that the commands you send are real telnet commands - telnet expects a \0 byte after \r\n (see the RFC), so certain servers may freak if \0 is not present.
So:
var net = require('net');
var conn = net.createConnection(8888, 'localhost');
conn.on('connect', function() {
console.log(conn.write(command_1) &&
conn.write(command_2) &&
conn.write(command_3) &&
//...
conn.write(command_n))
})
conn.on('data', function () {})
If it writes false - then you must wait for 'drain'. If it writes true - you must implement waiting. I discourage the event-based solution and suggest to look at using async or Step NPM modules instead.