loopbackio / loopback-next

LoopBack makes it easy to build modern API applications that require complex integrations.
https://loopback.io
Other
4.95k stars 1.07k forks source link

How to integrate socket in loopback 4 server API #4044

Closed imranalisolanki closed 3 years ago

imranalisolanki commented 4 years ago

i want implement socket in my loopback4 server API. i have integrate socket in my API but it;s routing is not working.

app.io = require('socket.io')(await app.start());
  app.io.on('connection', async function (socket: any) {

    console.log('connected', socket.id)
    socket.on('disconnect', function () {
      console.log('user disconnected');
    });
  });

it's my code but socket is not connected. i have seen this example.

https://github.com/raymondfeng/loopback4-example-websocket/ but it's work only socket server not working with API server. please suggest me how i can implement.

hacksparrow commented 4 years ago

Hi @imranalisolanki can you share a minimal version of your app reproducing the issue, so I can troubleshoot? Sharing a code snippet is not enough, since other factors responsible for the failure may be left out of the context.

imranalisolanki commented 4 years ago

@hacksparrow i have create a simple loopback4 server with some routing and i have gone through https://github.com/raymondfeng/loopback4-example-websocket/ this example and when i have configure it in my server then existing routing is not working. Application.ts
import { BootMixin } from '@loopback/boot'; import { ApplicationConfig, BindingKey } from '@loopback/core'; import { RestExplorerBindings, RestExplorerComponent, } from '@loopback/rest-explorer'; import { RepositoryMixin } from '@loopback/repository'; import { RestApplication } from '@loopback/rest'; import { ServiceMixin } from '@loopback/service-proxy'; import * as path from 'path'; import { AuthenticationSequence } from './sequence'; import { AuthenticationComponent, registerAuthenticationStrategy } from '@loopback/authentication';

import { TokenServiceBindings, UserServiceBindings, TokenServiceConstants, PasswordHasherBindings, EmailServiceBindings, FCMServiceBindings, ControllerServiceBindings } from './keys'; import { JWTService } from './services/jwt-service'; import { MemberService } from './services/user-service'; import { BcryptHasher } from './services/hash.password.bcryptjs'; import { JWTAuthenticationStrategy } from './authentication-strategies/jwt-strategy'; import { EmailService, FCMService, ControllerService } from './services'; import { ReviewController } from './controllers'; import { SECURITY_SCHEME_SPEC } from './utils/security-spec';

export interface PackageInfo { name: string; version: string; description: string; } export const PackageKey = BindingKey.create('application.package');

const pkg: PackageInfo = require('../package.json');

export class BreezeApiApplication extends BootMixin(ServiceMixin(RepositoryMixin(RestApplication))) { constructor(options: ApplicationConfig = {}) { super(options);

this.api({
  openapi: '3.0.0',
  info: { title: pkg.name, version: pkg.version },
  paths: {},
  components: { securitySchemes: SECURITY_SCHEME_SPEC },
  servers: [{ url: '/' }],
})

this.setUpBindings();

// Bind authentication component related elements
this.component(AuthenticationComponent);

registerAuthenticationStrategy(this, JWTAuthenticationStrategy);

// Set up the custom sequence
this.sequence(AuthenticationSequence);

// Set up default home page
this.static('/', path.join(__dirname, '../public'));

// Customize @loopback/rest-explorer configuration here
this.bind(RestExplorerBindings.CONFIG).to({ path: '/explorer', });
this.component(RestExplorerComponent);

this.projectRoot = __dirname;
// Customize @loopback/boot Booter Conventions here
this.bootOptions = {
  controllers: {
    // Customize ControllerBooter Conventions here
    dirs: ['controllers'],
    extensions: ['.controller.js'],
    nested: true,
  },
};

}

setUpBindings(): void { this.bind(TokenServiceBindings.TOKEN_SECRET).to(TokenServiceConstants.TOKEN_SECRET_VALUE); this.bind(TokenServiceBindings.TOKEN_EXPIRES_IN).to(TokenServiceConstants.TOKEN_EXPIRES_IN_VALUE); this.bind(TokenServiceBindings.TOKEN_SERVICE).toClass(JWTService);

this.bind(PasswordHasherBindings.ROUNDS).to(10);
this.bind(PasswordHasherBindings.PASSWORD_HASHER).toClass(BcryptHasher);

this.bind(UserServiceBindings.USER_SERVICE).toClass(MemberService);
this.bind(EmailServiceBindings.MAIL_SERVICE).toClass(EmailService)
this.bind(FCMServiceBindings.FCM_SERVICE).toClass(FCMService);
this.bind(ControllerServiceBindings.CONTROLLER_SERVICE).toClass(ControllerService);

} }

