javascript - Mengirim pesan websocket dari rute


Saya sedang mencoba mengatur server saya dengan websockets sehingga ketika saya memperbarui sesuatu melalui rute saya, saya juga bisa mengirim pesan websocket ketika sesuatu di rute itu diperbarui.

Ideanya adalah menyimpan sesuatu ke Mongo db saya ketika seseorang mengakses rute /add-team-member misalnya, lalu mengirim pesan ke semua orang yang terhubung melalui websocket dan merupakan bagian dari ruangan websocket yang sesuai dengan tim tersebut.

Saya telah mengikuti dokumentasi untuk socket.io untuk mengatur aplikasi saya dengan cara berikut:

App.js

// there's a lot of code in here which sets what to use on my app but here's the important lines

const app = express();
const routes = require('./routes/index');

const sessionObj = {
    secret: process.env.SECRET,
    key: process.env.KEY,
    resave: false,
    saveUninitialized: false,
    store: new MongoStore({ mongooseConnection: mongoose.connection }),
             secret : 'test',
             cookie:{_expires : Number(process.env.COOKIETIME)}, // time im ms    
}

app.use(session(sessionObj));
app.use(passport.initialize());
app.use(passport.session());

module.exports = {app,sessionObj};

mulai.js

const mongoose = require('mongoose');
const passportSocketIo = require("passport.socketio");
const cookieParser = require('cookie-parser');

// import environmental variables from our variables.env file
require('dotenv').config({ path: 'variables.env' });

// Connect to our Database and handle an bad connections
mongoose.connect(process.env.DATABASE);

// import mongo db models
require('./models/user');
require('./models/team');

// Start our app!
const app = require('./app');
app.app.set('port', process.env.PORT || 7777);

const server = app.app.listen(app.app.get('port'), () => {
  console.log(`Express running → PORT ${server.address().port}`);
});

const io = require('socket.io')(server);

io.set('authorization', passportSocketIo.authorize({
  cookieParser: cookieParser,
  key:         app.sessionObj.key,       // the name of the cookie where express/connect stores its session_id 
  secret:      app.sessionObj.secret,    // the session_secret to parse the cookie 
  store:       app.sessionObj.store,        // we NEED to use a sessionstore. no memorystore please 
  success:     onAuthorizeSuccess,  // *optional* callback on success - read more below 
  fail:        onAuthorizeFail,     // *optional* callback on fail/error - read more below 
}));


function onAuthorizeSuccess(data, accept){}

function onAuthorizeFail(data, message, error, accept){}

io.on('connection', function(client) {  
  client.on('join', function(data) {
      client.emit('messages',"server socket response!!");
  });

  client.on('getmessage', function(data) {
    client.emit('messages',data);
});  

});

Masalah saya adalah saya memiliki banyak aksi penyimpanan Mongo DB yang sedang berjalan di file ./routes/index saya dan saya ingin bisa mengirim pesan dari rute saya daripada dari akhir start.js tempat socket.io terhubung.

Apakah ada cara agar saya bisa mengirim pesan websocket dari file ./routes/index saya meskipun IO diatur lebih jauh di start.js?

misalnya sesuatu seperti ini:

router.get('/add-team-member', (req, res) => {
  // some io.emit action here
});

Mungkin saya perlu memindahkan tempat saya menginisialisasi hal-hal socket.io tapi belum bisa menemukan dokumentasi tentang ini atau mungkin saya sudah bisa mengakses socket.io dari rute entah bagaimana?

Terima kasih dan menghargai bantuanmu, beritahu aku jika ada yang tidak jelas!

Jawaban #1

Seperti disebutkan di atas, io berada dalam lingkup global Anda. Jika Anda melakukan

router.get('/add-team-member', (req, res) => {
    io.sockets.emit('AddTeamMember');
});

Kemudian setiap klien yang terhubung, jika mendengarkan event AddTeamMember itu, akan menjalankan fungsi .on yang terkait pada klien masing-masing. Ini mungkin solusi yang termudah, dan kecuali Anda mengharapkan gelombang pengguna yang sangat besar tanpa rencana untuk load balancing, ini seharusnya cukup untuk saat ini.

Alternatif lain yang bisa kamu pilih: library socket.io memiliki fungsi rooms di mana kamu bisa bergabung dan mengirim menggunakan objek io itu sendiri https://socket.io/docs/rooms-and-namespaces/ jika kamu punya bakat di bidang ini, itu akan terlihat seperti ini:

io.sockets.in('yourroom').broadcast('AddTeamMember');

Ini pada dasarnya akan melakukan hal yang sama seperti yang di atas, hanya saja alih-alih menyiarkan ke setiap klien, ini hanya akan menyiarkan ke mereka yang eksklusif di ruangan itu. Anda harus pada dasarnya mencari cara untuk memasukkan soket pengguna itu ke dalam ruangan //sebelum// mereka membuat permintaan get, atau dengan kata lain, membuat mereka eksklusif. Dengan cara itu Anda dapat mengurangi beban yang harus ditangani server Anda setiap kali permintaan rute itu dibuat.

Terakhir, jika tidak ada opsi di atas yang berhasil untuk Anda, dan Anda benar-benar harus mengirim ke klien tunggal itu saat mereka memulai, maka ini akan menjadi berantakan, karena Anda harus memiliki semacam identitas untuk orang itu, dan karena Anda tidak memiliki referensi, Anda harus menyimpan semua socket Anda saat koneksi, lalu membuat perbandingan. Saya tidak sepenuhnya merekomendasikan sesuatu seperti ini, karena, yah, saya belum pernah mengujinya, dan tidak tahu jenis konsekuensi apa yang bisa terjadi, tetapi ini adalah inti dari ide yang saya miliki:

app.set('trust proxy', true)
var SOCKETS = []
io.on('connection', function(client) {
  SOCKETS.push(client);
  client.on('join', function(data) {
    client.emit('messages',"server socket response!!");
  });

  client.on('getmessage', function(data) {
    client.emit('messages',data);
  });
});

router.get('/add-team-member', (req, res) => {
    for (let i=0; i< SOCKETS.length; i++){
        if(SOCKETS[i].request.connection.remoteAddress == req.ip)
          SOCKETS[i].emit('AddTeamMember');
    }
});

Ingat, jika kamu memang mengambil jalur ini, kamu akan perlu menjaga array itu ketika pengguna terputus, dan jika kamu melakukan manajemen sesi, itu akan menjadi sangat rumit dengan sangat cepat.

Semoga berhasil, beri tahu kami hasilmu.

Jawaban #2

Ya, itu mungkin, Anda hanya perlu melampirkan instance dari socket.io selama Anda menerima permintaan di server Anda. Melihat file start.js Anda, Anda hanya perlu mengganti fungsi-fungsi Anda sebagai berikut:

// Start our app!
const app = require('./app');
app.app.set('port', process.env.PORT || 7777);
const io = require('socket.io')(app.app);

const server = app.app.listen(app.app.get('port'), () => {
server.on('request', function(request, response){
    request.io = io;
}
console.log(`Express running → PORT ${server.address().port}`);
});

sekarang ketika Anda menerima sebuah event dan ingin mengirimkan beberapa pesan ke klien, Anda dapat menggunakan instance io Anda dari objek permintaan.

router.get('/add-team-member', (req, res) => {
    req.io.sockets.emit('addteammember', {member: 6});
    //as you are doing a broadcast you just need broadcast msg
    ....
    res.status(200)
    res.end()
});

Dengan melakukan itu saya juga dapat mengintegrasikan dengan kerangka uji seperti mocha, dan menguji event yang dipancarkan juga...

Saya melakukan beberapa integrasi seperti itu, dan menurut pengalaman saya, hal terakhir yang dilakukan adalah mengirim pesan ke instance di socket.

Sebagai praktik yang baik, pada awal fungsi middleware yang saya miliki, saya melakukan validasi data, sanitasi data, dan membersihkan data. Berikut adalah contoh kerja saya:

var app = require('../app');
var server = require('http').Server(app);
var io = require('socket.io')(server);

io.on('connection', function(client) {
        client.emit('connected');
        client.on('disconnect', function() {
            console.log('disconnected', client.id);
        });
});

server.on('request', function(request, response) {
    request.io = io;
});

pg.initialize(app.config.DATABASEURL, function(err){
  if(err){
    throw err;
  }

  app.set('port', process.env.PORT || 3000);

    var server1 = server.listen(app.get('port'), function(){
    var host = 'localhost';
    var port = server1.address().port;

    console.log('Example app listening at http://%s:%s', host, port);
  });
});