I'm trying to send data between an Android application created with Cordova and an application running on a Windows machine; the application on the Windows machine requires TCP protocol. I've worked with Cordova before, but I haven't used many plugins for Cordova--and therefore I've had difficulty setting up plugins--and socket programming isn't familiar ground for me.
Anywho, I read that I could use chrome.socket with Cordova somewhere, so I've tried doing that, but I'm only receiving errors indicating that "chrome" is undefined.
I first installed the plugin via command line: cordova plugin add org.chromium.socket
It then appeared in the plugins directory of my Cordova application.
Then, I included the following code in my application:
var dataString = "Message to send";
var data = new Uint8Array(dataString.length);
for (var i = 0; i < data.length; i++) {
data[i] = dataString.charCodeAt(i);
}
try {
chrome.socket.create("tcp", function(createInfo) {
var socketId = createInfo.socketId;
try {
chrome.socket.connect(socketId, hostname, port, function(result) {
if (result === 0) {
chrome.socket.write(socketId, data, function(writeInfo) {
chrome.socket.read(socketId, 1000, function(readInfo) {
});
});
}
else
{
alert("CONNECT FAILED");
}
});
}
catch (err)
{
alert("ERROR! " + err);
}
});
}
catch (err)
{
alert("ERROR! " + err);
}
I get the error alert every time, because chrome.socket is undefined; this makes me think that I did not set this up correctly. At any rate, any advice would be greatly appreciated.
Related
Is it possible to perform a lookup of a Bluetooth device given its address in a Nodejs script?
There are a few packages out there, the main one being Noble. However, they all focus around scanning, and not looking up a known address (as far as i can tell anyway!).
What i want to achieve, is to look up a known address, to see if the device can be found.
Much like PyBluez does for Python:
bluetooth.lookup_name('CC:20:E8:8F:3A:1D', timeout=5)
In Python, this can find the device even if it is undiscoverable, unlike a typical inquiry scan would.
I had this same problem and just found the btwatch lib, but it isn't working for me on the latest raspbian. But the source is just calling l2ping and looking for a string that I'm guessing no longer prints on success, so the modified code below works instead, similar to the lookup_name method, once you have l2ping installed (I think npm bluetooth or pybluez has it)
var Spawn = require('child_process').spawn;
function detectMacAddress(macAddress, callback)
{
//var macAddress = '72:44:56:05:79:A0';
var ls = Spawn('l2ping', ['-c', '1', '-t', '5', macAddress]);
ls.stdout.on('data', function (data) {
console.log("Found device in Range! " + macAddress);
callback(true);
});
ls.on('close', function () {
console.log("Could not find: " + macAddress);
callback(false);
});
}
Or, a synchronous way,
var execSync = require('child_process').execSync;
function detectMacAddressSync(macAddress)
{
var cmd = 'l2ping -c 1 -t 5 ' + macAddress;
try
{
var output = execSync(cmd );
console.log("output : "+ output );
return true;
}
catch(e)
{
console.log("caught: " + e);
return false;
}
}
As far as I have understood the problem you want to connect to the device using address. Then, I would suggest using node-bluetooth-serial-port.
var btSerial = new (require('bluetooth-serialport')).BluetoothSerialPort();
btSerial.on('found', function(address, name) {
btSerial.findSerialPortChannel(address, function(channel) {
btSerial.connect(address, channel, function() {
console.log('connected');
btSerial.write(new Buffer('my data', 'utf-8'), function(err, bytesWritten) {
if (err) console.log(err);
});
btSerial.on('data', function(buffer) {
console.log(buffer.toString('utf-8'));
});
}, function () {
console.log('cannot connect');
});
// close the connection when you're ready
btSerial.close();
}, function() {
console.log('found nothing');
});
});
BluetoothSerialPort.findSerialPortChannel(address, callback[, errorCallback])
Checks if a device has a serial port service running and if it is found it passes the channel id to use for the RFCOMM connection.
callback(channel) - called when finished looking for a serial port on the device.
errorCallback - called the search finished but no serial port channel was found on the device. Connects to a remote bluetooth device.
bluetoothAddress - the address of the remote Bluetooth device.
channel - the channel to connect to.
[successCallback] - called when a connection has been established.
[errorCallback(err)] - called when the connection attempt results in an error. The parameter is an Error object.
I am trying to receive push messages from GCM. I simply followed the tutorial and registered the service worker as below:-
function urlB64ToUint8Array(base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, '+')
.replace(/_/g, '/');
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
var subscribeForPush = function() {
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
serviceWorkerRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey : urlB64ToUint8Array('<API-KEY>')
})
.then(function(subscription) {
console.log("Subscription for Push successful: ", subscription);
})
.catch(function(error) {
console.log("Subscription for Push failed", error);
});
});
};
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
subscribeForPush();
}).catch(function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
Now, I get the device Id ("cw4jK8NhJgY:APA91bHr64E_kSdh7kN_VjcZRKulPf8KPLJLBjtnHI2qkYzx3-I9aUhunjzVcJjLtkHl9zvN8ys80gADK8tV8SueLX1R2jS0xgrf1Ur6cDw3jNjloUJp8PtWaIN-cEKXj69TZ9-D2Hiw")
from url:- "https://android.googleapis.com/gcm/send/cw4jK8NhJgY:APA91bHr64E_kSdh7kN_VjcZRKulPf8KPLJLBjtnHI2qkYzx3-I9aUhunjzVcJjLtkHl9zvN8ys80gADK8tV8SueLX1R2jS0xgrf1Ur6cDw3jNjloUJp8PtWaIN-cEKXj69TZ9-D2Hiw"
In my service worker file, I simply listen to push messages like:-
self.addEventListener('push', function(event) {
console.log(event)
});
I am able to send messages to gcm using terminal and get the message:-
{"multicast_id":4983340130324592129,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1483043226210433%cc9b4facf9fd7ecd"}]}
But I dont know why I dont see logs in push listener? I am stuck in this issue for more than 2 days. Can anyone please help me here? I am doing this in localhost.
You are not returning the pushManager subscribe
navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
return serviceWorkerRegistration.pushManager.subscribe({
'userVisibleOnly': true
}).then(function(subscription) {
if (!subscription) {
// Set appropriate app states.
return;
}
console.log(subscription.endpoint);
}).catch(function (err) {
console.log('error in subcription .. '+ err);
});
})
Check my github project https://github.com/bvakiti/bvakiti.github.io/blob/master/app.js
Let me know if you need any details
I would like to add to bvakiti's solution by declaring
applicationServerKey: convertedVapidKey
in the pushManager subscribe. Most of the tutorials I found in the internet just uses bvakiti's method and most of the devs who follow that tutorial encountered issues in receiving the push notifications.
I was facing the same issue. I updated my Google Chrome version and it fixed the issue.
If you are sure your service worker is registered in the browser but your 'push' event is not getting triggered, you can debug all incoming push notifications to Google Chrome by doing the following:
Open chrome://gcm-internals
Start recording
Send a test push notification to the browser
Observe the logs
The server was getting a 201 on the push request and the browser threw no errors in the console log. However, I was having a decryption issue. The browser was receiving the push notification but failed to decrypt it: AES-GCM decryption failed.
Helpful Source: https://developers.google.com/web/fundamentals/push-notifications/common-issues-and-reporting-bugs
So I want to fetch a large amount of data on Parse, a good solution I found is to make a recursive function: when the data are successfully found, launch another request. The way I'm doing it is pretty simple:
var containedLimit = 1000, // Also tried with 500, 300, and 100
parseUsersWaiting = {
// A lot of Users
},
parseUsers = {}, // Recipt for Users
getPlayers = function (containedIn) {
var count = 0;
if (containedIn == undefined) {
containedIn = [];
for (var i in parseUsersWaiting) {
count++;
if (count > containedLimit) {
break;
}
containedIn.push(parseUsersWaiting[i].id);
delete parseUsersWaiting[i];
}
}
var UserObject = Parse.Object.extend('User'),
UserQuery = new Parse.Query(UserObject);
UserQuery.limit(containedLimit);
UserQuery.containedIn('objectId', containedIn);
UserQuery.find({
success: function (results) {
if (results) {
for (var i in results) {
if (parseUsers[results[i].id] == undefined) {
parseUsers[results[i].id] = results[i];
}
// Other stuff
if (results.length < containedLimit) {
// End of process
} else {
getPlayers();
}
}
} else {
// Looks like an end of process too
}
}, error: function (error) {
console.log(error.message);
getPlayers(containedIn);
}
});
};
Now, here is what I would like to call the "issue": it happen, very frequently, that the "error" callback is launched with this:
Received an error with invalid JSON from Parse: Error: getaddrinfo ENOTFOUND api.parse.com
at errnoException (dns.js:44:10)
at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:94:26)
(With code 107, of course) So I searched on the Parse Documentation, and it says the exact same thing: "Received an error with invalid JSON". Yeah.
I'm using the Parse SDK provided by Parse.com (npm install parse)
I also tried to modify a bit the Parse code with replacing the host key by 'hostname' on line 361 of the file parse/node_modules/xmlhttprequest/lib/XMLHttpRequest.js (Parse package version: 1.5.0), and it didn't worked, so I removed my changes.
(By the way, I found a solution talking about using ulimit to change memory usage limit that could solve the problem, but actually, I haven't the necessary rights to execute this command)
This error occurs when it can not connect to the API (technically when it cannot lookup the IP address of the API server). Perhaps your internet connection was lost for a brief moment or their SDK server were unavailable (or maybe the server is denying your request due to rate limits).
Either way it is good to code some resilience into your application. I see your on error function retries the API call, but perhaps it would be worth adding a timeout before you do that to give the problem a chance to recover?
error: function (error) {
console.log(error.message);
setTimeout(getPlayers, 10000, containedIn);
}
If the sever is rate limiting your requests, this will also help.
While trying to build a web application using the Sinch Instant Messaging SDK I ran into an issue of not being able receive instant messages using the latest Javascript Instant Messaging SDK found here. I have also been following along this tutorial to help build my app that I think uses a different version of the SDK where instant messages can be received. However, the SDK version in the tutorial does not let me use generated userTickets for authentication for my application, while the latest SDK version does.
So, I was wondering if there was a way to either use generated userTickets for the SDK found in the tutorial or receive instant messages using the latest SDK.
On the latest SDK I have tried setting supportActiveConnection to true during configuration in order to receive messages using the code in the tutorial with no success. Here are some of the relevant code snippets from the tutorial to receive messages:
sinchClient = new SinchClient({
applicationKey: 'APP_KEY',
capabilities: {
messaging: true
},
supportActiveConnection: true,
});
var loginObject = {
username: username,
password: password
};
sinchClient.start(loginObject, function() {
global_username = username;
showPickRecipient();
}).fail(handleError);
var messageClient = sinchClient.getMessageClient();
var eventListener = {
onIncomingMessage: function(message) {
if (message.senderId == global_username) {
$('div#chatArea').append('<div>' + message.textBody + '</div>');
} else {
$('div#chatArea').append('<div style="color:red;">' + message.textBody + '</div>');
}
}
}
messageClient.addEventListener(eventListener);
The authentication ticket is generated by a python back-end through the following function and handler:
def getAuthTicket(username):
userTicket = {
'identity': {'type': 'username', 'endpoint': username},
'expiresIn': 3600,
'applicationKey': APPLICATION_KEY,
'created': datetime.utcnow().isoformat()
}
userTicketJson = json.dumps(userTicket).replace(" ", "")
userTicketBase64 = base64.b64encode(userTicketJson)
# TicketSignature = Base64 ( HMAC-SHA256 ( ApplicationSecret, UTF8 ( UserTicketJson ) ) )
digest = hmac.new(base64.b64decode(
APPLICATION_SECRET), msg=userTicketJson, digestmod=hashlib.sha256).digest()
signature = base64.b64encode(digest)
# UserTicket = TicketData + ":" + TicketSignature
signedUserTicket = userTicketBase64 + ':' + signature
return {"userTicket": signedUserTicket}
class TicketHandler(BaseHandler):
def get(self):
self.response.write(getAuthTicket(self.username))
Then on the client side I call a get request on the ticket handler.
$.get('/ticket', function(authTicket) {
sinchClient.start(eval("(" + authTicket + ")"))
.then(function() {
console.log("success");
})
.fail(function(error) {
console.log("fail");
});
});
The error I get when I try to start the start the Sinch client using the sinch.min.js file found in the tutorial is a "no valid identity or authentication ticket".
In the standard specs it says you can set ENUM value to "relay" : http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCIceServer
but, How do you set the enum to "relay" using Javascript of following?
if (tmp.indexOf("typ relay ") >= 0) { never occur in my test. how can i force it to enum "relay"?
or
is it a BUG? https://code.google.com/p/webrtc/issues/detail?id=1179
Any idea.
function createPeerConnection() {
try {
// Create an RTCPeerConnection via the polyfill (adapter.js).
pc = new RTCPeerConnection(pcConfig, pcConstraints);
pc.onicecandidate = onIceCandidate;
console.log('Created RTCPeerConnnection with:\n' +
' config: \'' + JSON.stringify(pcConfig) + '\';\n' +
' constraints: \'' + JSON.stringify(pcConstraints) + '\'.');
} catch (e) {
console.log('Failed to create PeerConnection, exception: ' + e.message);
alert('Cannot create RTCPeerConnection object; \
WebRTC is not supported by this browser.');
return;
}
pc.onaddstream = onRemoteStreamAdded;
pc.onremovestream = onRemoteStreamRemoved;
pc.onsignalingstatechange = onSignalingStateChanged;
pc.oniceconnectionstatechange = onIceConnectionStateChanged;
}
function onIceCandidate(event) {
if (event.candidate) {
var tmp = event.candidate.candidate;
if (tmp.indexOf("typ relay ") >= 0) {
/////////////////////////////////////////// NEVER happens
sendMessage({type: 'candidate',
label: event.candidate.sdpMLineIndex,
id: event.candidate.sdpMid,
candidate: tmp});
noteIceCandidate("Local", iceCandidateType(tmp));
}
} else {
console.log('End of candidates.');
}
}
There is a Chrome extension, WebRTC Network Limiter, that Configures how WebRTC's network traffic is routed by changing Chrome's privacy settings. You can force traffic to go through TURN without having to do any SDP mangling.
EDIT 1
The point of making this available in extensions, is for users that are worried about their security. You can check this excellent post about WebRTC security. The same as this extension does, can also be done in FF, by changing the flag media.peerconnection.ice.relay_only. This can be found in about:config. Don't know about the other two, but I'd wager they do have something similar.
On the other hand, if you are distributing an app and want all your clients to go through TURN, you won't have access to their browsers, and can't expect them to change those flags or install any extension. In that case, you'll have to mangle your SDP. You can use this code
function onIceCandidate(event) {
if (event.candidate) {
var type = event.candidate.candidate.split(" ")[7];
if (type != "relay") {
trace("ICE - Created " + type + " candidate ignored: TURN only mode.");
return;
} else {
sendCandidateMessage(candidate);
trace("ICE - Sending " + type + " candidate.");
}
} else {
trace("ICE - End of candidate generation.");
}
}
That code is taken from the discuss-webrtc list.
If you never get those candidates, it might very well be that your TURN server is not correctly configured. You can check your TURN server in this page. Don't forget to remove the existing STUN server configured in that demo.
EDIT 2
Even simpler: set iceTransportPolicy: "relay" in your WebRtcPeer config to force TURN:
var options = {
localVideo: videoInput, //if you want to see what you are sharing
onicecandidate: onIceCandidate,
mediaConstraints: constraints,
sendSource: 'screen',
iceTransportPolicy: 'relay',
iceServers: [{ urls: 'turn:XX.XX.XX.XX', username:'user', credential:'pass' }]
}
webRtcPeerScreencast = kurentoUtils.WebRtcPeer.WebRtcPeerSendrecv(options, function(error) {
if (error) return onError(error) //use whatever you use for handling errors
this.generateOffer(onOffer)
});
This is working for me:
iceServers = [
{ "url": "turn:111.111.111.111:1234?transport=tcp",
"username": "xxxx",
"credential": "xxxxx"
}
]
optionsForWebRtc = {
remoteVideo : localVideoElement,
mediaConstraints : videoParams,
onicecandidate : onLocalIceCandidate,
configuration: {
iceServers: iceServers,
iceTransportPolicy: 'relay'
}
}
To force the usage of a TURN server, you need to intercept the candidates found by the browser. Just like you are doing.
But if it never occurs in your testings, you may have some problems with your TURN server.
If you have a working TURN server, you'll get the candidates with typ relay.
Remember that you need to configure TURN server with authentication. It is mandatory and the browser will only use the candidates "relay" if the request is authenticated. Your iceServers config for PeerConnection must have the credentials defined for TURN servers.