index.ts

import { BreezeApiApplication } from './application'; import { ApplicationConfig } from '@loopback/core';

export { BreezeApiApplication };

export async function main(options: ApplicationConfig = {}) { const app = new BreezeApiApplication(options); await app.boot(); //await app.start();

app.io = require('socket.io')(await app.start()); app.io.on('connection', async function (socket: any) {

console.log('connected', socket.id)
socket.on('disconnect', function () {
  console.log('user disconnected');
});

});

const url = app.restServer.url; console.log(Server is running at ${url});

return app; }

hacksparrow commented 4 years ago

@imranalisolanki the code you pasted refers to many files not found in the original https://github.com/raymondfeng/loopback4-example-websocket/. Also it has basic run-time errors like console.log(Server is running at ${url});.

I will be able to help you if you can provide a minimal app reproducing the error. Code snippets are not enough.

kchraniuk commented 4 years ago

I have the same problem as @imranalisolanki. I would also like to know how to use/implement the websocket from this example https://github.com/raymondfeng/loopback4-example-websocket/ in this example https://github.com/strongloop/loopback4-example-shopping .

xdien commented 4 years ago

I have been following this topic for a long time. Everyone who has done it successfully with https://github.com/strongloop/loopback4-example-shopping. Particularly https://github.com/raymondfeng/loopback4-example-websocket/ using httpserver with "Express" I do not know how to implement with RestExplorerComponent

alexkander commented 4 years ago

Hi guys, I had the same problem: Looback 4 RestApplication + socket.io. I solved it based on loopback4-example-todo-list and loopback4-example-websocket. Here is the partial codes:

I am starting with LB4, and I don't know what are the future repercussions of this implementation or if it is a bad practice, but, at the moment, it works for me and maybe for you too.

-------------------------- Updated ---------------------

To inject dependencies of the main app into websocket controllers you must inherit the websocket context of the main context

xdien commented 4 years ago

thanks @arondn2 . I will try to integrate it into my program

imranalisolanki commented 4 years ago

Hi @arondn2 . when i integrate in my program i am getting error. Error: The key 'authentication.actions.authenticate' is not bound to any value in context application

alexkander commented 4 years ago

hi @imranalisolanki. I had a similar problem after my first comment in this feed. It is because websocket has not the same context of app. If you have a regular loopbak 4 app like loopback4-example-todo-list you must inherit the websocket context of the main context. I did with this changes.

-------------------------- Updated ---------------------

The links were wrong. I have updated them.

d-bo commented 4 years ago

