Send web socket message to server directly after connection - flutter

The WebSocket server I use needs to receive a message after the connection.
The only way seems to use WebSocket.connect instead of WebSocketChannel.connect (https://github.com/dart-lang/web_socket_channel/issues/209):
import 'package:web_socket_channel/io.dart';
// ...
IOWebSocketChannel? _channel;
// ...
WebSocket.connect("ws://...").then((ws) {
this._channel = IOWebSocketChannel(ws);
_channel!.sink.add(msg);
}
But this is not supported by web. How is it possible to do that with both mobile and web support ?

WebSocket.connect(socketUrl).then((value) {
_channel = IOWebSocketChannel(value);
_channel?.stream.listen(onData,
onError: onError, onDone: onDone, cancelOnError: true);
debugPrint(
"webSocket——readyState:${_channel?.innerWebSocket?.readyState}");
if (_channel?.innerWebSocket?.readyState == 1) {
} else {}
}).timeout(const Duration(seconds: 10));

Related

Client only listens to server when sends a message

I'm implementing socket.
Two clients connect to the server with no problem, when client1 sends a message to the server, the server publishes it to every other client (which in this case is client2). but client2 won't get the message unless it sends a message. It seems the listener of the client doesn't work. Obviously, I want client2 to get the message from client1 instantly.
here is my sever code:
import 'dart:io';
import 'dart:typed_data';
void main() async {
// bind the socket server to an address and port
MySocket mySocket = MySocket();
await mySocket.init();
}
class MySocket {
ServerSocket? server;
List<Socket> clients = [];
//initialize the socket
init() async {
server = await ServerSocket.bind("192.168.0.112", 4000);
// listen for client connections to the server
server!.listen((client) {
handleConnection(client);
addClient(client);
});
}
void handleConnection(Socket client) async {
print('Connection from'
' ${client.remoteAddress.address}:${client.remotePort}');
// listen for events from the client
client.listen(
// handle data from the client
(Uint8List data) async {
await Future.delayed(Duration(seconds: 1));
final message = String.fromCharCodes(data);
print(message);
publish(message, client);
},
// handle errors
onError: (error) {
print(error);
client.close();
},
// handle the client closing the connection
onDone: () {
print('Client left');
client.close();
},
);
}
void addClient(Socket client) {
//if client doesn't already exist add it to the list of clients
if (!clients.any((element) =>
'${client.remoteAddress.address}:${client.remotePort}' ==
'${element.remoteAddress.address}:${element.remotePort}')) {
clients.add(client);
}
}
void publish(String message, Socket client) {
//write the message to every client except the author of it
clients.forEach((element) async {
if ('${client.remoteAddress.address}:${client.remotePort}' !=
'${element.remoteAddress.address}:${element.remotePort}') {
element.write(message);
}
});
}
}
here is my client-side code:
import 'dart:io';
import 'dart:typed_data';
void main() async {
//gets the username
String name = '';
while (name.isEmpty) {
print('Enter your name: ');
name = stdin.readLineSync() ?? '';
}
// connect to the socket server
final socket = await Socket.connect("192.168.0.112", 4000);
print('Connected to: ${socket.remoteAddress.address}:${socket.remotePort}');
// listen for responses from the server
socket.listen(
// handle data from the server
(Uint8List data) {
final serverResponse = String.fromCharCodes(data);
print('$serverResponse');
},
// handle errors
onError: (error) {
print(error);
socket.destroy();
},
// handle server ending connection
onDone: () {
print('Left server.');
socket.destroy();
},
);
// sending messages to the server
String message = "";
while (message != "exit") {
message = stdin.readLineSync() ?? '';
await sendMessage(socket, name, message);
}
socket.close();
}
Future<void> sendMessage(Socket socket, String name, String message) async {
socket.write('$name: $message');
await Future.delayed(Duration(seconds: 2));
}
Thank you in advance.
The issue is because stdin.readLineSync() in your client blocks the current thread. You can get around this by spawning an isolate to handle that portion of the code, so that it does not block the socket.listen from printing out the responses from the server.
See updated client code below:
import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
void main() async {
//gets the username
String name = '';
while (name.isEmpty) {
print('Enter your name: ');
name = stdin.readLineSync() ?? '';
}
// connect to the socket server
final socket = await Socket.connect("192.168.0.112", 4000);
print('Connected to: ${socket.remoteAddress.address}:${socket.remotePort}');
// listen for responses from the server
socket.listen(
// handle data from the server
(Uint8List data) {
final serverResponse = String.fromCharCodes(data);
print('$serverResponse');
},
// handle errors
onError: (dynamic error) {
print(error);
socket.destroy();
},
// handle server ending connection
onDone: () {
print('Left server.');
socket.destroy();
},
);
final receive = ReceivePort();
final isolate = await Isolate.spawn(readMessages, receive.sendPort);
await for (final message in receive) {
if (message == 'exit') break;
await sendMessage(socket, name, message as String);
}
socket.close();
}
void readMessages(SendPort port) {
String message = '';
while (message != 'exit') {
message = stdin.readLineSync() ?? '';
port.send(message);
}
Isolate.exit(port);
}
Future<void> sendMessage(Socket socket, String name, String message) async {
socket.write('$name: $message');
await Future<void>.delayed(Duration(seconds: 2));
}

Testing WebSocket connections

I'm using WebSocket from dart:io and IOWebSocketChannel (https://pub.dev/packages/web_socket_channel) to create a connection with channel subscriptions.
The code is largely working, I can connect to my server and subscribe to channels, receive data etc, but I'm struggling with an approach to writing tests.
Given this constructor:
SocketServer.Connect(this.url, {this.onConnect}) {
channel = IOWebSocketChannel.connect(url);
_stream = channel.stream.listen((data) {
this.onConnect();
final payload = jsonDecode(data);
print('Received: ');
print(payload);
}, onError: (_) {
this.disconnect();
});
}
I've tried variations on the following:
test("it should create a connection", () async {
final server = await HttpServer.bind('localhost', 0);
server.transform(WebSocketTransformer()).listen((WebSocket socket) async {
final channel = await IOWebSocketChannel(socket);
channel.sink.add('test');
});
bool connected = false;
final websocketServer = SocketServer.Connect('ws://localhost:${server.port}/cable', onConnect: (SocketServer connection) {
print('connected');
connected = true;
});
expectLater(connected, true);
});
But I don't receive the 'connected' output, since my HttpServer isn't sending back the correct responses.
Would it be best to depend on a real server to handle the connections, or create one in the tests?

How to send image over socket connection?

I have been trying to send image over socket connection from client to server which will be saved on the server.
The socket connection works fine for string messages but when I try to send an image, it is not transmitted correctly. Please give me a clue on what's the right way to do it.
Server-side code:
import 'dart:io';
import 'dart:typed_data';
void main() async {
Uint8List bytes= await File('1.jpg').readAsBytes();
final socket = await Socket.connect('localhost', 8000);
print('Connected to: ${socket.remoteAddress.address}:${socket.remotePort}');
// listen for responses from the server
socket.listen(
// handle data from the server
(Uint8List data) {
final serverResponse = String.fromCharCodes(data);
print('Server: $serverResponse');
},
// handle errors
onError: (error) {
print(error);
socket.destroy();
},
// handle server ending connection
onDone: () {
print('Server left.');
socket.destroy();
},
);
// send some messages to the server
await sendMessage(socket, bytes);
}
Future<void> sendMessage(Socket socket, Uint8List message) async {
print('Client: $message');
socket.write(message);
}
Client-side code:
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:image/image.dart';
void main() async {
// bind the socket server to an address and port
final server = await ServerSocket.bind('127.0.0.1', 8000);
// listen for clent connections to the server
server.listen((client) {
handleConnection(client);
});
}
void handleConnection(Socket client) {
print('Connection from'
' ${client.remoteAddress.address}:${client.remotePort}');
// listen for events from the client
client.listen(
// handle data from the client
(Uint8List data) async {
// final message = String.fromCharCodes(data);
print(data);
await File('new.jpg').writeAsBytes(data);
},
// handle errors
onError: (error) {
print(error);
client.close();
},
// handle the client closing the connection
onDone: () {
print('Client left');
client.close();
},
);
}
Faulty image on server-side:
Just change socket.write(message) to socket.add(message) at the client-side and you are good to go
Future<void> sendMessage(Socket socket, Uint8List message) async {
print('Client: $message');
socket.add(message);
}
because socket.write(object) Converts object to a String by invoking Object.toString.
have a nice day:)

Flutter websocket onOpen?

I'm trying to create a websocket connection in Flutter.
I followed this tutorial and this
My query is that how can we know once the websocket connection is established? Like there is onOpen event in Javascript.
My code in flutter is:
void main() async {
final channel = IOWebSocketChannel.connect('wss://my.server.org');
channel.stream.listen(
(dynamic message) {
debugPrint('message $message');
},
onDone: () {
debugPrint('ws channel closed');
},
onError: (error) {
debugPrint('ws error $error');
},
);
}
This is what I used in one project to connect to AWS AppSync service. The communication is implemented over websockets.
What you do is parse the messages you receive through web socket. Messages will carry the data, but also there are messages to tell you that the connection was established, keep alive message etc.
I don't remember if the messages below are AppSync specific, or are applicable to any websocket server - but it should at least give you an idea how this could work.
var channel=IOWebSocketChannel.connect(url, protocols: ['graphql-ws']);
channel.sink.add(json.encode({"type": "connection_init"}));
channel.stream.listen((event) {
var e = json.decode(event);
switch (e['type']) {
case 'connection_ack':
wsTimeoutInterval = e['payload']['connectionTimeoutMs'];
var register = {
'id': uniqueKey,
'payload': {
'data': json.encode(query),
'extensions': {'authorization': header}
},
'type': 'start'
};
var payload = json.encode(register);
channel.sink.add(payload);
break;
case 'data':
// calling a callback function Function(Map<String, dynamic>) listener
listener(e);
break;
case 'ka':
print('Keep alive message received!!!');
break;
case 'start_ack':
print('Ws Channel: Subscription started');
break;
default:
print('Unimplemented event received $event');
}
}, onError: (error, StackTrace stackTrace) {
// error handling
print('Ws Channel: $error');
}, onDone: () {
// communication has been closed
channel.sink.close();
print('Ws Channel: Done!');
});

How to handle socket disconnects in Dart?

I'm using Dart 1.8.5 on server.
I want to implement TCP Socket Server that listens to incoming connections, sends some data to every client and stops to generate data when client disconnects.
Here is the sample code
void main() {
ServerSocket.bind(
InternetAddress.ANY_IP_V4,
9000).then((ServerSocket server) {
runZoned(() {
server.listen(handleClient);
}, onError: (e) {
print('Server error: $e');
});
});
}
void handleClient(Socket client) {
client.done.then((_) {
print('Stop sending');
});
print('Send data');
}
This code accepts connections and prints "Send data". But it will never print "Stop sending" even if client was gone.
The question is: how to catch client disconnect in listener?
A Socket is bidirectional, i.e. it has an input stream and an output sink. The Future returned by done is called when the output sink is closed by calling Socket.close().
If you want to be notified when the input stream closes try using Socket.drain() instead.
See the example below. You can test it with telnet. When you connect to the server it will send the string "Send." every second. When you close telnet (ctrl-], and then type close). The server will print "Stop.".
import 'dart:io';
import 'dart:async';
void handleClient(Socket socket) {
// Send a string to the client every second.
var timer = new Timer.periodic(
new Duration(seconds: 1),
(_) => socket.writeln('Send.'));
// Wait for the client to disconnect, stop the timer, and close the
// output sink of the socket.
socket.drain().then((_) {
print('Stop.');
timer.cancel();
socket.close();
});
}
void main() {
ServerSocket.bind(
InternetAddress.ANY_IP_V4,
9000).then((ServerSocket server) {
runZoned(() {
server.listen(handleClient);
}, onError: (e) {
print('Server error: $e');
});
});
}