stf源码解读之ControlService-sendTwoWay(socket.io)双向通信

前言

本文主要解读ControlService的sendTwoWay和sendOneWay的区别,sendTwoWay函数怎么实现双向通信的。

touchDown与install

我们看到 res/app/components/stf/screen/screen-directive.js有这样的调用方式

control.touchDown(nextSeq(), 0, scaled.xP, scaled.yP, pressure)

其调用的res/app/components/stf/control/control-service.js

this.touchDown = function(seq, contact, x, y, pressure) {
      sendOneWay('input.touchDown', {
        seq: seq
      , contact: contact
      , x: x
      , y: y
      , pressure: pressure
      })
    }

而 res/app/components/stf/install/install-service.js

control.install({
            href: installation.href
          , manifest: installation.manifest
          , launch: installation.launch
          })
          .progressed(function(result) {
            installation.update(50 + result.progress / 2, result.lastData)
          })
	      .then(function(result) {
	        
	      })

首先跟touchdown的区别是control.install可以后续通过progressed获取进度,通过then获取result。那为什么control.install可以发出消息后获取到后台返回的信息呢?
然后再看res/app/components/stf/control/control-service里面

this.install = function(options) {
      return sendTwoWay('device.install', options)
    }

出乎意料的简单,这也看不出哪能返回后台处理后的信息呀。

关键点就是 control.install调用的是sendTwoWay(), 而control.touchDown调用的是sendOneWay().

sendOneWay与sendTwoWay

而sendTwoWay()和sendOneWay()再看下源码:

function ControlService(target, channel) {
    function sendOneWay(action, data) {
      socket.emit(action, channel, data)
    }

    function sendTwoWay(action, data) {
      var tx = TransactionService.create(target)
      socket.emit(action, channel, tx.channel, data)
      return tx.promise
    }
    ......
}

sendOneWay,了解过socket.io的用法的都知道 socket.emit是发消息给后台, 要想接收后台发回的信息是要socket.on('xxxx,xxxx)的。 而sendOneWay只有socket.emit,没有socket.on(),所以只是单向发消息而已。

那sendTwoWay是怎么双向通信的呢? 它多了个 TransactionService, socket.emit多传了个 tx.channel的参数,而且返回的是tx.promise。

疑点TransactionService

那我们就进 res/app/components/stf/transaction/transaction-service.js看下

module.exports = function TransactionServiceFactory(socket, TransactionError) {
  var transactionService = {}

  function createChannel() {
    return 'tx.' + uuid.v4()
  }
  ......
  //sendTwoWay调用TransactionService.create(target)生成一个tx实例
  transactionService.create = function(target, options) {
    if (options && !options.result) {
      options.result = TransactionResult
    }

    if (Array.isArray(target)) {
      return new MultiTargetTransaction(target, options || {
        result: DeviceTransactionResult
      , id: 'serial'
      })
    }
    else {//target唯一,生成SingleTargetTransaction对象
      return new SingleTargetTransaction(target, options || {
        result: DeviceTransactionResult
      , id: 'serial'
      })
    }
  }

那追踪看下 SingleTargetTransaction

function SingleTargetTransaction(target, options) {
    var result = new options.result(target)
    var pending = new PendingTransactionResult(result)
    var channel = createChannel()

    function doneListener(someChannel, data) {
      if (someChannel === channel) {
        pending.done(data)
      }
    }

    function progressListener(someChannel, data) {
      if (someChannel === channel) {
        pending.progress(data)
      }
    }

    function cancelListener(someChannel, data) {
      if (someChannel === channel) {
        pending.cancel(data)
      }
    }

	//监听了tx.done消息类型
    socket.on('tx.done', doneListener)

	//监听了tx.progress消息类型
    socket.on('tx.progress', progressListener)
    socket.on('tx.cancel', cancelListener)

    this.channel = channel
    this.result = result
    this.results = [result]

	//并且有promise属性,progressed和then返回了结果。
    this.promise = pending.promise
      .finally(function() {
        socket.removeListener('tx.done', doneListener)
        socket.removeListener('tx.progress', progressListener)
        socket.removeListener('tx.cancel', cancelListener)
        socket.emit('tx.cleanup', channel)
      })
      .progressed(function() {
        return result
      })
      .then(function() {
        return result
      })
  }

那control.install接收tx.promise就包含了progressed和then的返回result。

所以就看 后台是不是发送的tx.done消息就行了。 这不完成了双向消息打通。

后台的消息发哪去了

install的后台程序是lib/units/device/plugins/install.js

//安装完成
push.send([
            channel
          , reply.okay('INSTALL_SUCCEEDED')
          ])

//安装进度
push.send([
          channel
        , reply.progress(data, progress)
        ])

追踪下reply的okay和progress函数
lib/wire/util.js

reply: function(source) {
    var seq = 0
    return {
      okay: function(data, body) {
        return wireutil.envelope(new wire.TransactionDoneMessage(
          source
        , seq++
        , true
        , data === null ? null : (data || 'success')
        , body ? JSON.stringify(body) : null
        ))
      }
    , progress: function(data, progress) {
        return wireutil.envelope(new wire.TransactionProgressMessage(
          source
        , seq++
        , data
        , ~~progress
        ))
      }
    }
  }

一个是包装成了TransactionDoneMessage,一个是TransactionProgressMessage。

那看下谁接收了这两个message.
lib/units/processor/index.js

	.on(wire.TransactionProgressMessage, function(channel, message, data) {
	      appDealer.send([channel, data])
	    })
    .on(wire.TransactionDoneMessage, function(channel, message, data) {
      appDealer.send([channel, data])
    })

然后lib/units/websocket/index.js

	.on(wire.TransactionProgressMessage, function(channel, message) {
	        socket.emit('tx.progress', channel.toString(), message)
	      })
      .on(wire.TransactionDoneMessage, function(channel, message) {
        socket.emit('tx.done', channel.toString(), message)
      })

我们看到了tx.progress和tx.done消息类型,正是前面TransactionService里监听处理的,然后以tx.promise的方式将progress和done/success传给了前端对control.install的调用。所以control.install可以再接Progressed()拿到进度数据,接then获取到success数据。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值