@arondn2 httpserver and websocketserver must have different ports ? I get Cannot start the application. { Error: listen EADDRINUSE: address already in use :::5000

index.ts used from src/index.ts

xdien commented 4 years ago

@d-bo I successfully handled the web when I changed port 5000 to a different port than the http server. index.js

if (require.main === module) {
  // Run the application
  const config = {
    rest: {
      port: +(process.env.PORT || 3000),
      host: process.env.HOST,
      protocol: process.env.HTTPS === "1" ? "https" : "http",
      key: fs.readFileSync(path.join(__dirname, './ssl/private/server-key.pem')).toString(),
      cert: fs.readFileSync(path.join(__dirname, './ssl/private/server-cert.pem')).toString(),
      // The `gracePeriodForClose` provides a graceful close for http/https
      // servers with keep-alive clients. The default value is `Infinity`
      // (don't force-close). If you want to immediately destroy all sockets
      // upon stop, set its value to `0`.
      // See https://www.npmjs.com/package/stoppable
      gracePeriodForClose: 5000, // 5 seconds
      openApiSpec: {
        // useful when used with OpenAPI-to-GraphQL to locate your application
        setServersFromRequest: true,
      },
    },
    websocket: { port: 5000 }
  };
  application.main(config).catch(err => {
    console.error('Cannot start the application.', err);
    process.exit(1);
  });
}
d-bo commented 4 years ago

@xdien just commented out original http server from skeleton app. finally both websocket and http server are on the same port:


// src/index.ts

// ...

export async function main(options: ApplicationConfig = {}) {
  const app = new PrjApplication(options);
  await app.boot();
  // commented out original http server
  //await app.start();
  await app.startWebSocket();

  const url = app.restServer.url;
  console.log(`Server is running at ${app.httpServer.url}/api`);

  return app;
}

// ...
alexkander commented 4 years ago

I didn't need to comment on that line. I made a complete example with the integration: loopback4-example-websocket-app. I hope it helps

d-bo commented 4 years ago

I try to inject websocket controller into notification.controller via

constructor(
    @inject('controllers.WebSocketController') private ws: WebSocketController,
) {}

Seems it is not the right way doing that:

500 ResolutionError: The key 'ws.socket' is not bound to any value in context 
RequestContext-6bGhwRlvTmCH8MjrUYqIBQ-4 (context: RequestContext-6bGhwRlvTmCH8MjrUYqIBQ-4, 
binding: ws.socket, resolutionPath: controllers.NotificationController --> 
@NotificationController.constructor[4] --> controllers.WebSocketController --> 
@WebSocketController.constructor[0])
alexkander commented 4 years ago

@d-bo why are you trying to inject a controller into another controller? I assume notification.controller is a regular rest controller to handle HTTP requests, and WebSocketController is to handle connections socket.io. WebSocketController need the socket instance from socket.io connection which doesn't exist in a HTTP request.

d-bo commented 4 years ago

@arondn2 i need to emit incoming data from rest notification.controller to websocket

// for example
@post('/notifications')
async create(data: any) {
    ...
    this.socket.emit('notification', data);
}

tried inject only socket

constructor(
    @ws.socket() private socket: Socket,
) {}

no luck) same 500 ResolutionError ...

alexkander commented 4 years ago

@d-bo I think you have to implement a room scheme like socket.io explains in their documentation here. I added some code in loopback4-example-websocket-app repo to show you a way to implement it, specifically how to emit an event from TodoController (Http request controller) to socket.io room. The specifics changes are:

argupta23 commented 4 years ago

@arondn2,

Thanks for sharing the updated repo.

I just downloaded your updated version and it looks like we still have an issue with "port in use".

`> loopback-example-websocket-app@0.1.2 prestart /home/zzz/latesttry/loopback/lb4/loopback4-example-websocket-app

npm run build

loopback-example-websocket-app@0.1.2 build /home/zzz/latesttry/loopback/lb4/loopback4-example-websocket-app lb-tsc

loopback-example-websocket-app@0.1.2 start /home/zzz/latesttry/loopback/lb4/loopback4-example-websocket-app node .

Cannot start the application. Error: listen EADDRINUSE: address already in use 127.0.0.1:3000 at Server.setupListenHandle [as _listen2] (net.js:1313:16) at listenInCluster (net.js:1361:12) at GetAddrInfoReqWrap.doListen [as callback] (net.js:1498:7) at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:68:8) { code: 'EADDRINUSE', errno: 'EADDRINUSE', syscall: 'listen', address: '127.0.0.1', port: 3000 } npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! loopback-example-websocket-app@0.1.2 start: node . npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the loopback-example-websocket-app@0.1.2 start script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in: `

