How to handle connection status of sockets in Flutter? - flutter

I am building an app that requires to connect to a videoconference device in order to send call commands.
My problem comes when the device gets disconnected because it's powered off, cable disconnection, or any other issue. In that scenario, I am able to show it in the app thanks to the vcfConnected variable. However, I can never get the connection back to work (only by restarting the app, which is not desirable)
I am connecting to the device via dart socket like follows. The idea behind this implementation is to try to reconnect whenever there is a problem. However, is not working fine.
Does anybody know how to better handle the connection state?
Is it better to connect to the device each time I have to send a command and close the socket after that each time?
void connect(){
Socket.connect('192.168.5.107', 55003).then((Socket socket) {
sock = socket;
vcfConnected = true;
socket.listen((data) {
String response = hex.encode(data);
dataHandler(context, response);
},
onError: (error) => errorHandler(error),
onDone: () => reconnect()
);
// Init protocol message
sendCommand('&IPV');
getMicMuteState();
getReceiveVolume();
timer = Timer.periodic(Duration(seconds: 10), (Timer t) => checkVcfStatus());
}).timeout(Duration(seconds: 15), onTimeout: () => onTimeout());
}
FutureOr<Null> onTimeout(){
reconnect();
}
void reconnect(){
print('VCF disconnected');
vcfConnected = false;
if (timer != null){
timer.cancel();
}
if (sock != null) {
sock.close();
}
sock = null;
connect();
}
void errorHandler(, error){
print('Error: $error');
reconnect();
}
void checkVcfStatus() {
sendCommand('?S');
}

Related

Udp socket in Flutter does not receive anything

I'm trying to use a udp socket as server in Flutter. I'd like to bind this socket on my localhost at 6868 port always in listening. Unfortunately when i try to send something from a client,it never prints the string "RECEIVED".
Here's the code:
static Future openPortUdp(Share share) async {
await RawDatagramSocket.bind(InternetAddress.anyIPv4,6868)
.then(
(RawDatagramSocket udpSocket) {
udpSocket.listen((e) {
switch (e) {
case RawSocketEvent.read:
print("RECEIVED");
print(String.fromCharCodes(udpSocket.receive().data));
break;
case RawSocketEvent.readClosed:
print("READCLOSED");
break;
case RawSocketEvent.closed:
print("CLOSED");
break;
}
});
},
);
}
Am I doing something wrong?
Anyway this is the client side, it is written is Lua:
local udp1 = socket.udp()
while true do
udp1:setpeername("192.168.1.24", 6868)
udp1:send("HI")
local data1 = udp1:receive()
if (not data1 == nil) then print(data1) break end
udp1:close()
end
I tested it with another server and it works well, so i don't think the client is the problem.
Thanks!
If it can help you, here my code for my SocketUDP (as singleton) in my app.
I used it in localhost and it works very well :
class SocketUDP {
RawDatagramSocket _socket;
// the port used by this socket
int _port;
// emit event when receive a new request. Emit the request
StreamController<Request> _onRequestReceivedCtrl = StreamController<Request>.broadcast();
// to give access of the Stream to listen when new request is received
Stream<Request> get onRequestReceived => _onRequestReceivedCtrl.stream;
// as singleton to maintain the connexion during the app life and be accessible everywhere
static final SocketUDP _instance = SocketUDP._internal();
factory SocketUDP() {
return _instance;
}
SocketUDP._internal();
void startSocket(int port) {
_port = port;
RawDatagramSocket.bind(InternetAddress.anyIPv4, _port)
.then((RawDatagramSocket socket) {
_socket = socket;
// listen the request from server
_socket.listen((e) {
Datagram dg = _socket.receive();
if (dg != null) {
_onRequestReceivedCtrl.add(RequestConvert.decodeRequest(dg.data, dg.address));
}
});
});
}
void send(Request requestToSend, {bool isBroadCast:false}) {
_socket.broadcastEnabled = isBroadCast;
final String requestEncoded = RequestConvert.encodeRequest(requestToSend);
List<int> requestAsUTF8 = utf8.encode(requestEncoded);
_socket.send(requestAsUTF8, requestToSend.address, _port);
}
}

How to merge aqueduct Future<RequestOrResponse> with dart console app?

