Android get an Error while connection to Socket - android

i'm trying to write simple application with nodejs and express.io after reading some express.io document and successful connection to http://chat.socket.io i'm find simple sample for create server side with nodejs and express.io, after run this below code in command line and opening http://localhost:3000 in browser i dont get any error, i can not find any good document about coding in http://chat.socket.io server, now i want to try send request from android client to server with samples, but i get connection error:
Error:
CONNECTION ERROR
server.js:
// Setup basic express server
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('../..')(server);
var port = process.env.PORT || 3000;
server.listen(port, function () {
console.log('Server listening at port %d', port);
});
// Routing
app.use(express.static(__dirname + '/public'));
// Chatroom
// usernames which are currently connected to the chat
var usernames = {};
var numUsers = 0;
io.on('connection', function (socket) {
var addedUser = false;
// when the client emits 'new message', this listens and executes
socket.on('new message', function (data) {
// we tell the client to execute 'new message'
socket.broadcast.emit('new message', {
username: socket.username,
message: data
});
});
// when the client emits 'add user', this listens and executes
socket.on('add user', function (username) {
// we store the username in the socket session for this client
socket.username = username;
// add the client's username to the global list
usernames[username] = username;
++numUsers;
addedUser = true;
socket.emit('login', {
numUsers: numUsers
});
// echo globally (all clients) that a person has connected
socket.broadcast.emit('user joined', {
username: socket.username,
numUsers: numUsers
});
});
// when the client emits 'typing', we broadcast it to others
socket.on('typing', function () {
socket.broadcast.emit('typing', {
username: socket.username
});
});
// when the client emits 'stop typing', we broadcast it to others
socket.on('stop typing', function () {
socket.broadcast.emit('stop typing', {
username: socket.username
});
});
// when the user disconnects.. perform this
socket.on('disconnect', function () {
// remove the username from global usernames list
if (addedUser) {
delete usernames[socket.username];
--numUsers;
// echo globally that this client has left
socket.broadcast.emit('user left', {
username: socket.username,
numUsers: numUsers
});
}
});
});
my android code:
private Socket mSocket;
{
try {
/* connection successful to http://chat.socket.io */
mSocket = IO.socket("http://localhost:3000");
} catch (URISyntaxException e) {
Log.e("Error URI", String.valueOf(e));
throw new RuntimeException(e);
}
}
public void onCreate(Bundle savedInstanceState) {
...
mSocket.on(Socket.EVENT_CONNECT_ERROR, onConnectError);
mSocket.on(Socket.EVENT_CONNECT_TIMEOUT, onConnectError);
mSocket.on("new message", onNewMessage);
mSocket.on("user joined", onUserJoined);
mSocket.on("user left", onUserLeft);
mSocket.on("typing", onTyping);
mSocket.on("stop typing", onStopTyping);
mSocket.connect();
...
Button signInButton = (Button) findViewById(R.id.sign_in_button);
signInButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
attemptLogin();
}
});
mSocket.on("login", onLogin);
}
private void attemptLogin() {
mUsernameView.setError(null);
String username = mUsernameView.getText().toString().trim();
if (TextUtils.isEmpty(username)) {
mUsernameView.setError(getString(R.string.error_field_required));
mUsernameView.requestFocus();
return;
}
mUsername = username;
mSocket.emit("add user", username);
}
Android Error:
E/AndroidRuntime﹕ FATAL EXCEPTION: EventThread
java.lang.IllegalArgumentException: delay < 0: -432345566375051264
at java.util.Timer.schedule(Timer.java:457)
at com.github.nkzawa.socketio.client.Manager.reconnect(Manager.java:497)
at com.github.nkzawa.socketio.client.Manager.access$2000(Manager.java:20)
at com.github.nkzawa.socketio.client.Manager$8$1$1.call(Manager.java:519)
at com.github.nkzawa.socketio.client.Manager$1$3.call(Manager.java:282)
at com.github.nkzawa.emitter.Emitter.emit(Emitter.java:117)
at com.github.nkzawa.engineio.client.Socket.onError(Socket.java:754)
at com.github.nkzawa.engineio.client.Socket.access$800(Socket.java:29)
at com.github.nkzawa.engineio.client.Socket$4.call(Socket.java:293)
at com.github.nkzawa.emitter.Emitter.emit(Emitter.java:117)
at com.github.nkzawa.engineio.client.Transport.onError(Transport.java:63)
at com.github.nkzawa.engineio.client.transports.PollingXHR.access$100(PollingXHR.java:19)
at com.github.nkzawa.engineio.client.transports.PollingXHR$6$1.run(PollingXHR.java:126)
at com.github.nkzawa.thread.EventThread$2.run(EventThread.java:75)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:838)