netstat -tulpn (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - tcp 0 0 172.20.20.235:5432 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:6011 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:6012 0.0.0.0:* LISTEN - tcp 0 0 172.20.20.235:6379 0.0.0.0:* LISTEN - tcp6 0 0 :::22 :::* LISTEN - tcp6 0 0 ::1:6010 :::* LISTEN - tcp6 0 0 ::1:6011 :::* LISTEN - tcp6 0 0 ::1:6012 :::* LISTEN - tcp6 0 0 :::80 :::* LISTEN - udp 0 0 127.0.0.53:53 0.0.0.0:* - udp 0 0 172.20.20.235:68 0.0.0.0:* -

Will appreciate if you can please look into it.

Thanks

alexkander commented 4 years ago

@argupta23 Sorry, i can't get this issue but I am looking at what is happening. Have you tried removing the node_modules folder, reinstalling dependencies? Can you use npm run clean && npm run start instead of npm run build, lb-tsc and node .?

argupta23 commented 4 years ago

@d-bo

I am in the same situation as you and trying to achieve something similar.

@arondn2

Yes, I did what you suggested but have the same result.

I even tried the suggestion that was earlier made to comment out app.start(), but that made matters worse.

To get around the issue, within index.ts, I appended the port with 5000 and it seems to comeup. websocket: { port: 5000 } It also requires public/index.html to listen on 5000

But in this case we now have the app running on 2 ports (3000 and 5000).

Hope this helps. Please let me know if you want me try something else.

alexkander commented 4 years ago

@argupta23 I will comment this when I find out the issue. @d-bo, do you have the same problem with the port? were you start the repo example that I left?

alexkander commented 4 years ago

@argupta23 I have run the example in 3 differents computers and it always run ok. I have noticed you haven't run netstat command as root, and logs are says Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all, which means those are not all the proccesses. Please trying using another port in src/index.js

d-bo commented 4 years ago

@arondn2 no, i didn't clone the repo loopback4-example-websocket-app. I took files and pieces of code and port them to skeleton app generated from lb4 cli ...

argupta23 commented 4 years ago

@d-bo, does your version comeup on a single port?

@arondn2,

Just ran a quick test again for you and here is the output.

ubuntu: 20.04 running kernel 5.4.0-40-generic node -v : 12.18.2 npm -v : 6.14.7

and package.json

{ "name": "@loopback/example-file-transfer", "version": "1.4.2", "description": "Example application for file upload/download with LoopBack 4", "main": "dist/index.js", "types": "dist/index.d.ts", "engines": { "node": ">=10.16" }, "author": "IBM Corp.", "copyright.owner": "IBM Corp.", "license": "MIT", "publishConfig": { "access": "public" }, "scripts": { "acceptance": "lb-mocha \"dist/tests/acceptance//.js\"", "build": "lb-tsc", "build:watch": "lb-tsc --watch", "clean": "lb-clean example-file-transfer.tgz dist .tsbuildinfo package", "verify": "npm pack && tar xf example-file-transfer.tgz && tree package && npm run clean", "lint": "npm run prettier:check && npm run eslint", "lint:fix": "npm run eslint:fix && npm run prettier:fix", "prettier:cli": "lb-prettier \"/*.ts\" \"/*.js\"", "prettier:check": "npm run prettier:cli -- -l", "prettier:fix": "npm run prettier:cli -- --write", "eslint": "lb-eslint --report-unused-disable-directives .", "eslint:fix": "npm run eslint -- --fix", "pretest": "npm run clean && npm run build", "test": "lb-mocha \"dist/tests/*/.js\"", "test:dev": "lb-mocha dist/tests//*.js && npm run posttest", "prestart": "npm run build", "start": "node ." }, "repository": { "type": "git", "url": "https://github.com/strongloop/loopback-next.git", "directory": "examples/file-transfer" }, "dependencies": { "@loopback/boot": "^2.4.0", "@loopback/core": "^2.9.2", "@loopback/repository": "^2.10.0", "@loopback/rest": "^5.2.1", "@loopback/rest-explorer": "^2.2.7", "loopback-connector-mongodb": "^5.3.0", "multer": "^1.4.2", "tslib": "^2.0.0" }, "devDependencies": { "@loopback/build": "^6.1.1", "@loopback/eslint-config": "^8.0.4", "@loopback/testlab": "^3.2.1", "@types/express-serve-static-core": "^4.17.8", "@types/multer": "^1.4.3", "@types/node": "^10.17.27", "eslint": "^7.5.0", "typescript": "~3.9.7" }, "keywords": [ "loopback", "LoopBack", "example", "file", "upload" ], "gitHead": "5538896411bb56467ae52670a29d1aec1690be74" }

sudo netstat -tulpn Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.53:53 0.0.0.0: LISTEN 816/systemd-resolve tcp 0 0 0.0.0.0:22 0.0.0.0: LISTEN 902/sshd: /usr/sbin tcp 0 0 172.20.20.235:5432 0.0.0.0: LISTEN 56555/postgres tcp 0 0 127.0.0.1:6010 0.0.0.0: LISTEN 704372/sshd: argupt tcp 0 0 127.0.0.1:6011 0.0.0.0: LISTEN 712372/sshd: argupt tcp 0 0 172.20.20.235:6379 0.0.0.0: LISTEN 43528/redis-server tcp6 0 0 :::22 ::: LISTEN 902/sshd: /usr/sbin tcp6 0 0 ::1:6010 ::: LISTEN 704372/sshd: argupt tcp6 0 0 ::1:6011 ::: LISTEN 712372/sshd: argupt tcp6 0 0 :::80 ::: LISTEN 54445/apache2 udp 0 0 127.0.0.53:53 0.0.0.0: 816/systemd-resolve udp 0 0 172.20.20.235:68 0.0.0.0: 814/systemd-network

set websocket port to blank loopback4-example-websocket-app$ vi src/index.ts loopback4-example-websocket-app$ npm start

loopback-example-websocket-app@0.1.2 prestart /home/argupta/latesttry/loopback/lb4/loopback4-example-webs npm run build loopback-example-websocket-app@0.1.2 build /home/argupta/latesttry/loopback/lb4/loopback4-example-websock lb-tsc loopback-example-websocket-app@0.1.2 start /home/argupta/latesttry/loopback/lb4/loopback4-example-websock node .

Cannot start the application. Error: listen EADDRINUSE: address already in use 127.0.0.1:3000 at Server.setupListenHandle [as _listen2] (net.js:1313:16) at listenInCluster (net.js:1361:12) at GetAddrInfoReqWrap.doListen [as callback] (net.js:1498:7) at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:68:8) { code: 'EADDRINUSE', errno: 'EADDRINUSE', syscall: 'listen', address: '127.0.0.1', port: 3000 } npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! loopback-example-websocket-app@0.1.2 start: node . npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the loopback-example-websocket-app@0.1.2 start script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

