比較してみましょう。
サーバーをノックし、接続を確立した後、識別子の数バイトをソケットに書き込み、次に現在の温度の数バイトを書き込むリモート温度センサー-これはプリミティブです。
サーバーからコマンドを受信して、測定頻度または測定値の平均化期間を変更できる同じセンサーは、すでにより複雑です。 これに加えて、たとえば、ファームウェアの更新をセンサーに送信する機能とコードは、その簡潔さを簡単に失う可能性があります。
そのような場合、オートマトンの理論の規範に準拠するふりをせずに、「ステートマシン」のスタイルでコードを整理します。
この記事の例は、 github.com / kityan / fsmConnectionから入手できます。 次に、いくつかの重要なポイントについて説明します。
メインアプリケーションのコード。
サーバー側のみを考慮してください。 コードは非常に簡単です。
var net = require('net'); var ClientConnection = require('./ClientConnection.js'); var config = {"socketTimeout":3000, "port": 30000} net.createServer(function(socket) {var clientConnection = new ClientConnection(socket, config);}) .listen(config.port, function () {console.log('Listening on: ' + config.port);});
接続を確立するたびに、サーバーはClientConnectionのインスタンスを作成し、ソケットと構成オブジェクトを渡します。
ClientConnectionモジュールのコードスニペット。
インスタンスフィールドを初期化します。
var ClientConnection = function (socket, config){...}
プロトタイプでは、マシンを切り替えるClientConnection.toメソッドを定義します。
ClientConnection.prototype.to = function (newState) { // onExitHandler? if (this.currentState && this.states[this.currentState].onExitHandler && typeof this.states[this.currentState].onExitHandler == 'function') { this.states[this.currentState].onExitHandler.call(this); } var prevState = this.currentState; this.currentState = newState; // inputHandler? if (this.currentState && this.states[this.currentState].inputHandler && typeof this.states[this.currentState].inputHandler == 'function') { this.handleInput = this.states[this.currentState].inputHandler.bind(this); } else { this.handleInput = this.noInputHandler } // onEnterHandler? if (this.states[this.currentState].onEnterHandler && typeof this.states[this.currentState].onEnterHandler == 'function') { this.states[this.currentState].onEnterHandler.call(this, prevState); } return this; }
切り替えるときに、 onExitHandlerメソッドに以前の状態があるかどうかを確認し、ある場合はそれを呼び出します。
次に、マシンのhandleInputメソッドに、新しい状態のinputHandlerへのポインターを割り当てます。 最後に、新しい状態にonEnterHandlerメソッドがあるかどうかを確認します。 ある場合、それを呼び出します。
ClientConnection.to(newState)を呼び出した後はどうなりますか? onExitHandlerとonEnterHandlerの呼び出しが別の状態に切り替わらない場合、マシンはこの状態のままになります。 そして、すべてがソケットデータに依存します。 到着するすべてのパケットはhandleInputにルーティングされます 。 なんで?
実際には、インスタンスを作成すると、すぐに初期化状態に切り替わり、ソケットイベントのハンドラーがハングアップします。
ClientConnection.prototype.states = { 'inital': { 'onEnterHandler': function(){ // socket events this.socket.on('timeout', function() {this.to('socket-timeout');}.bind(this)); this.socket.on('end', function() {this.to("socket-end");}.bind(this)); this.socket.on('error', function (exc) {this.to("socket-error").handleInput(exc);}.bind(this)); this.socket.on('close', function () {this.to("socket-close");}.bind(this)); this.socket.on('data', function (data) {this.handleInput(data);}.bind(this)); this.to("waitingForHelloFromClient"); } }, ... }
そして、次の状態に切り替えます。 私たちの場合、これは'waitingForHelloFromClient'です。
すべての状態は、 ClientConnection.prototype.statesオブジェクトで説明されています。 有効な状態にはinputHandlerがありません。 そのような状態に切り替えるとき、 onEnterHandler内で何らかのアルゴリズムを実行し 、すぐに別の状態に切り替えます。 inputHandlerで停止して、イベントループの次の反復でコードが呼び出され、ソケットデータが表示された場合に処理できるようにします。 onExitHandlerに切り替えないことを強くお勧めします。
実際にはすべて。 コードが便利だと思われる場合-健康に使用してください。 批判は大歓迎です。
一般的なケースではより便利であることが判明する解決策(たとえば、 Machina.JS )があることに注意してください。