I would blame this:
mSocket = IO.socket("http://localhost:3000");
I assume you are not running your node.js server on your android, but probably on your PC. If so, when testing on your android, you are trying to connect back on port 3000 to your android itself - as localhost links to device itself.
If you are using the same local network on your server and android, you should check your PC's IP and put it instead of localhost. If your server has public IP, you may want to use it instead.
edit
1
In other words, according to your comment: your PC IP is 192.168.1.5. As this is an internal IP, your android have to be connected to same sub-network your PC is, just because youre able to occur your connectin error. Basing to that, i assume you need to type http://192.168.1.5/ in adress bar in your android, to visit page your PC is serving. Assuming that, one remains nonchanged - the script "my android code" is running on your android. So instead of localhost there is required a proper host: 192.168.1.5. Cant tell if your android is blocking 3000 port, but localhost is improper from androids' point of view, as long as you are not running your nodejs server on that device.
Also that change may not take affect ad-hoc, during browser cache on mobile devices.
2
Looking into your code, I assume you will also occur some problems with users using same username. Yeah, sounds strange, but users may want to open few tabs in browser, connected to same socket server. Once that, your usernames and numUsers variables will corrupt.
As long as app is single-intance dedicated (eg. player#game), I would use
usernames[username] = socket
to store sockets aside, being able to post cross-player related events avoiding iteration over all opened sockets.
Also for chat-purposes, you may want to allow users being connected on few browser tabs at once. Usually I'm storing all sockets just this way:
if (!users[user]) {
users[user] = {
sockets: [socket]
};
console.log(sprintf('[%s] [CONNECTED] User %s', Date(), user));
} else {
users[user].sockets.push(socket);
}
your may be different, prolly based on chat-channels etc. Pushing sockets aside listeners allowed me to run separate UDP server in same node script file. It was in purpose of being able to monit/block/alert single user through all opened tabs, event if their are spread over two different browsers.

Related

TcpClient.ConnectAsync and TcpClient.BeginConnect always returning true in Xamarin.Forms Android Application

I have made an application in my Xamarin.Forms project where I can connect my Android phone to my Computer using a TCP connection. I have found while using both TcpClient.ConnectAsync and TcpClient.BeginConnect, they both return that client.Connected is true even though the port isn't open. I have verified this because I tried random IPs and random ports and it still says connection was successful.
When using TcpClient.ConnectAsync, it doesn't return true unless I press the button that runs the code under Button_Clicked 2 times, but when using TcpClient.BeginConnect, client.Connected always returns true. I know for a fact that the client isn't connected because I have a detection system that kicks the user to the reconnect page when the connection is lost.
The code I have for my TCPClient in MainPage.xaml.cs:
TcpClient client = new TcpClient();
private async void Button_Clicked(object sender, EventArgs e)
{
await client.ConnectAsync(ipAddress.Text, Convert.ToInt32(Port.Text));
if (client.Connected)
{
await DisplayAlert("Connected", "The client has successfully connected", "OK");
}
else
{
await DisplayAlert("Connection Unsuccessful", "The client couldn't connect!", "OK");
}
}
I have also tried using TcpClient.BeginConnect from How to set the timeout for a TcpClient?:
TcpClient client = new TcpClient();
private async void Button_Clicked(object sender, EventArgs e)
{
var result = client.BeginConnect(ipAddress.Text, Convert.ToInt32(Port.Text), null, null);
var success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1));
if (success)
{
await DisplayAlert("Connected", "The client has successfully connected", "OK");
}
else
{
await DisplayAlert("Connection Unsuccessful", "The client couldn't connect!", "OK");
}
}
I tried looking up the issue and the only thing I found was: TcpClient.Connected returns true yet client is not connected, what can I use instead? but, this link is stating that the client.Connected bool remains true after disconnection, while my problem is that it says the client connects even even though the client never gets a true connection to the server.
The project is currently using .NET Standard 2.0
I have found out the reason it would return client.Connected is true is because running the same ConnectAsync/BeginConnect method twice while the client is still trying to connect and hasn't yet timed out will cause the client.Connected value to be true for some reason.
The only way to fix this it to wait for the timeout to complete, or if the timeout is too long, to dispose the client and create a new one.