npm ERR! A complete log of this run can be found in: npm ERR! /home/argupta/.npm/_logs/2020-07-29T20_14_16_107Z-debug.log

sudo netstat -tulpn Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.53:53 0.0.0.0: LISTEN 816/systemd-resolve tcp 0 0 0.0.0.0:22 0.0.0.0: LISTEN 902/sshd: /usr/sbin tcp 0 0 172.20.20.235:5432 0.0.0.0: LISTEN 56555/postgres tcp 0 0 127.0.0.1:6010 0.0.0.0: LISTEN 704372/sshd: argupt tcp 0 0 127.0.0.1:6011 0.0.0.0: LISTEN 712372/sshd: argupt tcp 0 0 172.20.20.235:6379 0.0.0.0: LISTEN 43528/redis-server tcp6 0 0 :::22 ::: LISTEN 902/sshd: /usr/sbin tcp6 0 0 ::1:6010 ::: LISTEN 704372/sshd: argupt tcp6 0 0 ::1:6011 ::: LISTEN 712372/sshd: argupt tcp6 0 0 :::80 ::: LISTEN 54445/apache2 udp 0 0 127.0.0.53:53 0.0.0.0: 816/systemd-resolve udp 0 0 172.20.20.235:68 0.0.0.0: 814/systemd-network

