Handling callbacks in Dart classes - class

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).

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;
}

Netty client generates lots of TIME_WAIT socket states

I have written a netty client code to send some processed data to multiple clients. After running for 3-4 hours I exhaust all sockets and no more connections possible. Also when I check the socket states in the OS a large number of sockets are in TIME_WAIT state.
public class NettyClient {
private static LogHelper logger = new LogHelper(NettyClient.class);
private static EventLoopGroup workerGroup = new NioEventLoopGroup();
private static Bootstrap nettyClient = new Bootstrap()
.group(workerGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
private URL url;
private RequestVo Req;
private ChannelFuture chFuture;
private Object ReportData;
private JAXBContext jbContext;
private static final int CHANNEL_READ_TIMEOUT = 5;
public NettyClient() {
// TODO Auto-generated constructor stub
}
public NettyClient(RequestVo Req, JAXBContext jbCtx,Object data) {
this.Req = Req;
this.ReportData = data;
this.jbContext = jbCtx;
}
public void sendRequest() {
logger.debug("In sendRequest()");
//ChannelFuture chFuture = null;
try {
this.url = new URL(Req.getPushAddress());
//add handlers
nettyClient.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) {
ch.pipeline()
.addLast("timeout",
new ReadTimeoutHandler(CHANNEL_READ_TIMEOUT, TimeUnit.SECONDS));
ch.pipeline()
.addLast("codec", new HttpClientCodec());
ch.pipeline()
.addLast("inbound",
new NettyClientInBoundHandler(Req, jbContext, ReportData));
}
});
//make a connection to the Client
int port = url.getPort() == -1? url.getDefaultPort():url.getPort();
chFuture = nettyClient.connect(url.getHost(), port);
chFuture.addListener(new NettyClientConnectionListener(this.Req.getRequestId()));
} catch (Exception e) {
logger.error("Exception: Failed to connect to Client ", e);
} finally {
}
}
}
Here are the methods from ChannelInBoundHandler Class
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
{
Map<String, String> props = new HashMap<String, String>();
if(msg instanceof HttpResponse) {
logger.debug("channelRead()");
HttpResponse httpRes = (HttpResponse) msg;
HttpResponseStatus httpStatus = httpRes.status();
props.put(REQUEST_ID, this.Request.getRequestId());
props.put(CLIENT_RESPONSE_CODE, String.valueOf(httpStatus.code()));
JmsService.getInstance(DESTINATION).sendTextMessage(props, "");
logger.debug("channelRead() HttpResponse Code: " + httpStatus.code());
ctx.close();
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
Map<String, String> props = new HashMap<String, String>();
logger.error("exceptionCaught()", cause);
if(cause instanceof ReadTimeoutException) {
//If read-timeout, send back the response
props.put(REQUEST_ID, this.Request.getRequestId());
props.put(CLIENT_RESPONSE_CODE,
String.valueOf(HttpResponseStatus.REQUEST_TIMEOUT.code()));
JmsService.getInstance(DESTINATION).sendTextMessage(props, "");
ctx.close();
}
else {
logger.error("Exception: ", cause);
}
}
Any idea what is wrong in the code would greatly help me.
Thanks
I'm not familiar with netty, but I think I can explain part of your problem, and hopefully help you along the way:
When you make use of a port and then close it, the port will not automatically be available for use by other processes at once. Instead, it will go into the TIME_WAIT state for a certain period of time. For Windows, I believe this will be 240 seconds (four minutes).
I'd guess that your code is slowly using up all the available ports on your system, due to the release of ports from the TIME_WAIT state is happening too slowly.
It's not entirely clear to me where the actual port numbers are coming from (are they auto-generated by url.getDefaultPort() perhaps?), but perhaps you can find some way to reuse them? If you can keep one or more open connections and somehow reuse these, then you might be able to decrease the frequency of requests for new ports enough for the closed ports to go out of their TIME_WAIT state.

UWP Sockets: client socket StoreAsync operation not interrupted on socket close

With UWP sockets on Windows 10, I'm seeing an annoying behavior with pending asynchronous write operations not being interrupted when CancelIOAsync or Dispose is called on the socket. Below is some sample code that demonstrates the issue.
Basically, the while loop loops for 2 iterations until the send socket buffer is full (the server side connection doesn't read the data on purpose to demonstrate the problem). The next await socket.StoreAsync() operation ends up waiting indefinitely, it's not interrupted by the closing of the socket.
Any ideas on how to interrupt the pending StoreAsync?
Thanks
Benoit.
public sealed partial class MainPage : Page
{
private StreamSocket socket;
private StreamSocket serverSocket;
public MainPage()
{
this.InitializeComponent();
StreamSocketListener listener = new StreamSocketListener();
listener.ConnectionReceived += OnConnection;
listener.Control.KeepAlive = false;
listener.BindServiceNameAsync("12345").GetResults();
}
async private void Connect_Click(object sender, RoutedEventArgs e)
{
socket = new StreamSocket();
socket.Control.KeepAlive = false;
await socket.ConnectAsync(new HostName("localhost"), "12345");
DataWriter writer = new DataWriter(socket.OutputStream);
try
{
while(true)
{
writer.WriteBytes(new byte[1000000]);
await writer.StoreAsync();
Debug.WriteLine("sent bytes");
}
}
catch(Exception ex)
{
Debug.WriteLine("sent failed : " + ex.ToString());
}
}
async private void Close_Click(object sender, RoutedEventArgs e)
{
//
// Closing the client connection with a pending write doesn't cancel the pending write.
//
Debug.WriteLine("Closing Client Connection");
await socket.CancelIOAsync();
socket.Dispose();
}
private void OnConnection(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
{
System.Diagnostics.Debug.WriteLine("Received connection");
serverSocket = args.Socket;
}
}