Socket.io: Error: Callbacks are not supported when broadcasting at Socket.emit

I successed to send ack to android client from nodejs server but I don't succeed to do reverse. I have this error: Callbacks are not supported when broadcasting at Socket.emit
Serveur nodejs:
socket.broadcast.to(socketid).emit('message', data, callThis);
//this function is executed when client calls it
function callThis (dataFromClient){
console.log("Call back fired: " + dataFromClient);
}
client android:
socket.on("message", new Emitter.Listener() {
#Override
public void call(Object... args) {
Ack ack = (Ack) args[args.length - 1];
ack.call();
JSONObject data = (JSONObject) args[0];
.....
}
}
What can I do to resolve this problem?
Basically support the answer of #Xeoncross. When a connection came, just saved the socket into a map, like below
this.connections = new Map<string, SocketIO.Socket>()
this.server.on("connection", (socket: SocketIO.Socket) => {
this.connections.set(socket.id, socket)
})
Then use a loop to send all users individually
public broadcast(msg: string) {
for(const socket of this.connections.values()) {
socket.emit("block", msg, (confirm: string) => {
console.log("confirmation msg: ", confirm)
})
}
}
As the error says, "Callbacks are not supported when broadcasting". It doesn't look like you are broadcasting though, as you are trying to send to a single client. So assuming socket is an actual client socket instance you can change your code:
socket.broadcast.to(socketid).emit('message', data, callThis);
to just send to that one person
socket.emit('message', data, callThis);

Cannot receive specific user push notification using azure notification hub

I am trying to learn to use azure mobile app, but I am having serious problems in using the NotificationHub. I have an Imagine subscription to Azure. I creating an android mobile app with azure backend. I have created a notification hub associated to the azure mobile app on the azure portal.
To register the app on the notification hub I used the code in this tutorial:
https://learn.microsoft.com/en-gb/azure/notification-hubs/notification-hubs-android-push-notification-google-fcm-get-started
The users are authenticated on the azure backend previuosly by using their google account, microsoft account or facebook account. New users are inserted into the table Users by the following node js code written for the table script Users.js. I want a push notification to Welcome the new User.
var azureMobileApps = require('azure-mobile-apps');
var logger = require('azure-mobile-apps/src/logger');
var table = azureMobileApps.table();
table.access = 'authenticated';
/**
* Adds the email address from the claims to the context item - used for
* insert operations
* #param {Context} context the operation context
* #returns {Promise} context execution Promise
*/
function addEmailToContext(context) {
/*
* Getting claim fields
*/
return context.user.getIdentity().then((data) => {
if( data.microsoftaccount != undefined){
context.item.email = data.microsoftaccount.claims.emailaddress;
context.item.name = data.microsoftaccount.claims.givenname;
context.item.surname = data.microsoftaccount.claims.surname;
}
if( data.google != undefined){
context.item.email = data.google.claims.emailaddress;
context.item.name = data.google.claims.givenname;
context.item.surname = data.google.claims.surname;
context.item.picture_url = data.google.claims.picture;
}
if( data.facebook != undefined){
context.item.email = data.facebook.claims.emailaddress;
context.item.name = data.facebook.claims.givenname;
context.item.surname = data.facebook.claims.surname;
}
logger.info('[tables/Users.js] --> NEW USER REGISTERED:'
+'\n\t Name:'+context.item.name
+'\n\t Surname:'+context.item.surname
+'\n\t Email:'+context.item.email);
// Execute the insert. The insert returns the results as a Promise,
// Do the push as a post-execute action within the promise flow.
return context.execute()
.then(function (results) {
// Only do the push if configured
if (context.push) {
// Mobile Apps adds a user tag when registering for push notifications
// Define the GCM payload.
var payload = {
"data": {
"message": 'Welcome '+context.item.username
}
};
context.push.gcm.send(context.user.id, payload, function (error) {
if (error) {
logger.error('Error while sending push notification: ', error);
} else {
logger.info('Push notification sent successfully!');
}
});
}
// Don't forget to return the results from the context.execute()
return results;
})
.catch(function (error) {
logger.error('Error while running context.execute: ', error);
});
});
}
// CREATE - add or overwrite the authenticated user
table.insert(addEmailToContext);
module.exports = table;
According to "How to: Send push notifications to an authenticated user using tags" in the tutorial on How to use the Azure Mobile Apps Node.js SDK
"When an authenticated user registers for push notifications, a user ID tag is automatically added to the registration. "
So in the Users.js, as suggested in this tutorial I wrote the following code to send the push notification to the user.
context.push.gcm.send(context.user.id, payload, function (error) {
if (error) {
logger.error('Error while sending push notification: ', error);
} else {
logger.info('Push notification sent successfully!');
}
});
With this code the push notification results to be sent successfully, but the device doesn't receive any notifications. If I use null instead of context.user.id then all devices receive the push notification correctly:
context.push.gcm.send(null, payload, function (error) {
if (error) {
logger.error('Error while sending push notification: ', error);
} else {
logger.info('Push notification sent successfully!');
}
});
I also tried to invoke the following custom API to create tag when the user is registered to the hub. The invoked API is the following:
var logger = require('azure-mobile-apps/src/logger');
exports.post = function(req, res) {
logger.info('[api/registerTag.js] --> Invoked');
// Get the notification hub used by the mobile app.
var push = req.azureMobile.push,
installationId = req.get('X-ZUMO-INSTALLATION-ID'),
tags = req.body.tag.toString();
// Define an update tags operation.
var updateOperation = [{
"op": "add",
"path": "/tags",
"value": tags
}];
// Update the installation to add the new tags.
push.patchInstallation(installationId, updateOperation, function(error) {
if(error){
logger.error('[api/registerTag.js] --> An error occurred while adding'
+'the following tags: \n\t'+tags, error);
res.status(error.statusCode).send(error.detail);
} else {
logger.info('[api/registerTag.js] --> The following tags have been added'
+'to the Notification Hub: \n\t'+tags, error);
res.status(200).send(tags);
}
});
};
On the console it is printed that the tag has been added successfully. But if I then modify the Users.js code like this:
...
// Only do the push if configured
if (context.push) {
// Mobile Apps adds a user tag when registering for push notifications
var userTag = '_UserId:' + context.user.id;
logger.info("TAG "+userTag);
// Define the GCM payload.
var payload = {
"data": {
"message": 'Welcome '+context.item.username
}
};
context.push.gcm.send(userTag, payload, function (error) {
if (error) {
logger.error('Error while sending push notification: ', error);
} else {
logger.info('Push notification sent successfully!');
}
});
}
...
again nothing is received. I have also tried whitelisting tags or adding them automatically using the Push section of the mobile app like shown in the image:
IMAGE LINK: i.stack.imgur.com/KBvQI.png
But the problem is still there. Hope someone can help me. Thanks.
After several times of testing, I succeeded in reproducing your issue and got the same problem. To achieve your requirement I did some modification in Android client-end:
1, Cache authentication user in the MainActivity class. Following is my code snippet. For more details you can refer here.
public static final String SHAREDPREFFILE = "temp";
public static final String USERIDPREF = "uid";
public static final String TOKENPREF = "tkn";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
// Create the Mobile Service Client instance, using the provided Mobile Service URL and key
mClient = new MobileServiceClient(
"https://yourwebsitename.azurewebsites.net",
this).withFilter(new ProgressFilter());
// Extend timeout from default of 10s to 20s
mClient.setAndroidHttpClientFactory(new OkHttpClientFactory() {
#Override
public OkHttpClient createOkHttpClient() {
OkHttpClient client = new OkHttpClient();
client.setReadTimeout(20, TimeUnit.SECONDS);
client.setWriteTimeout(20, TimeUnit.SECONDS);
return client;
}
});
authenticate();
} catch (MalformedURLException e) {
createAndShowDialog(new Exception("There was an error creating the Mobile Service. Verify the URL"), "Error");
} catch (Exception e){
createAndShowDialog(e, "Error");
}
}
private void authenticate() {
// We first try to load a token cache if one exists.
if (loadUserTokenCache(mClient)) {
createTable();
register();
}
// If we failed to load a token cache, login and create a token cache
else {
// Login using the Google provider.
ListenableFuture<MobileServiceUser> mLogin = mClient.login(MobileServiceAuthenticationProvider.Google);
Futures.addCallback(mLogin, new FutureCallback<MobileServiceUser>() {
#Override
public void onFailure(Throwable exc) {
createAndShowDialog("You must log in. Login Required", "Error");
}
#Override
public void onSuccess(MobileServiceUser user) {
createAndShowDialog(String.format("You are now logged in - %1$2s", user.getUserId()), "Success");
cacheUserToken(mClient.getCurrentUser());
createTable();
register();
}
});
}
}
private void cacheUserToken(MobileServiceUser user) {
SharedPreferences prefs = getSharedPreferences(SHAREDPREFFILE, Context.MODE_PRIVATE);
Editor editor = prefs.edit();
editor.putString(USERIDPREF, user.getUserId());
editor.putString(TOKENPREF, user.getAuthenticationToken());
editor.commit();
}
private void register() {
NotificationsManager.handleNotifications(this, NotificationSettings.SenderId, MyHandler.class);
registerWithNotificationHubs();
}
2, In RegistrationIntentService class replace regID = hub.register(FCM_token).getRegistrationId(); with the following code:
regID = hub.register(FCM_token, prefs.getString("uid", "")).getRegistrationId();
3, Make sure add the line below to the first line within onHandleIntent method.
SharedPreferences prefs = getSharedPreferences("temp", Context.MODE_PRIVATE);