updated websocket port to 5000

loopback4-example-websocket-app$ vi src/index.ts loopback4-example-websocket-app$ sudo netstat -tulpn Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.53:53 0.0.0.0: LISTEN 816/systemd-resolve tcp 0 0 0.0.0.0:22 0.0.0.0: LISTEN 902/sshd: /usr/sbin tcp 0 0 172.20.20.235:5432 0.0.0.0: LISTEN 56555/postgres tcp 0 0 127.0.0.1:6010 0.0.0.0: LISTEN 704372/sshd: argupt tcp 0 0 127.0.0.1:6011 0.0.0.0: LISTEN 712372/sshd: argupt tcp 0 0 172.20.20.235:6379 0.0.0.0: LISTEN 43528/redis-server tcp6 0 0 :::22 ::: LISTEN 902/sshd: /usr/sbin tcp6 0 0 ::1:6010 ::: LISTEN 704372/sshd: argupt tcp6 0 0 ::1:6011 ::: LISTEN 712372/sshd: argupt tcp6 0 0 :::80 ::: LISTEN 54445/apache2 udp 0 0 127.0.0.53:53 0.0.0.0: 816/systemd-resolve udp 0 0 172.20.20.235:68 0.0.0.0: 814/systemd-network

loopback4-example-websocket-app$ npm start

loopback-example-websocket-app@0.1.2 prestart /home/argupta/latesttry/loopback/lb4/loopback4-example-webs npm run build loopback-example-websocket-app@0.1.2 build /home/argupta/latesttry/loopback/lb4/loopback4-example-websock lb-tsc loopback-example-websocket-app@0.1.2 start /home/argupta/latesttry/loopback/lb4/loopback4-example-websock node .

Server is running at http://127.0.0.1:3000

ran netstat -tulpn from another terminal sudo netstat -tulpn [sudo] password for argupta: Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 127.0.0.53:53 0.0.0.0: LISTEN 816/systemd-resolve tcp 0 0 0.0.0.0:22 0.0.0.0: LISTEN 902/sshd: /usr/sbin tcp 0 0 127.0.0.1:3000 0.0.0.0: LISTEN 715259/node tcp 0 0 172.20.20.235:5432 0.0.0.0: LISTEN 56555/postgres tcp 0 0 127.0.0.1:6010 0.0.0.0: LISTEN 704372/sshd: argupt tcp 0 0 127.0.0.1:6011 0.0.0.0: LISTEN 712372/sshd: argupt tcp 0 0 172.20.20.235:6379 0.0.0.0: LISTEN 43528/redis-server tcp6 0 0 :::22 ::: LISTEN 902/sshd: /usr/sbin tcp6 0 0 ::1:6010 ::: LISTEN 704372/sshd: argupt tcp6 0 0 ::1:6011 ::: LISTEN 712372/sshd: argupt *tcp6 0 0 :::5000 ::: LISTEN 715259/node* tcp6 0 0 :::80 ::: LISTEN 54445/apache2 udp 0 0 127.0.0.53:53 0.0.0.0: 816/systemd-resolve udp 0 0 172.20.20.235:68 0.0.0.0: 814/systemd-network

one thing to note is that the websocket is attached to an IPv6 address / port

alexkander commented 4 years ago

I can see you are using different ports for the configurations of rest and websocket. In my previous comment I say change the port for both, not just for websocket.

// src/index.ts
if (require.main === module) {
  const port = process.env.PORT ?? 5000; ///<------- UPDATE HERE
  // Run the application
  const config = {
    rest: {
      port,
      host: process.env.HOST ?? 'localhost',
      openApiSpec: {
        // useful when used with OpenAPI-to-GraphQL to locate your application
        setServersFromRequest: true,
      },
    },
    websocket: {
      port
    }
  };
  main(config).catch(err => {
    console.error('Cannot start the application.', err);
    process.exit(1);
  });
}

