首页 > 解决方案 > 当活动套接字只有一个时,NodeJs 无法监听 socket.io 私有通道

问题描述

我正在广播nodejs之间的连接,并且颤动......我正在向私人频道发射,并收听同一个频道。

我在 server.js 中定义 socket.io 配置,然后,我使用它的一个实例@authcontroller.js。

我遇到的问题是,当我尝试从 auth.controller 收听私人频道时,只有当活动套接字超过一个套接字时,我才会收到响应。但是,即使只有一个套接字,我也会从 server.js 立即收到响应。

下面是代码:

服务器.js

    //general dependencies
const port = process.env.PORT || 3000;
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);

const fileUpload = require('express-fileupload');

var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}))
const api = require('../api/AuthController')



var server = http.listen(3000, () => {
  console.log('server is running on port', server.address().port);
});


//routes
const routes = require('../api/routes');
routes(app,io);

//socket config
global.io = io; //added


    io.on('connection', function(socket){
      socket.on('privateChannelID', function (message) {
            console.log('message received from server.js')

    });
    });

AuthController.js:

global.io.on('connection', function (socket) {
         socket.on('privateChannelID', function (message) {
                    console.log('message received from authcontoller')

             });
             });

任何想法,我错过了什么?

提前致谢

更新

前端代码:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:adhara_socket_io/adhara_socket_io.dart';
import 'package:geocoder/geocoder.dart';

const String URI = "http://10.0.2.2:3000/";

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  _MyHomePageState();

  List<String> toPrint = ["trying to connect"];
  SocketIOManager manager;
  Map<String, SocketIO> sockets = {};
  Map<String, bool> _isProbablyConnected = {};
  bool newtripRequest = false;
  var pickupController;
  var dropoffController;
  SocketIO socketController;
  var driver = 'driver';
  String socketIdentifier;

  @override
  void initState() {
    super.initState();
    manager = SocketIOManager();
    initSocket("default");

  }

  @override
  void dispose() {
    super.dispose();
  }




  initSocket(String identifier) async {
    setState(() => _isProbablyConnected[identifier] = true);
    SocketIO socket = await manager.createInstance(SocketOptions(
        //Socket IO server URI
        URI,
        nameSpace: (identifier == "namespaced") ? "/adhara" : "/",
        //Query params - can be used for authentication
        query: {
          "auth": "--SOME AUTH STRING---",
          "info": "new connection from adhara-socketio",
          "timestamp": DateTime.now().toString()
        },
        //Enable or disable platform channel logging
        enableLogging: false,
        transports: [
          Transports.WEB_SOCKET /*, Transports.POLLING*/
        ] //Enable required transport
        ));
    setState(() {
      socketIdentifier = identifier;
    });
    socket.onConnect((data) {
      pprint("connected...");
      pprint(data);
      sendMessage('news', 'yes', socketIdentifier);
    });

    socket.onConnectError(pprint);
    socket.onConnectTimeout(pprint);
    socket.onError(pprint);
    socket.onDisconnect(pprint);
    socket.on("news", (data) => newTripRquest(data));
    socket.connect();
    sockets[identifier] = socket;
  }

  bool isProbablyConnected(String identifier) {
    return _isProbablyConnected[identifier] ?? false;
  }

  disconnect(String identifier) async {
    await manager.clearInstance(sockets[identifier]);
    setState(() => _isProbablyConnected[identifier] = false);
  }

  sendMessage(privateChannel, messageBody, identifier) {
    //pprint("sending message from '$identifier'...");
    sockets[identifier].emit(driverChannel, [
      {'response' : messageBody}]);
    //pprint("Message emitted from '$identifier'...");
  }

  pprint(data) {
    setState(() {
      if (data is Map) {
        data = json.encode(data);
      }
      print(data);
      toPrint.add(data);
    });
  }

更新:

AuthConroller.js

    'use strict'

    const jwt = require("jsonwebtoken");
    const bcrypt = require("bcryptjs");

    var authController = {

    findNearestDriver: function (req, res) {


        var query = Driver.find({
            'geo': {
                $near: [
                    req.body.lat,
                    req.body.lng
                ],
                // $maxDistance: distance

            }
        });
        query.exec(async function (err, driver) {
            if (err) {
                console.log(err);
                throw err;
            }

            if (!driver) {
                res.json({});
            } else {



                for (let i = 0; i < driver.length; i++) {

                    global.io.emit(`news${driver[i]._id}`, { 
               pickupLat: req.body.lat, pickupLng: req.body.lng, dropOffLat: 
               req.body.dropLat, dropOffLng: req.body.dropLng });
                    global.io.on('connection', function (socket) {
                        socket.on(`news${driver[i]._id}`, function (message) {
                            if (message.message === 'Accept') {
                                resultRecieved = true
                                console.log('message received')
                            }
                        });
                    });


                }


                res.json(driver);
            }
        });
    }
}

module.exports = authController;

标签: node.jssocket.io

解决方案


在 里面authController.js,你有一个:

global.io.on('connection', ...)

嵌入在其他一些功能findNearestDriver()中。findNearestDriver()因此,该连接事件处理程序在调用并执行函数之后以及其中的查询完成之前不会处于活动状态。这意味着您将错过connection调用该函数之前发生的任何事件。

它也在一个for循环中,这几乎不是您想要的,因为它只会创建相同但重复的事件处理程序。

像这样的事件处理程序global.io.on('connection', ...)通常应该靠近模块的顶层并在模块初始化时进行初始化,而不是在可以多次运行的地方。


请注意,您也在加载authController.js之前global.io有一个值。这意味着您不能只将 移动global.io.on('connection', ...)到模块的顶层,除非您也将require('authController.js')AFTERglobal.io设置为 AFTER 。

但是,我认为您需要对这部分的authController.js工作方式进行一些重新设计,因为connection事件处理程序需要位于模块的顶层,而不是深埋在for函数内部的循环中。

这很好地说明了在涉及异步操作时使用全局变量进行通信的问题之一。它会产生令人沮丧的时间或加载顺序问题。IMO,最好io在模块构造函数中显式传递给这个模块并从那里使用它,而不是从全局使用它。这将使您不太可能以io在初始化之前尝试使用的方式对其进行编码。


推荐阅读