Thanks to Bryan from voidrealms. I was struggling last 2 days (and weeks of resarch) and now I can get big data using dart tcp socket from the old system very fast. The process with dart console app is much faster than the dart Future project.
Now problem to mixing dart console app to aqueduct Future app. In below I put my questions after (********).
If I use Socket.connect("192..... Inside Future the process will be very slow and sometimes returns null response. So, my question is how to merge aqueduct Future with dart console app.
aqueduct.io part
class NtmsApiController extends Controller {
#override
Future<RequestOrResponse> handle(Request request) async {
try {
if (request.path.remainingPath != null) {
_requestValue = request.path.remainingPath;
// (********) In here I need to add below code, how?
}
} else {
_secureResponse = "$_errorData";
}
} catch (e) {
_secureResponse = "$_errorData";
}
return new Response.ok("$_secureResponse")
..contentType = ContentType.json;
}
}
dart console app
import 'dart:io';
import 'dart:async';
Socket socket;
String _response;
String _requestedData;
Stopwatch _stopWatch;
void main() {
_stopWatch = Stopwatch()..start();
_response = "";
_requestedData = "Q77:_:NBRT:_:6785417534\r\n";
Socket.connect("192.168.22.120", 3000).then((Socket sock) {
socket = sock;
socket.write('$_requestedData\r\n');
socket.listen(dataHandler,
onError: errorHandler,
onDone: doneHandler,
cancelOnError: false);
}).catchError((AsyncError e) {
print("Unable to connect: $e");
});
print("_requestedData: $_requestedData");
}
void dataHandler(data){
_response = new String.fromCharCodes(data).trim();
_printResponse(_response);
}
void errorHandler(error, StackTrace trace){
print(error);
}
void doneHandler(){
socket.destroy();
}
void _printResponse(String _response) {
// approximately I get 500 rows with 20 column data in 250ms
print("$_response ... (${_stopWatch.elapsedMilliseconds} ms)");
_stopWatch..stop();
if(_stopWatch.isRunning == false) {
socket.close();
// (********)return response object to aqueduct Future request_response--- how?
}
}

StreamSocket connection from HoloLens does not work properly

I'm working on a HoloLens app which works as a client in a client-server connection to a local server written in Java (you can assume the server is working properly).
HoloLens should open a StreamSocket and connect to the server, but for some reasons the code remains stuck in the process of connection.
The code is the following:
void Start ()
{
text = GameObject.Find("DEBUG_TEXT").GetComponent<Text>();
Invoke("Connect", 7f);
}
public void Connect()
{
#if !UNITY_EDITOR
Task.Run(async () => {
try
{
socket = new StreamSocket();
await socket.ConnectAsync(new HostName(ip), port); //STOP HERE
writer = new StreamWriter(socket.OutputStream.AsStreamForWrite());
reader = new StreamReader(socket.InputStream.AsStreamForRead());
await Send("Hi");
await Receive();
}
catch(Exception e)
{
text.text = e.Message;
}
});
#endif
}
Basically, the code stops at the ConnectionAsync and never goes on. On the server side, the server notifies me with "new client connected!", so i suppose HoloLens effectively connects, but something happens after.
Any idea of what could be wrong?
I ran into the same problem, and solved it with the method mentioned here.
In short, you should never do anything like Task.Wait() or Task.Result on the main thread.
In my situation, I used Task.Result and stuck right at the connect operation.
After removing it, the program works fine.
Here's the bad code:
private async Task<string> _SocketSend(string request)
{
using(StreamSocket streamSocket = new StreamSocket())
{
await streamSocket.ConnectAsync(new HostName(this.socketHost), this.port);
// other code
...
}
}
public string SocketSend(string request)
{
return _SocketSend(request).Result;
}

Handling callbacks in Dart classes

