Basically, I'm trying to check the status of my WebSocket Server.ws. However, when I query Server.ws.readyState, the only response I ever get is WebSocket.OPEN. How do I check if a WebSocket is disconnected if it always returns WebSocket.OPEN?
For example, I've tried to turn off the WiFi of the device used to test the Flutter app. Normally, after one second, the WebSocket is assumed disconnected and the connection is closed with a WebSocketStatus.GOING_AWAY close code. I assumed it would also change the WebSocket.readyState, but that doesn't seems to be the case.
So, how do I properly check the status of my WebSocket?
How I'm currently checking :
/// Connection status
IconButton _status() {
IconData iconData;
switch (Server.ws?.readyState) {
case WebSocket.CONNECTING:
print("readyState : CONNECTING");
iconData = Icons.wifi;
break;
case WebSocket.OPEN:
print("readyState : OPEN");
iconData = Icons.signal_wifi_4_bar;
break;
case WebSocket.CLOSING:
print("readyState : CLOSING");
iconData = Icons.signal_wifi_4_bar_lock;
break;
case WebSocket.CLOSED:
print("readyState : CLOSED");
iconData = Icons.warning;
break;
default:
print("readyState : " + Server.ws.readyState.toString());
break;
}
return new IconButton(
icon: new Icon(iconData),
tooltip: 'Connection Status', // TODO:Localize
onPressed: () {
setState(() {
Server.ws.close();
});
},
);
}
Additional info about the WebSocket :
/// Should be called when the IP is validated
void startSocket() {
try {
WebSocket.connect(Server.qr).then((socket) {
// Build WebSocket
Server.ws = socket;
Server.ws.listen(
handleData,
onError: handleError,
onDone: handleDone,
cancelOnError: true,
);
Server.ws.pingInterval = new Duration(
seconds: Globals.map["PingInterval"],
);
send(
"CONNECTION",
{
"deviceID": Globals.map["UUID"],
},
);
});
} catch (e) {
print("Error opening a WebSocket : $e");
}
}
/// Handles the closing of the connection.
void handleDone() {
print("WebSocket closed.");
new Timer(new Duration(seconds: Globals.map["PingInterval"]), startSocket);
}
/// Handles the WebSocket's errors.
void handleError(Error e) {
print("WebSocket error.");
print(e);
Server.ws.close();
}
I've gone ahead and taken a look at the source code for the WebSocket implementation. It appears that when the WebSocket is being closed with the status GOING_AWAY, the internal socket stream is being closed. However, it is possible that this event does not propagate to the transformed stream which handles the readyState of the instance. I would recommend filing a bug report at dartbug.com.
try setting the pingInterval, this checks for connection status every said interval, then the closeCode will update
Related
My custom v3 CAF receiver app is successfully playing the first few live & vod assets. After that, it gets into a state were media commands are being queued because "Load is in progress". It is still (successfully) fetching manifests, but MEDIA_STATUS remains "buffering". The log then shows:
[ 4.537s] [cast.receiver.MediaManager] Load is in progress, media command is being queued.
[ 5.893s] [cast.receiver.MediaManager] Buffering state changed, isPlayerBuffering: true old time: 0 current time: 0
[ 5.897s] [cast.receiver.MediaManager] Sending broadcast status message
CastContext Core event: {"type":"MEDIA_STATUS","mediaStatus":{"mediaSessionId":1,"playbackRate":1,"playerState":"BUFFERING","currentTime":0,"supportedMediaCommands":12303,"volume":{"level":1,"muted":false},"currentItemId":1,"repeatMode":"REPEAT_OFF","liveSeekableRange":{"start":0,"end":20.000999927520752,"isMovingWindow":true,"isLiveDone":false}}}
CastContext MEDIA_STATUS event: {"type":"MEDIA_STATUS","mediaStatus":{"mediaSessionId":1,"playbackRate":1,"playerState":"BUFFERING","currentTime":0,"supportedMediaCommands":12303,"volume":{"level":1,"muted":false},"currentItemId":1,"repeatMode":"REPEAT_OFF","liveSeekableRange":{"start":0,"end":20.000999927520752,"isMovingWindow":true,"isLiveDone":false}}}
Fetch finished loading: GET "(manifest url)".
No errors are shown.
Even after closing and restarting the cast session, the issue remains. The cast device itself has to be rebooted to resolve it. It looks like data is kept between sessions.
It could be important to note that the cast receiver app is not published yet. It is hosted on a local network.
My questions are:
What could be the cause of this stuck behavior?
Is there any session data kept between session?
How to fully reset the cast receiver app, without the necessity to restart the cast device.
The receiver app itself is very basic. Other than license wrapping it resembles the vanilla example app:
const { cast } = window;
const TAG = "CastContext";
class CastStore {
static instance = null;
error = observable.box();
framerate = observable.box();
static getInstance() {
if (!CastStore.instance) {
CastStore.instance = new CastStore();
}
return CastStore.instance;
}
get debugLog() {
return this.framerate.get();
}
get errorLog() {
return this.error.get();
}
init() {
const context = cast.framework.CastReceiverContext.getInstance();
const playerManager = context.getPlayerManager();
playerManager.addEventListener(
cast.framework.events.category.CORE,
event => {
console.log(TAG, "Core event: " + JSON.stringify(event));
}
);
playerManager.addEventListener(
cast.framework.events.EventType.MEDIA_STATUS,
event => {
console.log(TAG, "MEDIA_STATUS event: " + JSON.stringify(event));
}
);
playerManager.addEventListener(
cast.framework.events.EventType.BITRATE_CHANGED,
event => {
console.log(TAG, "BITRATE_CHANGED event: " + JSON.stringify(event));
runInAction(() => {
this.framerate.set(`bitrate: ${event.totalBitrate}`);
});
}
);
playerManager.addEventListener(
cast.framework.events.EventType.ERROR,
event => {
console.log(TAG, "ERROR event: " + JSON.stringify(event));
runInAction(() => {
this.error.set(`Error detailedErrorCode: ${event.detailedErrorCode}`);
});
}
);
// intercept the LOAD request to be able to read in a contentId and get data.
this.loadHandler = new LoadHandler();
playerManager.setMessageInterceptor(
cast.framework.messages.MessageType.LOAD,
loadRequestData => {
this.framerate.set(null);
this.error.set(null);
console.log(TAG, "LOAD message: " + JSON.stringify(loadRequestData));
if (!loadRequestData.media) {
const error = new cast.framework.messages.ErrorData(
cast.framework.messages.ErrorType.LOAD_CANCELLED
);
error.reason = cast.framework.messages.ErrorReason.INVALID_PARAM;
return error;
}
if (!loadRequestData.media.entity) {
// Copy the value from contentId for legacy reasons if needed
loadRequestData.media.entity = loadRequestData.media.contentId;
}
// notify loadMedia
this.loadHandler.onLoadMedia(loadRequestData, playerManager);
return loadRequestData;
}
);
const playbackConfig = new cast.framework.PlaybackConfig();
// intercept license requests & responses
playbackConfig.licenseRequestHandler = requestInfo => {
const challenge = requestInfo.content;
const { castToken } = this.loadHandler;
const wrappedRequest = DrmLicenseHelper.wrapLicenseRequest(
challenge,
castToken
);
requestInfo.content = wrappedRequest;
return requestInfo;
};
playbackConfig.licenseHandler = license => {
const unwrappedLicense = DrmLicenseHelper.unwrapLicenseResponse(license);
return unwrappedLicense;
};
// Duration of buffered media in seconds to start/resume playback after auto-paused due to buffering; default is 10.
playbackConfig.autoResumeDuration = 4;
// Minimum number of buffered segments to start/resume playback.
playbackConfig.initialBandwidth = 1200000;
context.start({
touchScreenOptimizedApp: true,
playbackConfig: playbackConfig,
supportedCommands: cast.framework.messages.Command.ALL_BASIC_MEDIA
});
}
}
The LoadHandler optionally adds a proxy (I'm using a cors-anywhere proxy to remove the origin header), and stores the castToken for licenseRequests:
class LoadHandler {
CORS_USE_PROXY = true;
CORS_PROXY = "http://192.168.0.127:8003";
castToken = null;
onLoadMedia(loadRequestData, playerManager) {
if (!loadRequestData) {
return;
}
const { media } = loadRequestData;
// disable cors for local testing
if (this.CORS_USE_PROXY) {
media.contentId = `${this.CORS_PROXY}/${media.contentId}`;
}
const { customData } = media;
if (customData) {
const { licenseUrl, castToken } = customData;
// install cast token
this.castToken = castToken;
// handle license URL
if (licenseUrl) {
const playbackConfig = playerManager.getPlaybackConfig();
playbackConfig.licenseUrl = licenseUrl;
const { contentType } = loadRequestData.media;
// Dash: "application/dash+xml"
playbackConfig.protectionSystem = cast.framework.ContentProtection.WIDEVINE;
// disable cors for local testing
if (this.CORS_USE_PROXY) {
playbackConfig.licenseUrl = `${this.CORS_PROXY}/${licenseUrl}`;
}
}
}
}
}
The DrmHelper wraps the license request to add the castToken and base64-encodes the whole. The license response is base64-decoded and unwrapped lateron:
export default class DrmLicenseHelper {
static wrapLicenseRequest(challenge, castToken) {
const wrapped = {};
wrapped.AuthToken = castToken;
wrapped.Payload = fromByteArray(new Uint8Array(challenge));
const wrappedJson = JSON.stringify(wrapped);
const wrappedLicenseRequest = fromByteArray(
new TextEncoder().encode(wrappedJson)
);
return wrappedLicenseRequest;
}
static unwrapLicenseResponse(license) {
try {
const responseString = String.fromCharCode.apply(String, license);
const responseJson = JSON.parse(responseString);
const rawLicenseBase64 = responseJson.license;
const decodedLicense = toByteArray(rawLicenseBase64);
return decodedLicense;
} catch (e) {
return license;
}
}
}
The handler for cast.framework.messages.MessageType.LOAD should always return:
the (possibly modified) loadRequestData, or
a promise for the (possibly modified) loadRequestData
null to discard the load request (I'm not 100% sure this works for load requests)
If you do not do this, the load request stays in the queue and any new request is queued after the initial one.
In your handler, you return an error if !loadRequestData.media, which will get you into that state. Another possibility is an exception in the load request handler, which will also get you in that state.
I guess we have a different approach and send everything possible through sendMessage, when we loading stuff we create a new cast.framework.messages.LoadRequestData() which we load with playerManager.load(loadRequest).
But I guess that you might be testing this on an integrated Chromecast, we see this problems as well!?
I suggest that you do one or more
Enable gzip compression on all responses!!!
Stop playback playerManager.stop() (maybe in the interseptor?)
Change how the licenseUrl is set
How we set licenseUrl
playerManager.setMediaPlaybackInfoHandler((loadRequestData, playbackConfig) => {
playbackConfig.licenseUrl = loadRequestData.customData.licenseUrl;
return playbackConfig;
}
);
I want to implement Play Install Referrer API and I found document and I read that but I have have some confusion. First I have implemented all code provided by google. But I want to know which type of url I need to create so that user can click on link and go to play store and install my app and then I get the referral detail..
I use this code:
InstallReferrerClient mReferrerClient;
mReferrerClient = newBuilder(this).build();
mReferrerClient.startConnection(this);
#Override
public void onInstallReferrerSetupFinished ( int responseCode){
switch (responseCode) {
case InstallReferrerClient.InstallReferrerResponse.OK:
// Connection established
/* ReferrerDetails response = null;
try {
response = mReferrerClient.getInstallReferrer();
response.getInstallReferrer();
response.getReferrerClickTimestampSeconds();
response.getInstallBeginTimestampSeconds();
} catch (RemoteException e) {
e.printStackTrace();
}*/
break;
case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
// API not available on the current Play Store app
break;
case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
// Connection could not be established
break;
}
}
#Override
public void onInstallReferrerServiceDisconnected () {
}
But which type of link I create so user go to play store and after install play store referral api give me data..
Sample url - "https://play.google.com/store/apps/details?id=com.dummy.app&referrer=referralCode%3D311566%26source%3DFacebook+App"
When using the Google Play Referrer API -
InstallReferrerClient mReferrerClient;
mReferrerClient = newBuilder(this).build();
mReferrerClient.startConnection(this);
#Override
public void onInstallReferrerSetupFinished ( int responseCode){
switch (responseCode) {
case InstallReferrerClient.InstallReferrerResponse.OK:
// Connection established
/* ReferrerDetails response = null;
try {
response = mReferrerClient.getInstallReferrer();
response.getInstallReferrer();
response.getReferrerClickTimestampSeconds();
response.getInstallBeginTimestampSeconds();
} catch (RemoteException e) {
e.printStackTrace();
}*/
// End the connection once you get the data
referrerClient.endConnection();
break;
case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
// API not available on the current Play Store app
break;
case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
// Connection could not be established
break;
}
}
#Override
public void onInstallReferrerServiceDisconnected () {
}
getInstallReferrer()
will return String 'referralCode=311566&source=Facebook App'
play install referral library i wants to describe this in simple wording, being a developer you wants to know about these elements how much time you app bundle take to install on the user devices from play store, and referral url , referral click time and many others elements , google make it easy for you know you have to use play install referral library for this purpose.
add this dependency
implementation 'com.android.installreferrer:installreferrer:1.1'
you can follow the guidelines from here:
play installer referral guidelines
declare this variable in any java activity
InstallReferrerClient referrerClient;
in on create method use this code below :
referrerClient = InstallReferrerClient.newBuilder(this).build();
referrerClient.startConnection(new InstallReferrerStateListener() {
#Override
public void onInstallReferrerSetupFinished(int responseCode) {
switch (responseCode) {
case InstallReferrerClient.InstallReferrerResponse.OK:
// Connection established.
break;
case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
// API not available on the current Play Store app.
break;
case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
// Connection couldn't be established.
break;
}
}
#Override
public void onInstallReferrerServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
inside onInstallReferrerSetupFinished stabled you can get these data easily,you code will be after that like this
ReferrerDetails response = null;
try {
response = referrerClient.getInstallReferrer();
} catch (RemoteException e) {
e.printStackTrace();
}
String referrerUrl = response.getInstallReferrer();
long referrerClickTime = response.getReferrerClickTimestampSeconds();
long appInstallTime = response.getInstallBeginTimestampSeconds();
boolean instantExperienceLaunched = response.getGooglePlayInstantParam();
whole code will be like this
referrerClient = InstallReferrerClient.newBuilder(this).build();
referrerClient.startConnection(new InstallReferrerStateListener() {
#Override
public void onInstallReferrerSetupFinished(int responseCode) {
switch (responseCode) {
case InstallReferrerClient.InstallReferrerResponse.OK:
// Connection established.
ReferrerDetails response = null;
try {
response = referrerClient.getInstallReferrer();
} catch (RemoteException e) {
e.printStackTrace();
}
String referrerUrl = response.getInstallReferrer();
long referrerClickTime = response.getReferrerClickTimestampSeconds();
long appInstallTime = response.getInstallBeginTimestampSeconds();
boolean instantExperienceLaunched = response.getGooglePlayInstantParam();
break;
case InstallReferrerClient.InstallReferrerResponse.FEATURE_NOT_SUPPORTED:
// API not available on the current Play Store app.
break;
case InstallReferrerClient.InstallReferrerResponse.SERVICE_UNAVAILABLE:
// Connection couldn't be established.
break;
}
}
#Override
public void onInstallReferrerServiceDisconnected() {
// Try to restart the connection on the next request to
// Google Play by calling the startConnection() method.
}
});
I am unable to use cancellation tokens to stop a TCP Listener. The first code extract is an example where I can successfully stop a test while loop in a method from another class. So I don't understand why I cant apply this similar logic to the TCP Listener Class. Spent many days reading convoluted answers on this topic and cannot find a suitable solution.
My software application requires that the TCP Listener must give the user the ability to stop it from the server end, not the client. If a user wants to re-configure the port number for this listener then they would currently have to shutdown the software in order for Windows to close the underlying socket, this is no good as would affect the other services running in my app.
This first extract of code is just an example where I am able to stop a while loop from running, this works OK but is not that relevant other than the faat I would expect this to work for my TCP Listener:
public void Cancel(CancellationToken cancelToken) // EXAMPLE WHICH IS WORKING
{
Task.Run(async () =>
{
while (!cancelToken.IsCancellationRequested)
{
await Task.Delay(500);
log.Info("Test Message!");
}
}, cancelToken);
}
Now below is the actual TCP Listener code I am struggling with
public void TcpServerIN(string inboundEncodingType, string inboundIpAddress, string inboundLocalPortNumber, CancellationToken cancelToken)
{
TcpListener listener = null;
Task.Run(() =>
{
while (!cancelToken.IsCancellationRequested)
{
try
{
IPAddress localAddr = IPAddress.Parse(inboundIpAddress);
int port = int.Parse(inboundLocalPortNumber);
listener = new TcpListener(localAddr, port);
// Start listening for client requests.
listener.Start();
log.Info("TcpListenerIN listener started");
// Buffer for reading data
Byte[] bytes = new Byte[1024];
String data = null;
// Enter the listening loop.
while (true)
{
// Perform a blocking call to accept client requests.
TcpClient client = listener.AcceptTcpClient();
// Once each client has connected, start a new task with included parameters.
var task = Task.Run(() =>
{
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
data = null;
int i;
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
// Select Encoding format set by string inboundEncodingType parameter.
if (inboundEncodingType == "UTF8") { data = Encoding.UTF8.GetString(bytes, 0, i); }
if (inboundEncodingType == "ASCII") { data = Encoding.ASCII.GetString(bytes, 0, i); }
// Use this if you want to echo each message directly back to TCP Client
//stream.Write(msg, 0, msg.Length);
// If any TCP Clients are connected then pass the appended string through
// the rules engine for processing, if not don't send.
if ((listConnectedClients != null) && (listConnectedClients.Any()))
{
// Pass the appended message string through the SSSCRulesEngine
SendMessageToAllClients(data);
}
}
// When the remote client disconnetcs, close/release the socket on the TCP Server.
client.Close();
});
}
}
catch (SocketException ex)
{
log.Error(ex);
}
finally
{
// If statement is required to prevent an en exception thrown caused by the user
// entering an invalid IP Address or Port number.
if (listener != null)
{
// Stop listening for new clients.
listener.Stop();
}
}
}
MessageBox.Show("CancellationRequested");
log.Info("TCP Server IN CancellationRequested");
}, cancelToken);
}
Interesting to see that no one had come back with any solutions, admittedly it took me a long while to figure out a solution. The key to stopping the TCP Listener when using a synchronous blocking mode like the example below is to register the Cancellation Token with the TCP Listener itself, as well the TCP Client that may have already been connected at the time the Cancellation Token was fired. (see comments that are marked as IMPORTANT)
The example code may differ slightly in your own environment and I have extracted some code bloat that is unique to my project, but you'll get the idea in what we're doing here. In my project this TCP Server is started as a background service using NET Core 5.0 IHosted Services. My code below was adapted from the notes on MS Docs: https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=net-5.0
The main difference between the MS Docs and my example below is I wanted to allow multiple TCP Clients to connect hence the reason why I start up a new inner Task each time a new TCP Client connects.
/// <summary>
/// </summary>
/// <param name="server"></param>
/// <param name="port"></param>
/// <param name="logger"></param>
/// <param name="cancelToken"></param>
public void TcpServerRun(
int pluginId,
string pluginName,
string encoding,
int bufferForReadingData,
string ipAddress,
int port,
bool logEvents,
IServiceScopeFactory _scopeFactory,
CancellationToken cancelToken)
{
IPAddress localAddrIN = IPAddress.Parse(ipAddress);
TcpListener listener = new TcpListener(localAddrIN, port);
Task.Run(() =>
{
// Dispose the DbContext instance when the task has completed. 'using' = dispose when finished...
using var scope = _scopeFactory.CreateScope();
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TcpServer>>();
try
{
listener.Start();
cancelToken.Register(listener.Stop); // THIS IS IMPORTANT!
string logData = "TCP Server with name [" + pluginName + "] started Succesfully";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Started", pluginName, logData, null, _scopeFactory);
while (!cancelToken.IsCancellationRequested)
{
TcpClient client = listener.AcceptTcpClient();
logData = "A TCP Client with IP Address [" + client.Client.RemoteEndPoint.ToString() + "] connected to the TCP Server with name: [" + pluginName + "]";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Connected", pluginName, logData, null, _scopeFactory);
// Once each client has connected, start a new task with included parameters.
var task = Task.Run(async () =>
{
// Get a stream object for reading and writing
NetworkStream stream = client.GetStream();
// Buffer for reading data
Byte[] bytes = new Byte[bufferForReadingData]; // Bytes variable
String data = null;
int i;
cancelToken.Register(client.Close); // THIS IS IMPORTANT!
// Checks CanRead to verify that the NetworkStream is readable.
if (stream.CanRead)
{
// Loop to receive all the data sent by the client.
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0 & !cancelToken.IsCancellationRequested)
{
data = Encoding.ASCII.GetString(bytes, 0, i);
logData = "TCP Server with name [" + pluginName + "] received data [" + data + "] from a TCP Client with IP Address [" + client.Client.RemoteEndPoint.ToString() + "]";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Receive", pluginName, logData, null, _scopeFactory);
}
// Shutdown and end connection
client.Close();
logData = "A TCP Client disconnected from the TCP Server with name: [" + pluginName + "]";
// Custom Logger - you would use your own logging method here...
WriteLogEvent("Information", "TCP Servers", "Disconnected", pluginName, logData, null, _scopeFactory);
}
}, cancelToken);
}
}
catch (SocketException ex)
{
// When the cancellation token is called, we will always encounter
// a socket exception for the listener.AcceptTcpClient(); blocking
// call in the while loop thread. We want to catch this particular exception
// and mark the exception as an accepted event without logging it as an error.
// A cancellation token is passed usually when the running thread is manually stopped
// by the user from the UI, or will occur when the IHosted service Stop Method
// is called during a system shutdown.
// For all other unexpected socket exceptions we provide en error log underneath
// in the else statement block.
if (ex.SocketErrorCode == SocketError.Interrupted)
{
string logData = "TCP Server with name [" + pluginName + "] was stopped due to a CancellationTokenSource cancellation. This event is triggered when the SMTP Server is manually stopped from the UI by the user or during a system shutdown.";
WriteLogEvent("Information", "TCP Servers", "Stopped", pluginName, logData, null, _scopeFactory);
}
else
{
string logData = "TCP Server with name [" + pluginName + "] encountered a socket exception error and exited the running thread.";
WriteLogEvent("Error", "TCP Servers", "Socket Exception", pluginName, logData, ex, _scopeFactory);
}
}
finally
{
// Call the Stop method to close the TcpListener.
// Closing the listener does not close any exisiting connections,
// simply stops listening for new connections, you are responsible
// closing the existing connections which we achieve by registering
// the cancel token with the listener.
listener.Stop();
}
});
}
Looking at this code, I'm wondering what triggers onNotificationGCM? It is triggered when the app is registered but when does it get triggered again, say, when when I want to push a message to the user? I have a chat app that I'd like to push a message when chats come in. So I understand that I register the device but then this code needs to run again, I assume, with the new event. I just need understand part flow and part code.
// handle GCM notifications for Android
$window.onNotificationGCM = function (event) {
switch (event.event) {
case 'registered':
if (event.regid.length > 0) {
// Your GCM push server needs to know the regID before it can push to this device
// here is where you might want to send it the regID for later use.
var device_token = event.regid;
RequestsService.register(device_token).then(function(response){
alert('registered!');
});
//send device reg id to server
}
break;
case 'message':
// if this flag is set, this notification happened while we were in the foreground.
// you might want to play a sound to get the user's attention, throw up a dialog, etc.
if (event.foreground) {
console.log('INLINE NOTIFICATION');
var my_media = new Media("/android_asset/www/" + event.soundname);
my_media.play();
} else {
if (event.coldstart) {
console.log('COLDSTART NOTIFICATION');
} else {
console.log('BACKGROUND NOTIFICATION');
}
}
navigator.notification.alert(event.payload.message);
console.log('MESSAGE -> MSG: ' + event.payload.message);
//Only works for GCM
console.log('MESSAGE -> MSGCNT: ' + event.payload.msgcnt);
//Only works on Amazon Fire OS
console.log('MESSAGE -> TIME: ' + event.payload.timeStamp);
break;
case 'error':
console.log('ERROR -> MSG:' + event.msg);
break;
default:
console.log('EVENT -> Unknown, an event was received and we do not know what it is');
break;
}
};
Have a look at this example:
case 'message':
/*
if (e.foreground) {
window.alert("Message recieved");
}
else {
if (e.coldstart) {
window.alert("Coldstart Notification");
} else {
window.alert("Background Notification");
}
}
window.alert("Notification message: " + e.payload.message
+ "\n\n Time: " + e.payload.conversation);
*/
var data = e.payload;
if (data.conversation){
window.history.replaceState(null, '', '#/chats/');
ProjectName.conversation.load(data.conversation);
}
if (data.product){
window.history.replaceState(null, '', '#/product/' + data.product);
}
break;
case 'error':
// window.alert("Notification error: " + e.msg);
break;
default:
// window.alert("Notification - Unknown event");
break;
}
The e.payload contains all data that you send to your application, including message. You can access your other variables in case 'message'
When I run my code I get:
Breaking on exception: String expected
What I am trying to do is connect to my server using a websocket. However, it seems that no matter if my server is online or not the client still crashes.
My code:
import 'dart:html';
WebSocket serverConn;
int connectionAttempts;
TextAreaElement inputField = querySelector("#inputField");
String key;
void submitMessage(Event e) {
if (serverConn.readyState == WebSocket.OPEN) {
querySelector("#chatLog").text = inputField.value;
inputField.value = "";
}
}
void recreateConnection(Event e) {
connectionAttempts++;
if (connectionAttempts <= 5) {
inputField.value = "Connection failed, reconnecting. Attempt" + connectionAttempts.toString() + "out of 5";
serverConn = new WebSocket("ws://127.0.0.1:8887");
serverConn.onClose.listen(recreateConnection);
serverConn.onError.listen(recreateConnection);
} else {
inputField.value = "Connections ran out, please refresh site";
}
}
void connected(Event e) {
serverConn.sendString(key);
if (serverConn.readyState == WebSocket.OPEN) {
inputField.value = "CONNECTED!";
inputField.readOnly = false;
}
}
void main() {
serverConn = new WebSocket("ws://127.0.0.1:8887");
serverConn.onClose.listen(recreateConnection);
serverConn.onError.listen(recreateConnection);
serverConn.onOpen.listen(connected);
//querySelector("#inputField").onInput.listen(submitMessage);
querySelector("#sendInput").onClick.listen(submitMessage);
}
My Dart Editor says nothing about where the problem comes from nor does it give any warning until run-time.
You need to initialize int connectionAttempts; with a valid value;
connectionAttempts++; fails with an exception on null.
You also need an onMessage handler to receive messages.
serverConn.onMessage.listen((MessageEvent e) {
recreateConnection should register an onOpen handler as well.
After serverConn = new WebSocket the listener registered in main() will not work
If you register a listener where only one single event is expected you can use first instead of listen
serverConn.onOpen.first.then(connected);
According to #JAre s comment.
Try to use a hardcoded string
querySelector("#chatLog").text = 'someValue';
to ensure this is not the culprit.