logo
  • 世界杯德国vs韩国
socket.io emit callback调用探秘

socket.io

https://socket.io/

https://socket.io/docs/

What Socket.IO is

Socket.IO is a library that enables real-time, bidirectional and event-based communication between the browser and the server. It consists of:

a Node.js server: Source | API

a Javascript client library for the browser (which can be also run from Node.js): Source | API

emit callback 用法

https://stackoverflow.com/questions/20337832/is-socket-io-emit-callback-appropriate

Recently I have been messing around with socket.io and found this interesting thing, that I can have emit function callback like this.

I start emitting on client side like this:

client.emit('eventToEmit', dataToEmit, function(error, message){

console.log(error);

console.log(message);

});

Then I can fire a callback from server-side like this:

client.on('eventToEmit', function(data, callback){

console.log(data);

callback('error', 'message');

});

Everything works fine with no errors, but I am interested if doing something like this is appropriate since I have not seen anything similar in the documentation or any example so far.

见老外的疑惑,也是本篇的主题, 为什么服务器端能够直接调用客户端设置的回调函数。

跨进程可以调用函数,真是稀奇。 类似RPC。

官方文档的解释

https://socket.io/docs/#Sending-and-getting-data-acknowledgements

Sending and getting data (acknowledgements)

Sometimes, you might want to get a callback when the client confirmed the message reception.

To do this, simply pass a function as the last parameter of .send or .emit. What’s more, when you use .emit, the acknowledgement is done by you, which means you can also pass data along:

Server (app.js)

var io = require('socket.io')(80);io.on('connection', function (socket) { socket.on('ferret', function (name, word, fn) { fn(name + ' says ' + word); });});

Client (index.html)

client-代码跟踪

https://github.com/socketio/socket.io-client

以客户端源码为研究对象。

在socket.js文件中,存在emit实现:

如下代码中, 17-20行代码中, 会将callback函数存储到本地的acks数组中, 并将基数记为 packet.id,

然后packet作为数据整体,传送的服务器端。

1 Socket.prototype.emit = function (ev) {

2 if (events.hasOwnProperty(ev)) {

3 emit.apply(this, arguments);

4 return this;

5 }

6

7 var args = toArray(arguments);

8 var packet = {

9 type: (this.flags.binary !== undefined ? this.flags.binary : hasBin(args)) ? parser.BINARY_EVENT : parser.EVENT,

10 data: args

11 };

12

13 packet.options = {};

14 packet.options.compress = !this.flags || false !== this.flags.compress;

15

16 // event ack callback

17 if ('function' === typeof args[args.length - 1]) {

18 debug('emitting packet with ack id %d', this.ids);

19 this.acks[this.ids] = args.pop();

20 packet.id = this.ids++;

21 }

22

23 if (this.connected) {

24 this.packet(packet);

25 } else {

26 this.sendBuffer.push(packet);

27 }

28

29 this.flags = {};

30

31 return this;

32 };

服务器端处理完数据后, 调用callback接口后,服务器端调用的接口为包装接口, 包装了数据为packet, 并将id打在packet上, 表示此packet为emit时候的packet对应。

服务器端数据到来后, 根据packet.id定位到 callback函数, 并将packet.data作为参数传递到callback中。

/**

* Called upon a server acknowlegement.

*

* @param {Object} packet

* @api private

*/

Socket.prototype.onack = function (packet) {

var ack = this.acks[packet.id];

if ('function' === typeof ack) {

debug('calling ack %s with %j', packet.id, packet.data);

ack.apply(this, packet.data);

delete this.acks[packet.id];

} else {

debug('bad ack %s', packet.id);

}

};

server-代码跟踪

https://github.com/socketio/socket.io

socket.js中 在onevent中 在数据的args数组之后, 添加了 acknowledge 回调函数

1 /**

2 * Called upon event packet.

3 *

4 * @param {Object} packet object

5 * @api private

6 */

7

8 Socket.prototype.onevent = function(packet){

9 var args = packet.data || [];

10 debug('emitting event %j', args);

11

12 if (null != packet.id) {

13 debug('attaching ack callback to event');

14 args.push(this.ack(packet.id));

15 }

16

17 this.dispatch(args);

18 };

19

20 /**

21 * Produces an ack callback to emit with an event.

22 *

23 * @param {Number} id packet id

24 * @api private

25 */

26

27 Socket.prototype.ack = function(id){

28 var self = this;

29 var sent = false;

30 return function(){

31 // prevent double callbacks

32 if (sent) return;

33 var args = Array.prototype.slice.call(arguments);

34 debug('sending ack %j', args);

35

36 self.packet({

37 id: id,

38 type: hasBin(args) ? parser.BINARY_ACK : parser.ACK,

39 data: args

40 });

41

42 sent = true;

43 };

44 };

在 dispatch 负责调用 emitter 原生接口 on 绑定的 事件处理函数:

/** * `EventEmitter#emit` reference. */var emit = Emitter.prototype.emit;

1 /**

2 * Dispatch incoming event to socket listeners.

3 *

4 * @param {Array} event that will get emitted

5 * @api private

6 */

7

8 Socket.prototype.dispatch = function(event){

9 debug('dispatching an event %j', event);

10 var self = this;

11 function dispatchSocket(err) {

12 process.nextTick(function(){

13 if (err) {

14 return self.error(err.data || err.message);

15 }

16 emit.apply(self, event);

17 });

18 }

19 this.run(event, dispatchSocket);

20 };

21

22 /**

23 * Sets up socket middleware.

24 *

25 * @param {Function} middleware function (event, next)

26 * @return {Socket} self

27 * @api public

28 */

29

30 Socket.prototype.use = function(fn){

31 this.fns.push(fn);

32 return this;

33 };

34

35 /**

36 * Executes the middleware for an incoming event.

37 *

38 * @param {Array} event that will get emitted

39 * @param {Function} last fn call in the middleware

40 * @api private

41 */

42 Socket.prototype.run = function(event, fn){

43 var fns = this.fns.slice(0);

44 if (!fns.length) return fn(null);

45

46 function run(i){

47 fns[i](event, function(err){

48 // upon error, short-circuit

49 if (err) return fn(err);

50

51 // if no middleware left, summon callback

52 if (!fns[i + 1]) return fn(null);

53

54 // go on to next

55 run(i + 1);

56 });

57 }

58

59 run(0);

60 };

至此实现上彻底明了了。

Copyright © 2088 1990世界杯_世界杯竞猜 - xindsw.com All Rights Reserved.
友情链接