My dart application it's listening to a socket I want to return the socket reply on the command(...) function after it is processed in the dataHandler event.
import 'dart:io';
import 'dart:async';
class TeamSpeak3{
Socket socket;
String command;
String _ip;
int _port;
TeamSpeak3(String ip, int port) {
this._ip = ip;
this._port = port;
}
Future<int> connect() async {
await Socket.connect(_ip, _port)
.then((Socket sock) {
socket = sock;
socket.listen(
dataHandler,
onError: errorHandler,
onDone: doneHandler,
cancelOnError: false);
}).catchError((AsyncError e) {
print("Connection failed: $e");
exit(1);
});
socket.done;
return 1;
}
void auth(String name, String pass){
socket.write("login $name $pass\n");
}
void send(String cmd){
command = cmd;
socket.write('$cmd\n');
//return reply from dataHandler
}
void dataHandler(data){
var reply = new String.fromCharCodes(data).trim();
//return $reply on the send function
}
void errorHandler(error, StackTrace trace){
print(error);
}
void doneHandler(){
print("Connection termiated!");
socket.destroy();
exit(0);
}
}
First of all, you don't know for sure that the entire response to your send arrives in one packet, so you might not have the entire response.
Let's assume that you do (otherwise you'll have to do more processing in dataHandler to collect the response before delivering it).
The canonical way to allow a callback to be called when something has happened in the future, is to return a Future. You will also need a way to complete that future, so you create a Completer and store it until you need it. Since you can probably do more sends, you need to remember more than one completer. So, all in all, I'd write this as:
Queue<Completer<String>> _queue = Queue();
Future<String> send(String cmd){
socket.writeln(cmd);
var completer = new Completer<String>();
_queue.add(completer);
return completer.future;
}
void _dataHandler(data){
var reply = new String.fromCharCodes(data).trim();
// Add some sanity checking here. Make sure you have the entire response before
// executing the code below.
_queue.removeFirst().complete(reply);
}
(I made _dataHandler private because you probably don't want the user calling send to alse be able to call dataHandler).

Xamarin Forms Sockets

I don't get it, my friend and I are developing an API and our WebSocket services works, but not the mobile side.. I tried with a couple of clients, on the web, our echo messaging and everything works.
The thing is, I mean, the things seem like the socket is mono-directional. I tried the example of https://github.com/rdavisau/sockets-for-pcl#a-tcp-client:
var address = "127.0.0.1";
var port = 11000;
var r = new Random();
var client = new TcpSocketClient();
await client.ConnectAsync(address, port);
// we're connected!
for (int i = 0; i<5; i++)
{
// write to the 'WriteStream' property of the socket client to send data
var nextByte = (byte) r.Next(0,254);
client.WriteStream.WriteByte(nextByte);
await client.WriteStream.FlushAsync();
// wait a little before sending the next bit of data
await Task.Delay(TimeSpan.FromMilliseconds(500));
}
await client.DisconnectAsync();
First, after I get connected with this :
public async void ConnectSocketToAPIAsync()
{
SocketClient = new TcpSocketClient();
await SocketClient.ConnectAsync("my.ws.service", 4242);
ActiveSocketExchange();
}
public async void ActiveSocketExchange()
{
var bytesRead = -1;
var buf = new byte[1];
while (bytesRead != 0)
{
bytesRead = await SocketClient.ReadStream.ReadAsync(buf, 0, 1);
if (bytesRead > 0)
MessagingCenter.Send((App)Current, SOCKET_Message, System.Text.Encoding.UTF8.GetString(buf, 0, bytesRead));
}
}
Everything's fine, my TcpClient is well initialized (even the web-link becomes the http's API addr)
From my page view, when I'm done writing my text, I'm pressing the done button of the keyboard and this code is called:
private void InitSocketPart()
{
MessagingCenter.Subscribe<App, string>((App)Application.Current, App.SOCKET_Message, (sender, text) =>
{
SocketResponseText = text;
});
}
private async void OnTextCompleted(object sender, EventArgs ea)
{
var bytes = Encoding.UTF8.GetBytes(TextToSend);
try {
if (App.SocketClient.WriteStream.CanRead)
Debug.WriteLine("canRead");
if (App.SocketClient.WriteStream.CanWrite)
Debug.WriteLine("canWrite");
App.SocketClient.WriteStream.Write(bytes, 0, TextToSend.Length);
App.SocketClient.WriteStream.Flush();
} catch (Exception e)
{
Debug.WriteLine(e);
}
}
So now CanWrite && CanRead are true, but nothing at all happens, even with the use of Async methods... Why so? I don't get it..
The use of messaging center is just to have only one point of incoming message. I'm using it for other things and it works perfectly :)
Thank for any help..