Also, it is very important to run npm run clean before npm start.

Anyway I don't know how I can help you more. I still thinking there is process using that port.

d-bo commented 4 years ago

@argupta23 yeah, both http and websocket on a single port 3000. I disabled original http server started from generated lb4 cli app https://github.com/strongloop/loopback-next/issues/4044#issuecomment-663359434

argupta23 commented 4 years ago

@d-bo,

Maybe I am missing something on my end. Can I please request you to share a snippet the code of what changes need to be made?

@arondn2 I did try what you have suggested with the same results. If you want we could do an online session.

Thanks

alexkander commented 4 years ago

@argupta23 in my profile is my email

d-bo commented 4 years ago

@arondn2 confirmed, address in use:

bdo@bdo-PC:~/prj/loopback4-example-websocket-app$ npm start

> loopback-example-websocket-app@0.1.2 prestart /home/bdo/prj/loopback4-example-websocket-app
> npm run build

> loopback-example-websocket-app@0.1.2 build /home/bdo/prj/loopback4-example-websocket-app
> lb-tsc

> loopback-example-websocket-app@0.1.2 start /home/bdo/prj/loopback4-example-websocket-app
> node .

Cannot start the application. { Error: listen EADDRINUSE: address already in use 127.0.0.1:3000
    at Server.setupListenHandle [as _listen2] (net.js:1280:14)
    at listenInCluster (net.js:1328:12)
    at GetAddrInfoReqWrap.doListen [as callback] (net.js:1461:7)
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:61:10)
  code: 'EADDRINUSE',
  errno: 'EADDRINUSE',
  syscall: 'listen',
  address: '127.0.0.1',
  port: 3000 }
alexkander commented 4 years ago

@d-bo @argupta23 I will try to find out the problem

d-bo commented 4 years ago

@arondn2, @argupta23 in websocket.application.ts commented out await super.start(); and it works !

  public async start(): Promise<void> {
    await this.wsServer.start();
    //await super.start();
  }
alexkander commented 4 years ago

@d-bo fine. I don't know why it works for me. I gonna try if that works in my proyect to make a refactoring of the example.

d-bo commented 4 years ago

@arondn2 thank you for example. there are the ws workspace and ws rest controller injector solved

d-bo commented 4 years ago

@arondn2 some issues with socket rooms ...

I want to join client to the room with user id.

@ws({name: 'pzNsp', namespace: '/pz' })
export class WebSocketController {
    ...
    @ws.connect()
    connect() {
        console.log('Client connected: ', this.socket.id);
    }

    @ws.subscribe('authentication')
    async handleAuthentication(userId: string) {
        // leave default room
        this.socket.leaveAll();
        // join room with userId
        this.socket.join(`/pz#${userId}`, (err: any) => {
            this.socket.in(`/pz#${userId}`).broadcast.emit('authenticated');
        });
    }
}

It is not emitting to client. Works only with this.socket.emit('authenticated'); Is this a bug or i misunderstood the concept ?

alexkander commented 4 years ago

@d-bo this is what says the socket.io documentation:

// sending to all clients except sender
socket.broadcast.emit('broadcast', 'hello friends!');

I think If you want to indicate to the current connection is authenticated you should use this.socket.emit('authenticated');. When you use this.socket.in(`/pz#${userId}`).broadcast.emit('authenticated');the event will be sended to all connections of this user except the current connection.

d-bo commented 4 years ago

@arondn2 the point is to push notifications to user logged in from many devices, browsers. to put user into own socket.io room after successfull authentication . so i tried all the matched variants from socket.io cheatsheet this.socket.in(`/pz#${userId}`).emit('authenticated'), this.socket.to(`/pz#${userId}`).emit('authenticated') ... client is not responding at all

alexkander commented 4 years ago

@d-bo do you want to send the event to all others connections of the same authenticated user or of all users connected?

d-bo commented 4 years ago

@arondn2 to all other connections of the same authenticated user

alexkander commented 4 years ago

