blocshop / sockets-for-cordova

Cordova plugin for socket network communication
138 stars 128 forks source link

How to properly integrate in Ionic 3 #50

Open mapicard opened 4 years ago

mapicard commented 4 years ago

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 commented 4 years ago

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

But... vsCode autocompletion not working...

mapicard commented 4 years ago

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 commented 4 years ago

@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 commented 4 years ago

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

daleffe commented 4 years ago

@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 commented 4 years ago

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 commented 4 years ago

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

Burnie777 commented 3 years ago

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 commented 3 years ago

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:

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 commented 3 years ago

Thank you @daleffe,

I will check it out… And let you know when and where I get stuck in the process… I would appreciate the sample project as well…

Burnie777 commented 3 years ago

Thank you @daleffe,

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

Burnie777 commented 3 years ago

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 commented 10 months ago

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

daleffe commented 10 months ago

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

Do you have plans for Capacitor version?

kitolog commented 10 months ago

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