I was using:
https://www.npmjs.com/package/ngx-socket-io
in my ionic project, and I needed to make sure that the connection through mobile devices would also work correctly, however, when I run the nodejs socket server and connect a client to the server, on the laptop it works correctly, that is, I can have as many clients connected as required and it works correctly, but when I try to make this same socket server work but through a mobile device such as an android (the real device, none emulated), and I try to connect to the socket server, I don't get any error message on the server side, but it doesn't connect me either, on the client side it doesn't give me any error either when I inspect it connected through:
chrome://inspect
This would be the socket server created in nodejs:
const app = require('express')();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
let usersConnections = [];
io.on('connection', (socket)=>{
console.log("connection received!");
socket.on('disconnect', ()=>{
for(let i = 0; i < usersConnections.length; i++){
if(
usersConnections[i]["ID"] == socket.id &&
usersConnections[i]["username"] == socket.nickname &&
usersConnections[i]["type_user"] == socket.type_user
){
usersConnections.splice(i, 1);
io.emit('users-changed', {
user: socket.nickname,
index: i - 1,
type_user: socket.type_user,
event: 'left'
});
console.log(usersConnections);
console.log("\n\n");
}
}
});
socket.on('set-nickname', (data)=>{
socket.nickname = data.nickname;
socket.type_user = data.type_user;
socket.location = data.location;
usersConnections.push(
{ID: socket.id, username: data.nickname, type_user: data.type_user, location: data.location}
);
socket.broadcast.emit(
'users-changed',
{
name: data.nickname,
src: data.src,
description: data.description,
isChecked: false,
location: data.location,
event: 'joined'
}
);
console.log(usersConnections);
});
socket.on('send-invitation', (data)=>{
for(let i = 0; i < usersConnections.length; i++){
if(
usersConnections[i].username == data.username &&
usersConnections[i].type_user == data.type_user){
console.log("yes!");
io.to(usersConnections[i].ID).emit('subject',
{from: {nick: socket.nickname, typeU: socket.type_user, location: socket.location}, sended: new Date()}
);
break;
}
}
});
socket.on('confirm-invitation', (isConfirmed)=>{
io.emit('newRoom', {confirmed: isConfirmed,
from: {nick: socket.nickname, typeU: socket.type_user}}
);
});
socket.on('add-message', (message)=>{
io.emit('message', {text: message.text, from: socket.nickname, created: new Date()});
});
});
const PORT = process.env.PORT || 3001;
http.listen(PORT, ()=>{
console.log(`Socket server listening in: http://localhost:${PORT}`);
});
in the app.module.ts I have:
import {NgPipesModule} from 'ngx-pipes';
import { SocketIoModule, SocketIoConfig } from 'ngx-socket-io';
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';
import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
const config: SocketIoConfig = {url: `${GLOBAL_SERVER}:3001`, options: {}};
@NgModule({
declarations: [AppComponent, SelectUsersModalPage],
entryComponents: [SelectUsersModalPage],
imports: [
BrowserModule,
HttpModule,
HttpClientModule,
FormsModule,
NgPipesModule,
SocketIoModule.forRoot(config),
IonicStorageModule.forRoot(),
IonicModule.forRoot(),
AppRoutingModule
],
providers: [
StatusBar,
SplashScreen,
PostProvider,
Camera,
Geolocation,
GoogleMaps,
LocalNotifications,
{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
],
bootstrap: [AppComponent]
})
export class AppModule {}
and in order not to extend the code too much, the way in which I connect to the socket server in the page where I use the socket is this:
select-users-modal.page.ts:
import { Component, OnInit } from '@angular/core';
import { getUsersDistance } from '../../global_modules/locationTreating';
import { removeASingleFromArray } from '../../global_modules/globalFunctions';
import { Geolocation } from '@ionic-native/geolocation/ngx';
import { Socket } from 'ngx-socket-io';
import { Observable } from 'rxjs/Observable';
@Component({
selector: 'app-select-users-modal',
templateUrl: './select-users-modal.page.html',
styleUrls: ['./select-users-modal.page.scss'],
})
export class SelectUsersModalPage implements OnInit {
public selectedUsers = [];
public absOnlineUsers = [];
public onlineUsers = [];
public actualGeoLocation;
public userData;
private randomContent = [
'Sneaky little hobbitses!',
"Go back, Sam! I'm going to Mordor alone!",
"What we need is a few good taters.",
"Also i need a burger, let's pick one up!",
"I have hunger!, let me food!"
];
constructor(private socket: Socket, geolocation: Geolocation){
this.getConnections().subscribe((data) =>{
switch(data['event']){
case 'joined':
this.onlineUsers = this.onlineUsers.concat([data]);
this.absOnlineUsers = getUsersDistance(this.actualGeoLocation, this.onlineUsers);
break;
default:
this.onlineUsers = removeASingleFromArray(this.onlineUsers, data['index']);
this.absOnlineUsers = removeASingleFromArray(this.absOnlineUsers, data['index']);
break;
}
});
}
private async getActualGeoPosition() {
const rta = await this.geolocation.getCurrentPosition();
return {
lat: rta.coords.latitude,
lng: rta.coords.longitude
};
}
getConnections(){
let observable = new Observable(observer =>{
this.socket.on("users-changed", data =>{
observer.next(data);
});
});
return observable;
}
ionViewWillEnter(){
this.setup();
}
async setup(){
this.userData = await this.getUserData();
this.actualGeoLocation = await this.getActualGeoPosition();
this.setSocConnection();
}
async setSocConnection(){
this.userData = await this.getUserData();
this.actualGeoLocation = await this.getActualGeoPosition();
this.socket.connect();
this.socket.emit('set-nickname',
{
nickname: this.userData.data.username, type_user: this.userData.data.type_user,
src: this.defaultAvatarImage, //cambiar a una url si se desea
description: this.randomContent[Math.random() * this.randomContent.length | 0],
location: this.actualGeoLocation
}
);
}
async getUserData(){
//session storage = {data: {username: '', type_user: ''}}
return await this.storage.get('session_storage');
}
ionViewWillLeave(){
this.socket.disconnect();
}
}
the functions that are imported called getUserDistance and remove a single fromArray look like this:
export function removeASingleFromArray(arr, index){
let firstPart = arr.slice(0, index < 0 ? 0 : index);
let secondPart = index < arr.length ? arr.slice(index + 1): null;
let transformedArr = secondPart ? firstPart.concat(secondPart) : firstPart;
return transformedArr;
}
export function getUsersDistance(userLocation, locations){
return locations.map(loc => {
const latDiff = Math.abs(userLocation.lat - loc.lat);
const lngDiff = Math.abs(userLocation.lng - loc.lng);
const diff = latDiff + lngDiff;
return {
src: loc.src,
name: loc.name,
description: loc.description,
isChecked: loc.isChecked,
location: {
lat: latDiff, lng: lngDiff, diff
},
event: loc.event
};
});
}
the GLOBAL_SERVER constant looks like this:
export const GLOBAL_SERVER = "http://192.168.1.178";
What has to happen is that when I open the app on the cel in that tab I have to make a connection to the socket, which works well for me when I do it from the PC and I can make as many connections as I want, but when I try to do it on the phone, absolutely nothing happens... as last I leave part of the html of the select-users-modal.page.html page:
<ion-header translucent>
<ion-toolbar class = "selectUsersToolbar">
<!-- Ciert contenido irrelevante... -->
</ion-toolbar>
</ion-header>
<ion-content fullscreen>
<ion-list>
<ion-item lines = "inset" class = "usersItem unselectedItem"
*ngFor = "let onlineUser of absOnlineUsers | orderBy: ['location.diff'];">
<ion-avatar slot="start">
<img [src]="onlineUser.src"><!-- cambiar si se desea a una url -->
</ion-avatar>
<ion-label>
<h2>{{onlineUser.name}}</h2>
<p>{{onlineUser.description}}</p>
</ion-label>
</ion-item>
</ion-list>
</ion-content>
For example, if I go to the app from my cell phone, at the time the connection is made (if it is made), and then, for example, from my PC, I try to make another connection from my PC to the socket, as a result, my cell phone should a new user appears in the list, which is not the case... what's more, the socket server doesn't even detect any connection to the server from my android... and when one of the two is connected and I try to connect from another device, in In my case (my cell phone and the PC), the server does not detect neither the cell phone nor the PC connections when I disconnect from the socket and reconnect...
I was reviewing my code and I didn't really find anything strange either from the client or from the server, however the connection was still not made correctly from the cell phone, so yesterday I was placing many console.log from there for there and I realized something:
The server connection function from the cell phone and some others were never being executed! The reason why it worked on the PC but did not work on the cell phone is that I had already given location permissions from the PC and had saved them, for Therefore, it obtained the geolocation of the client and executed AFTER, the location had been obtained, it executed the other functions, this was precisely the problem!
what happens is that functions like:
I made them asynchronous, the result of this is that until both are executed successfully or are rejected, it will not execute the third line, which is the connection with the socket:
And I did not have the location activated from the phone when I tried to execute the connection, for this reason the function that obtains the location was never resolved or rejected, so I activated the location of the phone and it worked! part of the connection with the socket on the phone because the location was never resolved...