@d-bo I think if you are using this.socket.in(`/pz#${userId}`).emit('authenticated') or this.socket.in(`/pz#${userId}`).emit('authenticated'), a simple client connection should received the event. Make sure you have socket.on('authenticated', function(){...}) in the client.

If you want send the event to all other connections of the same authenticated user, you will need to use broadcast, and to test it you will need several connections for the same user at the same time.

You need to create a minimal repository loopback project with this problem, because there's not much I can say to find out the problem if I can't have the entire view.

sergiogaitan commented 4 years ago

Hi @arandn2 , I did what you said,but it keeps throwing me this error: Cannot start the application. TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received undefined at validateString (internal/validators.js:117:11) at Object.resolve (path.js:980:7) at new Bootstrapper (/Users/Sergio/Documents/kloustr/socketio/myIntSocketIO/my-int-socket-io/node_modules/@loopback/boot/src/bootstrapper.ts:46:24) Do you know what might I be doing wrong?

alexkander commented 4 years ago

@sergiogaitan I have say many things in this thread.. jajajajajajajaja... Please, tell me which of my comments you are talking about to try to help you.

Otherwise, did you run the example that I left?

ahmed-adly-khalil commented 4 years ago

@arondn2 Thanks for solving this issue, this example works great: https://github.com/arondn2/loopback4-example-websocket-app

d-bo commented 4 years ago

@arondn2 how to get io instance from rest / websocket controllers ? The broadcast don't comes from a socket.

alexkander commented 4 years ago
async controllerMethod(
    @ws.server() io: Server
  ): Promise<any> {
      // ...
  }

regards, @d-bo

w20k commented 4 years ago

Have anyone tried to implement the 'socket.io-client', loopback way? @arondn2, thanks your example is awesome! ;)

alexkander commented 4 years ago

hi @w20k. Here is a example how to use socket.io-client for a acceptance test. https://github.com/strongloop/loopback-next/blob/socketio/extensions/socketio/src/tests/acceptance/socketio.server.acceptance.ts. I think you need to install socket.io-client and @types/socket.io-client dependencies

w20k commented 4 years ago

Hi, @arondn2 thanks for a quick answer. Probably my issue is a bit more complicated :)

I have a bidirectional connection via WebSocket where loopback is a middleman with (currently) Socket.io server (from the example). And now I need to figure out a proper way to initiate a connection to a server using the (as I've understood SocketIoServer doesn't support it?) 'socket.io-client', and somehow make those two websockets communicate, per user session_id. Communication part is not an issue, but to subscribe for client messages and push those on server.

Or that's the only option, and I'm overthinking 😄

alexkander commented 4 years ago

@w20k I'm not sure I understand. The scheme that you are trying to explain is something like this:

  1. client (maybe a html/js page)
  2. Server middleman (Loopback4+socket.io)
  3. Main Server (Maybe Loopback4+socket.io)

And are you trying send messages from (1) to (2) and those message from (2) to (3)? For this you need server.io-client in (2)?

w20k commented 4 years ago

@arondn2 yeah, you got it right.

  1. Client (html/js SPA + socket.io-client)
  2. Server middleman (Loopback4 + socket.io)
  3. Server Main (Java + Websocket)

It's a WebSocket connection between (1) - (3), where (2) is a "Websocket manager", of some sort. And we need to send a message from (1) - (3) on login to establish a Websocket connection and back to client (3) - (1) if there were any updates.

And, yes, for this I was thinking I'd need to use 'server.io-client' in (2).

alexkander commented 4 years ago

@w20k if (3) is public or visible for (1) you can connect directly to (3). But I think (3) is private or maybe you just is required connect through (2). If this is the case, my opinion is you must implement a kind of proxy, where client in (1) connects to server in (2) and a client in (2) using socket.io-client in loopback connects to server in (3).

Also (2) must catch events from client in (1) and emit them to server in (3) and catch events from server in (3) and emit them to (1).

You could do the latter linking event by event, or you could use socket.use (documentation) for dynamically linking.