• frp内网穿透-通过公网IP访问局域网stf(延迟太高了)


    原文地址:https://testerhome.com/articles/22617

    云服安全组需要放开对应的端口访问权限即可

     需要优化sft的stream.js文件内容,修改后最新文件如下:

      1 var util = require('util')
      2 var gm = require('gm').subClass({imageMagick: true})
      3 var Promise = require('bluebird')
      4 var syrup = require('@devicefarmer/stf-syrup')
      5 var WebSocket = require('ws')
      6 var uuid = require('uuid')
      7 var EventEmitter = require('eventemitter3')
      8 var split = require('split')
      9 var adbkit = require('@devicefarmer/adbkit')
     10 
     11 var logger = require('../../../../util/logger')
     12 var lifecycle = require('../../../../util/lifecycle')
     13 var bannerutil = require('./util/banner')
     14 var FrameParser = require('./util/frameparser')
     15 var FrameConfig = require('./util/frameconfig')
     16 var BroadcastSet = require('./util/broadcastset')
     17 var StateQueue = require('../../../../util/statequeue')
     18 var RiskyStream = require('../../../../util/riskystream')
     19 var FailCounter = require('../../../../util/failcounter')
     20 
     21 module.exports = syrup.serial()
     22   .dependency(require('../../support/adb'))
     23   .dependency(require('../../resources/minicap'))
     24   .dependency(require('../util/display'))
     25   .dependency(require('./options'))
     26   .define(function(options, adb, minicap, display, screenOptions) {
     27     var log = logger.createLogger('device:plugins:screen:stream')
     28 
     29     function FrameProducer(config) {
     30       EventEmitter.call(this)
     31       this.actionQueue = []
     32       this.runningState = FrameProducer.STATE_STOPPED
     33       this.desiredState = new StateQueue()
     34       this.output = null
     35       this.socket = null
     36       this.pid = -1
     37       this.banner = null
     38       this.parser = null
     39       this.frameConfig = config
     40       this.readable = false
     41       this.needsReadable = false
     42       this.failCounter = new FailCounter(3, 10000)
     43       this.failCounter.on('exceedLimit', this._failLimitExceeded.bind(this))
     44       this.failed = false
     45       this.readableListener = this._readableListener.bind(this)
     46     }
     47 
     48     util.inherits(FrameProducer, EventEmitter)
     49 
     50     FrameProducer.STATE_STOPPED = 1
     51     FrameProducer.STATE_STARTING = 2
     52     FrameProducer.STATE_STARTED = 3
     53     FrameProducer.STATE_STOPPING = 4
     54 
     55     FrameProducer.prototype._ensureState = function() {
     56       if (this.desiredState.empty()) {
     57         return
     58       }
     59 
     60       if (this.failed) {
     61         log.warn('Will not apply desired state due to too many failures')
     62         return
     63       }
     64 
     65       switch (this.runningState) {
     66       case FrameProducer.STATE_STARTING:
     67       case FrameProducer.STATE_STOPPING:
     68         // Just wait.
     69         break
     70       case FrameProducer.STATE_STOPPED:
     71         if (this.desiredState.next() === FrameProducer.STATE_STARTED) {
     72           this.runningState = FrameProducer.STATE_STARTING
     73           this._startService().bind(this)
     74             .then(function(out) {
     75               this.output = new RiskyStream(out)
     76                 .on('unexpectedEnd', this._outputEnded.bind(this))
     77               return this._readOutput(this.output.stream)
     78             })
     79             .then(function() {
     80               return this._waitForPid()
     81             })
     82             .then(function() {
     83               return this._connectService()
     84             })
     85             .then(function(socket) {
     86               this.parser = new FrameParser()
     87               this.socket = new RiskyStream(socket)
     88                 .on('unexpectedEnd', this._socketEnded.bind(this))
     89               return this._readBanner(this.socket.stream)
     90             })
     91             .then(function(banner) {
     92               this.banner = banner
     93               return this._readFrames(this.socket.stream)
     94             })
     95             .then(function() {
     96               this.runningState = FrameProducer.STATE_STARTED
     97               this.emit('start')
     98             })
     99             .catch(Promise.CancellationError, function() {
    100               return this._stop()
    101             })
    102             .catch(function(err) {
    103               return this._stop().finally(function() {
    104                 this.failCounter.inc()
    105                 this.emit('error', err)
    106               })
    107             })
    108             .finally(function() {
    109               this._ensureState()
    110             })
    111         }
    112         else {
    113           setImmediate(this._ensureState.bind(this))
    114         }
    115         break
    116       case FrameProducer.STATE_STARTED:
    117         if (this.desiredState.next() === FrameProducer.STATE_STOPPED) {
    118           this.runningState = FrameProducer.STATE_STOPPING
    119           this._stop().finally(function() {
    120             this._ensureState()
    121           })
    122         }
    123         else {
    124           setImmediate(this._ensureState.bind(this))
    125         }
    126         break
    127       }
    128     }
    129 
    130     FrameProducer.prototype.start = function() {
    131       log.info('Requesting frame producer to start')
    132       this.desiredState.push(FrameProducer.STATE_STARTED)
    133       this._ensureState()
    134     }
    135 
    136     FrameProducer.prototype.stop = function() {
    137       log.info('Requesting frame producer to stop')
    138       this.desiredState.push(FrameProducer.STATE_STOPPED)
    139       this._ensureState()
    140     }
    141 
    142     FrameProducer.prototype.restart = function() {
    143       switch (this.runningState) {
    144       case FrameProducer.STATE_STARTED:
    145       case FrameProducer.STATE_STARTING:
    146         this.desiredState.push(FrameProducer.STATE_STOPPED)
    147         this.desiredState.push(FrameProducer.STATE_STARTED)
    148         this._ensureState()
    149         break
    150       }
    151     }
    152 
    153     FrameProducer.prototype.updateRotation = function(rotation) {
    154       if (this.frameConfig.rotation === rotation) {
    155         log.info('Keeping %d as current frame producer rotation', rotation)
    156         return
    157       }
    158 
    159       log.info('Setting frame producer rotation to %d', rotation)
    160       this.frameConfig.rotation = rotation
    161       this._configChanged()
    162     }
    163 
    164     FrameProducer.prototype.updateProjection = function(width, height) {
    165       if (this.frameConfig.virtualWidth === width &&
    166           this.frameConfig.virtualHeight === height) {
    167         log.info(
    168           'Keeping %dx%d as current frame producer projection', width, height)
    169         return
    170       }
    171 
    172       log.info('Setting frame producer projection to %dx%d', width, height)
    173       this.frameConfig.virtualWidth = width
    174       this.frameConfig.virtualHeight = height
    175       this._configChanged()
    176     }
    177 
    178     FrameProducer.prototype.nextFrame = function() {
    179       var frame = null
    180       var chunk
    181 
    182       if (this.parser) {
    183         while ((frame = this.parser.nextFrame()) === null) {
    184           chunk = this.socket.stream.read()
    185           if (chunk) {
    186             this.parser.push(chunk)
    187           }
    188           else {
    189             this.readable = false
    190             break
    191           }
    192         }
    193       }
    194 
    195       return frame
    196     }
    197 
    198     FrameProducer.prototype.needFrame = function() {
    199       this.needsReadable = true
    200       this._maybeEmitReadable()
    201     }
    202 
    203     FrameProducer.prototype._configChanged = function() {
    204       this.restart()
    205     }
    206 
    207     FrameProducer.prototype._socketEnded = function() {
    208       log.warn('Connection to minicap ended unexpectedly')
    209       this.failCounter.inc()
    210       this.restart()
    211     }
    212 
    213     FrameProducer.prototype._outputEnded = function() {
    214       log.warn('Shell keeping minicap running ended unexpectedly')
    215       this.failCounter.inc()
    216       this.restart()
    217     }
    218 
    219     FrameProducer.prototype._failLimitExceeded = function(limit, time) {
    220       this._stop()
    221       this.failed = true
    222       this.emit('error', new Error(util.format(
    223         'Failed more than %d times in %dms'
    224       , limit
    225       , time
    226       )))
    227     }
    228 
    229     FrameProducer.prototype._startService = function() {
    230       log.info('Launching screen service')
    231       return minicap.run(util.format(
    232           '-S -Q %d -P %s'
    233         , options.screenJpegQuality
    234         , this.frameConfig.toString()
    235         ))
    236         .timeout(10000)
    237     }
    238 
    239     FrameProducer.prototype._readOutput = function(out) {
    240       out.pipe(split()).on('data', function(line) {
    241         var trimmed = line.toString().trim()
    242 
    243         if (trimmed === '') {
    244           return
    245         }
    246 
    247         if (/ERROR/.test(line)) {
    248           log.fatal('minicap error: "%s"', line)
    249           return lifecycle.fatal()
    250         }
    251 
    252         var match = /^PID: (d+)$/.exec(line)
    253         if (match) {
    254           this.pid = Number(match[1])
    255           this.emit('pid', this.pid)
    256         }
    257 
    258         log.info('minicap says: "%s"', line)
    259       }.bind(this))
    260     }
    261 
    262     FrameProducer.prototype._waitForPid = function() {
    263       if (this.pid > 0) {
    264         return Promise.resolve(this.pid)
    265       }
    266 
    267       var pidListener
    268       return new Promise(function(resolve) {
    269           this.on('pid', pidListener = resolve)
    270         }.bind(this)).bind(this)
    271         .timeout(2000)
    272         .finally(function() {
    273           this.removeListener('pid', pidListener)
    274         })
    275     }
    276 
    277     FrameProducer.prototype._connectService = function() {
    278       function tryConnect(times, delay) {
    279         return adb.openLocal(options.serial, 'localabstract:minicap')
    280           .timeout(10000)
    281           .then(function(out) {
    282             return out
    283           })
    284           .catch(function(err) {
    285             if (/closed/.test(err.message) && times > 1) {
    286               return Promise.delay(delay)
    287                 .then(function() {
    288                   return tryConnect(times - 1, delay * 2)
    289                 })
    290             }
    291             return Promise.reject(err)
    292           })
    293       }
    294       log.info('Connecting to minicap service')
    295       return tryConnect(5, 100)
    296     }
    297 
    298     FrameProducer.prototype._stop = function() {
    299       return this._disconnectService(this.socket).bind(this)
    300         .timeout(2000)
    301         .then(function() {
    302           return this._stopService(this.output).timeout(10000)
    303         })
    304         .then(function() {
    305           this.runningState = FrameProducer.STATE_STOPPED
    306           this.emit('stop')
    307         })
    308         .catch(function(err) {
    309           // In practice we _should_ never get here due to _stopService()
    310           // being quite aggressive. But if we do, well... assume it
    311           // stopped anyway for now.
    312           this.runningState = FrameProducer.STATE_STOPPED
    313           this.emit('error', err)
    314           this.emit('stop')
    315         })
    316         .finally(function() {
    317           this.output = null
    318           this.socket = null
    319           this.pid = -1
    320           this.banner = null
    321           this.parser = null
    322         })
    323     }
    324 
    325     FrameProducer.prototype._disconnectService = function(socket) {
    326       log.info('Disconnecting from minicap service')
    327 
    328       if (!socket || socket.ended) {
    329         return Promise.resolve(true)
    330       }
    331 
    332       socket.stream.removeListener('readable', this.readableListener)
    333 
    334       var endListener
    335       return new Promise(function(resolve) {
    336           socket.on('end', endListener = function() {
    337             resolve(true)
    338           })
    339 
    340           socket.stream.resume()
    341           socket.end()
    342         })
    343         .finally(function() {
    344           socket.removeListener('end', endListener)
    345         })
    346     }
    347 
    348     FrameProducer.prototype._stopService = function(output) {
    349       log.info('Stopping minicap service')
    350 
    351       if (!output || output.ended) {
    352         return Promise.resolve(true)
    353       }
    354 
    355       var pid = this.pid
    356 
    357       function kill(signal) {
    358         if (pid <= 0) {
    359           return Promise.reject(new Error('Minicap service pid is unknown'))
    360         }
    361 
    362         var signum = {
    363           SIGTERM: -15
    364         , SIGKILL: -9
    365         }[signal]
    366 
    367         log.info('Sending %s to minicap', signal)
    368         return Promise.all([
    369             output.waitForEnd()
    370           , adb.shell(options.serial, ['kill', signum, pid])
    371               .then(adbkit.util.readAll)
    372               .return(true)
    373           ])
    374           .timeout(2000)
    375       }
    376 
    377       function kindKill() {
    378         return kill('SIGTERM')
    379       }
    380 
    381       function forceKill() {
    382         return kill('SIGKILL')
    383       }
    384 
    385       function forceEnd() {
    386         log.info('Ending minicap I/O as a last resort')
    387         output.end()
    388         return Promise.resolve(true)
    389       }
    390 
    391       return kindKill()
    392         .catch(Promise.TimeoutError, forceKill)
    393         .catch(forceEnd)
    394     }
    395 
    396     FrameProducer.prototype._readBanner = function(socket) {
    397       log.info('Reading minicap banner')
    398       return bannerutil.read(socket).timeout(2000)
    399     }
    400 
    401     FrameProducer.prototype._readFrames = function(socket) {
    402       this.needsReadable = true
    403       socket.on('readable', this.readableListener)
    404 
    405       // We may already have data pending. Let the user know they should
    406       // at least attempt to read frames now.
    407       this.readableListener()
    408     }
    409 
    410     FrameProducer.prototype._maybeEmitReadable = function() {
    411       if (this.readable && this.needsReadable) {
    412         this.needsReadable = false
    413         this.emit('readable')
    414       }
    415     }
    416 
    417     FrameProducer.prototype._readableListener = function() {
    418       this.readable = true
    419       this._maybeEmitReadable()
    420     }
    421 
    422     function createServer() {
    423       log.info('Starting WebSocket server on port %d', screenOptions.publicPort)
    424 
    425       var wss = new WebSocket.Server({
    426         port: screenOptions.publicPort
    427       , perMessageDeflate: false
    428       })
    429 
    430       var listeningListener, errorListener
    431       return new Promise(function(resolve, reject) {
    432           listeningListener = function() {
    433             return resolve(wss)
    434           }
    435 
    436           errorListener = function(err) {
    437             return reject(err)
    438           }
    439 
    440           wss.on('listening', listeningListener)
    441           wss.on('error', errorListener)
    442         })
    443         .finally(function() {
    444           wss.removeListener('listening', listeningListener)
    445           wss.removeListener('error', errorListener)
    446         })
    447     }
    448 
    449     return createServer()
    450       .then(function(wss) {
    451         var frameProducer = new FrameProducer(
    452           new FrameConfig(display.properties, display.properties))
    453         var broadcastSet = frameProducer.broadcastSet = new BroadcastSet()
    454 
    455         broadcastSet.on('nonempty', function() {
    456           frameProducer.start()
    457         })
    458 
    459         broadcastSet.on('empty', function() {
    460           frameProducer.stop()
    461         })
    462 
    463         broadcastSet.on('insert', function(id) {
    464           // If two clients join a session in the middle, one of them
    465           // may not release the initial size because the projection
    466           // doesn't necessarily change, and the producer doesn't Getting
    467           // restarted. Therefore we have to call onStart() manually
    468           // if the producer is already up and running.
    469           switch (frameProducer.runningState) {
    470           case FrameProducer.STATE_STARTED:
    471             broadcastSet.get(id).onStart(frameProducer)
    472             break
    473           }
    474         })
    475 
    476         display.on('rotationChange', function(newRotation) {
    477           frameProducer.updateRotation(newRotation)
    478         })
    479 
    480         frameProducer.on('start', function() {
    481           broadcastSet.keys().map(function(id) {
    482             return broadcastSet.get(id).onStart(frameProducer)
    483           })
    484         })
    485 
    486         frameProducer.on('readable', function next() {
    487           var frame = frameProducer.nextFrame()
    488           if (frame) {
    489             Promise.settle([broadcastSet.keys().map(function(id) {
    490               return broadcastSet.get(id).onFrame(frame)
    491             })]).then(next)
    492           }
    493           else {
    494             frameProducer.needFrame()
    495           }
    496         })
    497 
    498         frameProducer.on('error', function(err) {
    499           log.fatal('Frame producer had an error', err.stack)
    500           lifecycle.fatal()
    501         })
    502 
    503         wss.on('connection', function(ws) {
    504           var id = uuid.v4()
    505           var pingTimer
    506 
    507           function send(message, options) {
    508             return new Promise(function(resolve, reject) {
    509               switch (ws.readyState) {
    510               case WebSocket.OPENING:
    511                 // This should never happen.
    512                 log.warn('Unable to send to OPENING client "%s"', id)
    513                 break
    514               case WebSocket.OPEN:
    515                 // This is what SHOULD happen.
    516                 ws.send(message, options, function(err) {
    517                   return err ? reject(err) : resolve()
    518                 })
    519                 break
    520               case WebSocket.CLOSING:
    521                 // Ok, a 'close' event should remove the client from the set
    522                 // soon.
    523                 break
    524               case WebSocket.CLOSED:
    525                 // This should never happen.
    526                 log.warn('Unable to send to CLOSED client "%s"', id)
    527                 clearInterval(pingTimer)
    528                 broadcastSet.remove(id)
    529                 break
    530               }
    531             })
    532           }
    533 
    534           function wsStartNotifier() {
    535             return send(util.format(
    536               'start %s'
    537             , JSON.stringify(frameProducer.banner)
    538             ))
    539           }
    540 
    541           function wsPingNotifier() {
    542             return send('ping')
    543           }
    544 
    545           function wsFrameNotifier(frame) {
    546             return send(frame, {
    547               binary: true
    548             })
    549           }
    550 
    551           // Sending a ping message every now and then makes sure that
    552           // reverse proxies like nginx don't time out the connection [1].
    553           //
    554           // [1] http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout
    555           pingTimer = setInterval(wsPingNotifier, options.screenPingInterval)
    556 
    557           ws.on('message', function(data) {
    558             var match = /^(on|off|(size) ([0-9]+)x([0-9]+))$/.exec(data)
    559             if (match) {
    560               switch (match[2] || match[1]) {
    561               case 'on':
    562                 broadcastSet.insert(id, {
    563                   onStart: wsStartNotifier
    564                   , onFrame: (function (t) {
    565                     var wait_time = 0;
    566                     return function (frame) {
    567                       wait_time++
    568                       // return wsFrameNotifier(frame);
    569                       if (wait_time < 30) {
    570                         clearTimeout(t)
    571 
    572                         wait_time % 4 || gm(frame).quality(10).toBuffer(function (err, buffer) {
    573                           wsFrameNotifier(buffer)
    574                         })
    575 
    576                         t = setTimeout(function () {
    577                           wait_time = 0
    578                           wsFrameNotifier(frame)
    579                         }, 100)
    580                       }
    581                     }
    582                   })(0)
    583                 })
    584                 break
    585               case 'off':
    586                 broadcastSet.remove(id)
    587                 // Keep pinging even when the screen is off.
    588                 break
    589               case 'size':
    590                 frameProducer.updateProjection(
    591                   Number(match[3]), Number(match[4]))
    592                 break
    593               }
    594             }
    595           })
    596 
    597           ws.on('close', function() {
    598             clearInterval(pingTimer)
    599             broadcastSet.remove(id)
    600           })
    601         })
    602 
    603         lifecycle.observe(function() {
    604           wss.close()
    605         })
    606 
    607         lifecycle.observe(function() {
    608           frameProducer.stop()
    609         })
    610 
    611         return frameProducer
    612       })
    613   })
    View Code
  • 相关阅读:
    函数对象、名称空间与作用域
    函数
    leetcode语法练习(二)
    leetcode语法练习(一)
    字符编码与文件操作
    集合类型内置方法与总结
    列表,元组与字典类型
    数据类型内置方法之数据类型与字符串类型
    [SVG实战]饼图全面解析
    [JavaScript语法学习]重新认识JavaScript
  • 原文地址:https://www.cnblogs.com/sc912/p/14548092.html
Copyright © 2020-2023  润新知