javascript - NodeJS & Socket.IO: Hantar acara permintaan dan dapatkan respons, bila/di mana saya perlu mengikat pendengar?


Saya sedang tertanya-tanya tentang apakah amalan pengaturcaraan terbaik dalam kes ini:

Katakan saya mempunyai klien yang disambungkan ke pelayan saya. Dan klien ini meminta pelayan untuk pengesahan dengan acara auth dan nama penggunanya.

socket = io();
socket.emit('auth', "John");

Dalam kes mudah ini, pelayan bertindak balas dengan acara auth_succeed dengan ID pengguna.

io.on('connection', function(socket) {
    socket.on('auth', function(username) {
        socket.emit('auth_succeed', id);
    }
}

Jadi soalan saya ialah, bila atau di mana saya harus mengikat pendengar untuk acara auth_succeed dalam klien? Terdapat dua cara untuk saya:

Sebelum memancarkan, yang memastikan, saya rasa, bahawa acara respons akan sentiasa dikendalikan dengan betul, tetapi membawa kepada sedikit kod spaghetti. Contoh:

socket = io();
socket.on('auth_succeed', function(id){
    //Do some post-auth stuff here
}); 
socket.emit('auth', "John");

Atau selepas menghantar, yang membawa kepada kod yang lebih bersih, tetapi mungkin, saya rasa sekali lagi, terlepas acara jika dihantar dengan cukup cepat. Contoh:

socket = io();
socket.emit('auth', "John");
socket.on('auth_succeed', function(id){
    //Do some post-auth stuff here
}); 

Apa pendapat anda tentang masalah itu?

Jawapan #1

Oleh kerana respons daripada emit sepatutnya bersifat tak segerak, dan JS sisi klien bersifat segerak secara semula jadi, ikatan socket.on('auth_succeed') akan berlaku sebelum panggilan balas daripada acara auth.

Aliran berikut akan berlaku pada pelanggan...

// EXECUTION SCOPE BEGINS
...
// this will send a message to the server
// the message and/or response will be sent asynchronously
socket.emit('auth', 'John'); 
// so your code will continue before you get anything from the server

//which means the following binding will happen before any response to the above emit
socket.on('auth_succeed', function(id){
  //... handle message from server ...
});
...
// EXECUTION SCOPE ENDS

Pada suatu ketika selepas skop/fungsi yang melingkungi selesai melaksanakan, acara 'auth_succeed' akan dicetuskan.

Anda mungkin juga ingin mempertimbangkan untuk memecahkan pengendali acara anda...

socket.on('auth_succeed', onAuthSucceed.bind(null, socket));
socket.emit('auth', 'john');

// ... elsewhere ...

function onAuthSucceed(socket, id) {
  // handle message from server
}

Ini akan mengurangkan bunyi dengan pengikatan anda, dan acara isyarat anda, tanpa mengira sama ada anda memilih untuk mengikat atau memancarkan dahulu.

Dengan menjadikan fungsi memerlukan apa sahaja yang ia perlukan, dan menggunakan binding untuk acara tersebut, kaedah yang dimaksudkan boleh berada dalam fail/modul yang berasingan, dan lebih mudah diuji secara berasingan.

Jawapan #2

Dokumen socket.io, dalam Migrating from 0.9, melakukan pekerjaan yang hebat dalam menunjukkan cara mengesahkan soket dalam 1.0.

Satu contoh untuk anda adalah:

Anda perlu mencipta middleware pengesahan untuk mengendalikannya.

Buat pengawal yang dipanggil authorization.js:

/**
 * Socket.io middleware that that authorizes clients
 * @param {object} socket
 * @param {function} next
 */
module.exports = function(socket, next) {

    // do some custom auth here
    var handshakeData = socket.request;

    // make sure the handshake data looks good with some
    // custom logic
    // if doesn't you can do this:
    // next(new Error('not authorized');
    // else just call next

    next();
}

Dalam index.js, hanya lampirkan middleware dengan io.use:

var authorizeSockets = require('./controllers/authorization');
io.use(authorizeSockets);

Menggunakan io.set telah dihentikan penggunaannya untuk io.use() sejak socket.io 1.0.

Anda kemudian boleh memberitahu pelanggan bahawa mereka telah berjaya mengesahkan dan bersambung dengan menghantar auth_suceed pada acara sambungan kerana dengan perisian perantara baru yang dipasang, soket tidak akan dapat bersambung melainkan mereka berjaya mengesahkan.

io.on('connection', function(socket){
    socket.emit('auth_succeed', yourPayloadHere);
});