Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to properly integrate in Ionic 3 #50

Open
mapicard opened this issue Mar 2, 2020 · 15 comments
Open

How to properly integrate in Ionic 3 #50

mapicard opened this issue Mar 2, 2020 · 15 comments

Comments

@mapicard
Copy link

mapicard commented Mar 2, 2020

Hi,
I've installed the plugin with npm i blocshop-sockets-for-cordova-plugin and added it under cordova.js in index.html as instructed.

If I use this.socket = new Socket(); in my constructor, the build fails with
Cannot find name 'Socket'.
Error: Failed to transpile program

If I use this.socket = new (<any>window).Socket();

When building the app with
ionic cordova emulate ios --target "iPhone-XS-Max, 12.4"
or
ionic cordova emulate android

the builds succeed but do not contain the file socket.js and Chrome console says
window.Socket is not a constructor.

I'm using
ionic-angular 3.9.2
Cordova android:7.0.0, ios: 5.1.1

@mapicard mapicard changed the title socket.js file not copied to ios build socket.js file not copied to build packages (iOS and Android) Mar 3, 2020
@mapicard
Copy link
Author

mapicard commented Mar 3, 2020

I have removed the plugin npm remove blocshop-sockets-for-cordova-plugin
Then I added it with

ionic cordova plugin add blocshop-sockets-for-cordova-plugin

Then no more errors when running my constructor.
Still have an issue with the plugin package.json file, vscode complains that
Incorrect type. "Expected array". [32,25]
which is "bundleDependencies": false,
I changed it to "bundleDependencies": [],
No more errors in vscode.

I still have to use (<any>window).Socket() in my constructor for the build to succeed.

When I try to import { Socket } from 'blockshop-sockets-for-cordova-plugin';
vscode complains about not finding the module and the build fails with
Cannot find module 'blockshop-sockets-for-cordova-plugin'.

Some more googling lead me try replace the import with declare var Socket:any; and now

  • vsCode does not complain about the module
  • I can use this.socket = new Socket(); in my constructor
  • socket apis (open, write, etc.) seem to work on Android and iOS

But... vsCode autocompletion not working...

@mapicard mapicard changed the title socket.js file not copied to build packages (iOS and Android) How to properly integrate in Ionic 3 Mar 4, 2020
@mapicard
Copy link
Author

mapicard commented Mar 4, 2020

Just in case somebody else needs this, I'll keep a recap here to summarize the steps to make it work under Ionic3.

Intall the plugin with

ionic cordova plugin add blocshop-sockets-for-cordova-plugin

In the module where you need it, use declare instead of import and use the api as documented.

I'm updating this recap to provide more details on my progress as requested. So far I can connect to my legacy server, send some data, receive a response and close the connection.

*** Keep in mind: I am new to Ionic & this library, if there's a better way, please advise. ***

In my case, tcp-service.ts looks like this so far