Stripe android app connect to Node.js server

I'm working a project to integrate Stripe's payment service to my android app. I have the basic client code setup.
Card card = new Card("4242424242424242", 12, 2016, "123");
boolean validate = card.validateCard();
if (validate) {
try {
new Stripe(TEST_PUBLUSHABLE_KEY).createToken(card, new TokenCallback() {
#Override
public void onError(Exception e) {
System.out.println("ERROR");
}
#Override
public void onSuccess(Token token) {
System.out.println("SUCCESS");
}
});
} catch (AuthenticationException e) {
e.printStackTrace();
}
}
Now I need to setup a server, which I plan on using Node.js and Express. I followed their sample code on: https://stripe.com/docs/tutorials/charges
var express = require('express');
var bodyParser = require('body-parser');
var stripe = require('stripe')('sk_test_sGyqMsiFmf45xoZrDCy5ItcU'); // Test Secret Key
var app = express();
app.use(bodyParser());
app.post('/charge', function(req, res) {
var stripeToken = request.body.stripeToken;
var charge = stripe.charges.create({
amount: 1000, // amount in cents, again
currency: "cad",
card: stripeToken,
description: "payinguser#example.com"
},
function(err, charge) {
if (err && err.type === 'StripeCardError') {
console.log("The card has been declined");
}
});
});
app.use(express.static(__dirname + '/public'));
app.listen(3000);
I have never worked with servers, so I think I'm having trouble communicating between the android app and the server that is on my computer's localhost:3000.
According to Stripe's documentation, I need to have my server accept a HTTP POST call for the token, but I'm not quite sure how to do that.
Really appreciate your help.
Update #1:
Use Ultrahook to forward Stripe's POST to my localhost.
I use Node.js to setup my server, which receives all Stripe's requests and then get the information I need from the request body.
Still having trouble getting the onSuccess callback on Android to work, it always go to the onError callback.
Update #2:
Solved the onError callback error by printing the error message to console.
Permission denied (missing INTERNET permission?
Turns out I need to include this line to the AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Link to the Stack Overflow post that solved this part of my problem:
What permission do I need to access Internet from an android application?

How to attach POST variables to socket.io connection?

I have a webchat application which is running on Node.js and Socket.io. After user logins on main homepage port 80, he gets redirected to port 3000 (chat application) along with POST data containing his ID, username etc... On chat application page it validates details and registers user as new client.
However, now I am building android chat application and I get immediately disconnected from chat because basically android client fails validation since there is no POST data attached.
How can I add POST data along with connection request?
Here is a code that does it all in android:
try {
IO.Options opts = new IO.Options();
opts.forceNew = true;
final Socket socket = IO.socket("http://host:3000", opts);
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener(){
#Override
public void call(Object... args){
socket.emit("mobile_ping", sf.getTextValue(MainActivity.this, R.id.editText));
}
});
socket.connect();
} catch (Exception e) {
Log.d("ERR", String.valueOf(e));
}

Categories

Resources