Node.js如何在Express路由中使用socket.io

10
在我的node.js脚本中,我试图在express路由中使用socket.io。我找到了许多类似的问题,并尝试按照建议实施解决方案,但没有成功。可能是因为我对express路由的理解不够。我遵循以下链接: 如何在node.js中使用socket.io和express路由 在expressjs路由中使用socket.io而不是在主服务器.js文件中使用 这是我的app.js文件。
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);

const PORT = 3000;
server.listen(PORT);
console.log('Server is running');
var api = require('./routes/api');

//app.use('/api', api);
app.use('/api', (req, res) => {
res.sendFile(__dirname + '/api.html');
});

app.get('/', (req, res) => {
   res.send("this is home location");
});

并将位于./routes文件夹中的api.js路由文件进行路由

var express = require('express');
var router = express.Router();
var fs = require("fs");
var bodyParser = require('body-parser');

const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);

console.log("inside api route");

router.get('/', function(req, res, next) {
console.log("api route called");

const connections = [];
var jsonobj = [{name:"john",score:345},{name:"paul",score:678}]

io.sockets.on('connection',(socket) => {
    connections.push(socket);
    console.log(' %s sockets is connected', connections.length); // this is not printing

    socket.on('disconnect', () => {
       connections.splice(connections.indexOf(socket), 1);
    });

    socket.emit('server message', jsonobj);

 }); 
    //res.send(jsonobj) 
});

module.exports = router;
Socket.emit在我正在使用的路由上呈现的HTML页面上没有显示数据。我的HTML代码如下:
// api.html
<!DOCTYPE html>
<html lang="en">

<body>
   <div class="container">
      <h1 class="jumbotron">
         Node js Socket io with  socket route example
      </h1>
      <div class="results">results</div>      
   </div>
   <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
   <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
   <script>
    jQuery(document).ready(function() {
       var socket = io.connect();
       var jsondata = "";

       socket.on('server message', function(data){
         console.log('got data from server',data)
         jsondata = JSON.stringify(data);
         //console.log('jsondata',jsondata)
         $('.results').html(jsondata);
       });
    });   
 </script>
</body>
</html>
请建议我该如何在HTML页面中获取路由套接字数据。 谢谢。

1
尝试调用 http://localhost:3000/api/api/ - Vikash_Singh
谢谢@Vikash。是的,它起作用了。但是为什么?我如何使其适用于“http://localhost:3000/api”?请建议。 - usersam
1
因为在 app.use 和 router.get 两个地方都使用了相同的路由字符串 '/api'。 - Vikash_Singh
嗨@Vikash Singh,这个不像预期的那样工作。它没有在HTML页面上显示套接字数据。 - usersam
5个回答

6

好的,让我们尝试理解为什么您需要在路由中通过套接字发送数据。 WebSocket 旨在异步发送数据,不需要客户端发出请求。如果客户端已经发出 HTTP 请求,则可以将数据直接发送到 HTTP 响应中。

但是,有些使用情况需要根据其他用户请求的操作向某个 WebSocket 通道发送数据。如果是这种情况,则有多种方法可以实现。一种清晰的方式是使用事件驱动架构

尝试类似以下内容的东西... 在下面找到我的内联注释-

const express = require('express');
const router = express.Router();
const fs = require("fs");
const bodyParser = require('body-parser');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);

// move the socket connection outside of the route controller
// you must register the event listeners before anything else
const connections = [];

io.sockets.on('connection', (socket) => {
    connections.push(socket);
    console.log(' %s sockets is connected', connections.length); // this is not printing

    socket.on('disconnect', () => {
        connections.splice(connections.indexOf(socket), 1);
    });
});


// Event emitter for sending and receving custom events
const EventEmitter = require('events').EventEmitter;
const myEmitter = new EventEmitter();

myEmitter.on('my-event', function (jsonobj) {
    // do something here like broadcasting data to everyone
    // or you can check the connection with some logic and 
    // only send to relevant user
    connections.forEach(function(socket) {
        socket.emit('server message', jsonobj);
    });
});

router.get('/some-route', function (req, res, next) {  
    const jsonobj = [{ name: "john", score: 345 }, { name: "paul", score: 678 }]

    // emit your custom event with custom data
    myEmitter.emit('my-event', jsonobj);

    // send the response to avoid connection timeout
    res.send({ok: true});
});

module.exports = router;

5
乍一看,似乎你在两个地方都声明了URL前缀,一次在 app.js,另一次在 api.js。
尝试访问 localhost:port/api/api
如果是这种情况,请更改
router.get('/api', function(req, res, next){

router.get('/', function(req, res, next){
这将允许你访问 localhost:port/api 并访问你的端点。

嗨@user3424216,我很遗憾地说它仍然不起作用。我得到的解决方案响应是因为res.send(jsonobj),而不是socket。我尝试在HTML中获取socket,但它没有显示。我使用了“app.use('/api', (req, res) => { res.sendFile (__dirname + '/api.html'); });”,我也会相应地编辑问题。 - usersam

0

我自己也刚开始理解这个,但我觉得你已经接近了。

在你的app.js文件末尾添加以下内容:

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);

const PORT = 3000;
server.listen(PORT);
console.log('Server is running');
var api = require('./routes/api');

//app.use('/api', api);
app.use('/api', (req, res) => {
res.sendFile(__dirname + '/api.html');
});

app.get('/', (req, res) => {
   res.send("this is home location");
});

app.set("socketio", io);    // <== this line

将变量"io"存储在"socketio"中。您可以在其他任何".js"文件中获取它。

var express = require('express');
var router = express.Router();
var fs = require("fs");
var bodyParser = require('body-parser');

const app = express();
const server = require('http').createServer(app);
//const io = require('socket.io').listen(server); // <== change this
const io = app.get("socketio"); // <== to this

console.log("inside api route");

router.get('/', function(req, res, next) {
console.log("api route called");

const connections = [];
var jsonobj = [{name:"john",score:345},{name:"paul",score:678}]

io.sockets.on('connection',(socket) => {
    connections.push(socket);
    console.log(' %s sockets is connected', connections.length); // this is not printing

    socket.on('disconnect', () => {
       connections.splice(connections.indexOf(socket), 1);
    });

    socket.emit('server message', jsonobj);

 }); 
    //res.send(jsonobj) 
});

module.exports = router;

对于其他 ".js" 文件中所需的任何其他变量,您也应该这样做。

另请注意,在您的文件中,您正在重新设置变量。最好像我向您展示的那样使用 "io"。在其他文件中我设置的唯一变量是 "app" 本身。

希望这可以帮助到您...


嗨@WLGfx。app.set(“socketio”,io);这行代码看起来很吸引人。我尝试了这个解决方案,但它没有起作用。我注意到它没有进入路由器部分。“api route called”在“http://localhost:3000/api”上没有在控制台中显示。 - usersam

0

您尝试从单个项目的两个不同位置创建和启动服务器,这很不方便。您只需要进行一些清理,就可以解决问题。

app.js

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);


// Listen to sockets here instead of listening in routes/api.js
const connections = [];
var jsonobj = [{name:"john",score:345},{name:"paul",score:678}]

io.sockets.on('connection',(socket) => {
  connections.push(socket);
  console.log(' %s sockets is connected', connections.length); // this is not printing

  socket.on('disconnect', () => {
     connections.splice(connections.indexOf(socket), 1);
  });

  socket.emit('server message', jsonobj);

});

const PORT = 3000;
server.listen(PORT);
console.log('Server is running');
var api = require('./routes/api');

//app.use('/api', api);
app.use('/api', (req, res) => {
res.sendFile(__dirname + '/api.html');
});

app.get('/', (req, res) => {
   res.send("this is home location");
});

routes/api.js

var express = require('express');
var router = express.Router();
var fs = require("fs");
var bodyParser = require('body-parser');

// Comment these out
// const app = express();
// const server = require('http').createServer(app);
// const io = require('socket.io').listen(server);

console.log("inside api route");

router.get('/', function(req, res, next) {
console.log("api route called");

  // Comment these out

  // const connections = [];
  // var jsonobj = [{name:"john",score:345},{name:"paul",score:678}]

  // io.sockets.on('connection',(socket) => {
  //     connections.push(socket);
  //     console.log(' %s sockets is connected', connections.length); // this is not printing

  //     socket.on('disconnect', () => {
  //        connections.splice(connections.indexOf(socket), 1);
  //     });

  //     socket.emit('server message', jsonobj);

  //  }); 
      //res.send(jsonobj) 
});

module.exports = router;

请将您的 api.html 文件保持不变。希望这能帮到您。


0
本地主机 基本上,我们创建了一个简单的中间件:
const { createServer } = require("http"); // you can use https as well
const express = require("express");
const socketIo = require("socket.io");

const app = express();
const server = createServer(app);
const io = socketIo(server, { cors: { origin: "*" } }); // you can change the cors to your own domain

app.use((req, res, next) => {
  req.io = io;
  return next();
});

// Now all routes & middleware will have access to req.io

app.use("/api", require("./routes/api")); // this file's express.Router() will have the req.io too.

server.listen(3000, () => console.log(`Server started!`));

这是一个比将io作为函数参数传递给所需的导入然后由路由使用的更好的解决方案。
api.js文件如何利用io?看看这个例子,
const express = require("express");

const Router = express.Router();

// Very simple example
Router.post("/new-message", (req, res) => {
  // You can do validation or database stuff before emiting
  req.io.emit("new-message", { content: req.body.content });
  return res.send({ success: true });
});

module.exports = Router;

您可以管理多个套接字、房间,甚至命名空间。
此解决方案由:Aaryan 提供。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接