import { Injectable } from '@angular/core';
var declare Socket:any  //import { Socket } from 'blocshop-sockets-for-cordova-plugin
@Injectable()
export class TCPServices {
  private socket;
  constructor() {
    this.socket = new Socket();
    this.socket.onData = ((data) => {
      // invoked after new batch of data is received (typed array of bytes Uint8Array)
      console.log('socket.onData'+this.arrayBuffer2str(data));
    });
    this.socket.onError = function(errorMessage) {
      // invoked after error occurs during connection
      console.log('socket.onError'+errorMessage);
    };
    this.socket.onClose = function(hasError) {
      // invoked after connection close
      console.log('socket.onClose'+hasError);
    };
  }
  sendTest() {
    this.socket.open("192.168.1.71", 23,
      () => {
        // invoked after successful opening of socket
        console.log('socket.open.success');
        this.socket.write(String.fromCharCode(13));
        setTimeout(()=> { this.socket.shutdownWrite(); }, 2000);
      },
      (errorMessage) => {
        // invoked after unsuccessful opening of socket
        console.log('socket.open.failed'+errorMessage);
      }
    );		
  }

and my server-details.ts

import { TCPServices } from '../../providers/providers';
@Component({
export class ServerDetailsPage {
  ...
  constructor(..., public tcp:TCPServices) {
    ...
  }
  buttonClicked(thisServer:Server) {
    this.tcp.sendTest();
  }
}

@daleffe
Copy link

daleffe commented Mar 8, 2020

@mapicard I'm trying to send data to the other side, but it is not working, I can only make the connection, could you send me a complete example?

@mapicard
Copy link
Author

mapicard commented Mar 8, 2020

@daleffe I have updated my recap with more details. Hope this helps.

@daleffe
Copy link

daleffe commented Mar 9, 2020

@daleffe I have updated my recap with more details. Hope this helps.

Thanks for your update.

I did some tests using netcat tcp servers and i was unable to receive the string, what i'm doing wrong?

I tried like this:

this.socket.open("192.168.25.6", 37005,
      () => {
        // invoked after successful opening of socket        
	var dataString = "Hello world";
        var data = new Uint8Array(dataString.length);
        for (var i = 0; i < data.length; i++) {
          data[i] = dataString.charCodeAt(i);
        }

        this.socket.write(data + String.fromCharCode(10) + String.fromCharCode(13));
        setTimeout(()=> { this.socket.shutdownWrite(); }, 2000);
      },
      (errorMessage) => {
        // invoked after unsuccessful opening of socket
        console.log('socket.open.failed'+errorMessage);
      }
    );	

And i tried like this:

this.socket.write ("Hello world" + String.fromCharCode (10) + String.fromCharCode (13));

Can you help me?

@mapicard
Copy link
Author

mapicard commented Mar 9, 2020

I don't know your server app since port 37005 is unassigned, but I doubt its tcp handshake is expecting the string "Hello world". But then anything is possible. Try to send something that the server expects.

I did some tests using netcat tcp servers and i was unable to receive the string, what i'm doing wrong?

I'm not familiar with netcat but I would start by making sure the client device (mobile) can communicate with the server app. Any configurable Telnet client (iOS & Android) woud do. You won't go through the whole handshake but you'll be able to tell if you can connect and therefore determine if your problem is or is not related to the network or firewall.

@daleffe
Copy link

daleffe commented Aug 6, 2020

@mapicard i did new test again and it works. Thanks!

@Burnie777
Copy link

Any way to help with a ionic 4/5 integration using this... I am struggling to get the var declare Socket:any setup to work in Ionic 4/5

@daleffe
Copy link

daleffe commented Dec 9, 2020

Any way to help with a ionic 4/5 integration using this... I am struggling to get the var declare Socket:any setup to work in Ionic 4/5

Hi @Burnie777, i will reproduce some parts of code of production app that works to help you.

  1. The socket service

Here we created a service with following content, transforming the Cordova events in Observables:

import { Injectable } from '@angular/core';
import { Platform } from '@ionic/angular';

import { Observable } from 'rxjs';

import { ConverterProvider } from './../providers/converter.provider';

@Injectable({
  providedIn: 'root'
})
export class SocketService {
  tcp: any;

  constructor(private platform: Platform, private converter: ConverterProvider) { 
    this.platform.ready().then(() => {      
      this.tcp = new (<any>window).Socket();
    });    
  }

  init() {
    this.tcp = new (<any>window).Socket();
  }

  events() {    
    return new Observable(observer => {
      this.tcp.onData = asData => observer.next('socket.onData|' + this.converter.arrayBuffer2str(asData));

      this.tcp.onError = asError => observer.next('socket.onError|' + JSON.stringify(asError));

      this.tcp.onClose = asClose => observer.error('socket.onClose|' + asClose);      
    });
  }  
}
  1. Converter helper

We created a provider with converter functions:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class ConverterProvider {

  constructor() { }

  arrayBuffer2str(buf: Uint8Array) {
    return String.fromCharCode.apply(null, new Uint16Array(buf));
  }

  Str2ArrayBuffer(dataString: string) {
    let data = new Uint8Array(dataString.length);
    for (let i = 0; i < data.length; i++) {
      data[i] = dataString.charCodeAt(i);
    }
    return data;
  }
}
  1. Putting it to work

Finally we are able to use sockets! Below is an example of how to implement it on your foo.page.ts (some parts of code ommited):

import { Component, OnInit, ViewChild, NgZone } from '@angular/core';

import { ConverterProvider } from './../../providers/converter.provider';

import { SocketService } from './../../services/socket.service';

constructor(private ngZone: NgZone, private converter: ConverterProvider, private socket: SocketService) {}

private open(device: DeviceModel) {
    this.socket.init();

    this.socket.tcp.open(device.address, device.netPortConfig,
      () => {
        this.ngZone.run(() => {
          this.setDeviceState(true);

          console.log(`[Socket] New connection: ${device.address} (${device.name})`);

          this.message.popUp(`${device.name} connected`);
        });

        this.socket.events().subscribe(
          data => {
            let split = data.toString().split('|');

            if (split[0].toLowerCase().trim().includes('socket.onerror')) {
              this.ngZone.run(() => console.warn(`[Socket] Connection error:`, JSON.parse(split[1].trim())));
            } else {
              let received = split[1].trim();
              console.log('Received data: ', received);
            }
          },
          error => {
            this.ngZone.run(() => {
              this.message.popUp(`Device was disconnected`);
              
              this.setDeviceState(false);

              console.error('[Socket] Connection closed:', error);
            });
          }
        );        

        this.socket.tcp.write(
          this.converter.Str2ArrayBuffer('Hello World!'),
          () => {
            this.ngZone.run(() => {
              console.log(`[Socket] Data successfully sent to ${device.address} (${device.name})`);
            });
          }, error => {
            this.ngZone.run(() => {
              console.error(`[Socket] Unable to sent data to ${device.address} (${device.name})`, error);
            });
          }
        )
      }, (error: string) => {
        this.ngZone.run(() => {
          this.setDeviceState(false);          
          
          console.error(`[Socket] Unable to connect: ${device.address} (${device.name}) `, error);
          this.message.popUp(`Unable to connect: ${device.name}`);
        });
      }
    );
  }

You can now create a button by calling the open method and passing the settings of the server (which I called the device) you want to connect.

Some notes:

  • The Device parameter in open method has the connection configs (host address, network port, etc);
  • We created the method init() in socket service because when you reuse the object a lot of times it crashes plugin, then we "init" every new connection;
  • NgZone is used to properly update interface/visual elements (i just left the console.log for example).

Hope it helps, if you have any problems, just point out that we help (I screwed up a lot until it worked correctly).

I'll create an example project this week to help then update here.

@Burnie777
Copy link

Burnie777 commented Dec 9, 2020 via email

@Burnie777
Copy link

Thank you @daleffe,

I was able to get it to work with the above mentioned code... Much appreciated...

@Burnie777
Copy link

Burnie777 commented Apr 21, 2021

Good day, how would I be able to timeout the call with an error if not responding within a specific timeout?

I have tried a settimeout in 2 places with no success...

 events() {    
    return new Observable(observer => {
      setTimeout({
      this.tcp.onData = asData => observer.next('socket.onData|' + this.converter.arrayBuffer2str(asData));

      this.tcp.onError = asError => observer.next('socket.onError|' + JSON.stringify(asError));

      this.tcp.onClose = asClose => observer.error('socket.onClose|' + asClose);      
    },2000); 
 });
  }  

and also put the onOpen content in a setTimeout...

The issue we are having is that some of the places, our app is being used, may struggle with connection to the server, and sometimes overall connectivity... thus open connection times out and fails... so I would like to shorten the time of that timeout

@kitolog
Copy link

kitolog commented Jan 18, 2024

Alternatively, you can try this plugin (fork with connection fixes)
cordova-plugin-socket-tcp

@daleffe
Copy link

daleffe commented Jan 19, 2024

Alternatively, you can try this plugin (fork with connection fixes) cordova-plugin-socket-tcp

Do you have plans for Capacitor version?

@kitolog
Copy link

kitolog commented Jan 21, 2024

Alternatively, you can try this plugin (fork with connection fixes) cordova-plugin-socket-tcp

Do you have plans for Capacitor version?

It's possible, I'll add the Capacitor version in a few months

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants