CachéDBMS 2013.1の新機能:ネイティブWebSocketサポート

前回の記事で 、通常のソケットの上にこのプロトコルを独自に実装したサーバーの例を使用して、WebSocketの操作について既に説明しました。

CachéDBMS 2013.1では、CSPゲートウェイに、WebサーバーとHTML 5互換ブラウザ間のWebSocket接続用のHTML 5仕様のサポートが含まれるようになりました。 この機能は、Apache 2.2以降、およびWindows Server 2012の一部であるIIS 8.0で使用できます。

Apache 2.4は既にCaché2013.1に組み込まれているため、サンプルを実行します。
クライアント部分を実装するために、 ZENフレームワークが使用されましたが、 CSPテクノロジーまたはその他を使用して例を再作成できます。

それでは始めましょう。

理論


前述のとおり、CachéDBMSのサーバー側でWebSocketをサポートするためのすべての内部ロジックが引き継がれます。 プログラマは、 %CSP.WebSocketクラスから継承して独自のクラスを作成し、そのクラスのいくつかのメソッドをオーバーライドするだけです。
クラス%CSP.WebSocketのオブジェクトは、WebSocketプロトコルを使用したクライアントとサーバー間の通信のイベントハンドラーとして機能します。 すべてのWebSocketサーバーは%CSP.WebSocketを継承します。
%CSP.WebSocketクラスのメソッドとプロパティの詳細な説明は、クラスリファレンスにあります。
ここでは、そのうちのいくつかだけを簡単に説明します。
メソッド/プロパティ説明
読む()クライアントからデータを受信する
書き込み()クライアントにデータを送信する
OnPreServer()PreServerイベントハンドラー:WebSocketサーバーを起動する前に呼び出されます
OnPostServer()PostServerイベントハンドラー:WebSocketサーバーが停止すると呼び出されます
サーバー()WebSocketサーバー自体
EndServer()Websocketサーバーを停止します
出席する読み取り中にWebSocketサーバーが現在のデータフレームの最後に到達すると、プロパティの値はtrue(1)になります
共有接続このプロパティは、クライアントとWebSocketサーバー間の情報交換が専用のCSPゲートウェイ接続を介して行われるか、共有接続のプール(まだ使用されていない)を介して行われるかを決定します

練習する


これらすべてを実際に使用する方法の簡単な例を見てみましょう。
まず、 USER StudioでZENページ( demo.WebSocketクラス)を作成し、 %CSP.WebSocketクラスから継承します。
クラスdemo.WebSocket Extends ( %ZEN.Component.page 、 %CSP.WebSocket )
{

XData コンテンツ[ XMLNamespace = " www.intersystems.com/zen" ]
{
< page xmlns = " www.intersystems.com/zen" title = "" >
</ ページ >
}

}

JSONの操作をサポートするために、zenCSLM.jsファイルを含めます。
パラメーター JSINCLUDES = "zenCSLM.js" ;

WebSocketを介してクライアントを接続し、対応するイベントを処理するためのメソッドを実装します。
///このクライアントイベントが存在する場合、ページがロードされると発生します。
ClientMethod onloadHandler()[ 言語 = javascript]
{
ws = null;

url = ((window.location.protocol == 'https:' ) ? 'wss:' : 'ws:' ) + '//' + window.location.host + window.location.pathname;

wsCtor = window [ 'MozWebSocket' ] ? MozWebSocket : window [ 'WebSocket' ] ? WebSocket : null;

if (zenIsMissing(wsCtor))zenAlert( 'WebSocketはブラウザでサポートされていません!' );
}

ClientMethod start()[ 言語 = javascript]
{
if ( ! zenIsMissing(wsCtor)){
if (zenIsMissing(ws)){
ws = 新しい wsCtor(url);

ws.onopen = function (){
zenAlert( 'onopen \ n \ nreadyState:' 、ws.readyState、 '\ nbinaryType:' 、ws.binaryType、 '\ nbufferedAmount:' 、ws.bufferedAmount);
};

ws.onmessage = function (e){
zenAlert(e.data);
};

ws.onclose = function (e){
zenAlert( 'onclose \ n \ nwasClean:' 、e.wasClean、 '\ ncode:' 、e.code、 '\ nreason:' 、e.reason);
ws = null;
};

ws.onerror = function (e){
zenAlert( 'onerror' );
};
}
}
}

WebSocketサーバーの実装は同じページにあるため、接続文字列はプロトコルを除いて同一になります。 つまり、ZENページのURLの形式が
localhost:57772/csp/user/demo.WebSocket.cls
、次にWebSocketサーバーの場合は
ws://localhost:57772/csp/user/demo.WebSocket.cls

次に、ページに小さなインターフェイスを追加しましょう。サーバーにデータを接続、切断、送信するためのボタンがいくつかあります。
XData コンテンツ[ XMLNamespace = " www.intersystems.com/zen" ]
{
< page xmlns = " www.intersystems.com/zen" title = "" >
< text id = "txt" label = "送信するテキスト" value = "World" />
< button caption = " 1.Connect " onclick = "zenPage.start();" />
< button caption = "2.テキストを送信" onclick = "if(!zenIsMissing(ws))ws.send(zenGetProp( 'txt'、 'value'));" />
< button caption = "3.長い行を送信する" onclick = "zenPage.sendLongStr(100000);" />
< button caption = "4.Send JSON" onclick = "zenPage.sendJSON();" />
< button caption = "5.電源を切る" onclick = "if(!zenIsMissing(ws))ws.close();" />
</ ページ >
}

sendLongStrメソッドのコードは次のとおりです。
ClientMethod sendLongStr( N )[ 言語 = javascript]
{
if (zenIsMissing(ws)) return ;

var s = 'a' ;
for ( var i = 1 ; i < N; i ++ )s + = 'a' ;
ws.send(s);
}

sendJSONメソッドコード :
ClientMethod sendJSON()[ 言語 = javascript]
{
if (zenIsMissing(ws)) return ;

var obj = {
"firstName" : "Ivan" 、
"lastName" : "Ivanov" 、
「アドレス」 :{
"streetAddress" : "モスクワハイウェイ101、 apt。101" 、
「都市」 : 「レニングラード」 、
「 郵便番号 」 : 101101
}、
「phoneNumbers」 :[
「812 123-1234」 、
「916 123-4567」
]
};

ws.send(ZLM.jsonStringify(obj));
}

WebSocketサーバー自体を実装するときが来ました。 これを行うには、次のようにOnPreServer 、 OnPostServer、およびServerメソッドをオーバーライドします。
メソッド OnPreServer() As%Status
{
$ system .Processを実行し ます。 未定義 (2)

Set ^ tmp( $ Increment (^ tmp)、 "OnPreServer" )= ""
$$$ OKを終了OK
}

メソッド OnPostServer() As%Status
{
Set ^ tmp( $ Increment (^ tmp)、 "OnPostServer" )= ""
$$$ OKを終了OK
}

メソッド サーバー() As%ステータス
{
{

len = 32656に 設定
設定 データ = $ ZConvert (.. 読み取り ( 。Len , . Status )、 "I" 、 "UTF8" )

$$$ ISOK ( status ) {

data = "World"の 場合 {
。。Write ( $ ZConvert ( "Hello、" _ data _ "!" 、 "O" 、 "UTF8" ))
} ElseIf data = "bye" {

; WebSocketサーバーを強制的にシャットダウンし、無限ループを終了します
; oncloseはクライアントで動作します
Do .. EndServer ()
やめる

} その他 {

#Dim obj As %RegisteredObject = $$$ NULLOREF

Set ^ tmp = $ Increment (^ tmp)

; 文字列をオブジェクトに変換する
$$$ ISOK ( ##クラス ( %ZEN.Auxiliary.jsonProvider )。 %ConvertJSONToObject ( data ,,。 Obj )) {

; エラーがない場合、プロパティ値を保存します
^ tmp(^ tmp、 "Server" 、 "firstName" )= objを設定し ます。 firstName
^ tmp(^ tmp、 "Server" 、 "lastName" )= objを設定し ます。 lastName
^ tmp(^ tmp、 "Server" 、 "address.streetAddress" )= objを設定し ます。 住所 streetAddress
^ tmp(^ tmp、 "Server" 、 "address.city" )= objを設定し ます。 住所 市
^ tmp(^ tmp、 "Server" 、 "address.postalCode" )= objを設定し ます。 住所 postalCode
^ tmp(^ tmp、 "Server" 、 "phoneNumbers.1" )= objを設定し ます。 phoneNumbers 。 GetAt (1)
^ tmp(^ tmp、 "Server" 、 "phoneNumbers.2" )= objを設定し ます。 phoneNumbers 。 GetAt (2)

; 姓を変更
objを 設定し ます。 lastName = "Sidorov"

; 別の電話を追加
objを実行し ます。 phoneNumbers 。 挿入 ( "111 111-1111" )

; オブジェクトにさらに2つの新しいプロパティを追加します。
objを 設定し ます。 name = "Vasya"
objを 設定し ます。 street = "17 Mira Street"

; 変更されたオブジェクトを文字列に変換し、クライアントに送り返します
Do .. Write (.. Write2Str ( 。Obj ))

} その他 {

; 長い文字列を受信したときにデータを保存する
Set ^ tmp(^ tmp、 "Server" 、 "longStr" )= .. AtEnd _ ":" _ $ Length ( data )_ ":" _ len
}
}
} その他 {
終了 :( $$$ GETERRORCODE ( status )= $$$ CSPWebSocketClosed )
}
}
$$$ OKを終了OK
}

Write2Strユーティリティメソッドは、Writeコマンドによって出力されたデータを文字列にアセンブルするために使用されます。
ClassMethod Write2Str( ByRef obj ) As%String [ プライベート ]
{
{
tIO = $ IO 、 tXDEV = "| XDEV |"を設定します _ + $ Job
{

// $$$ IsUnicodeにはUTF-8を使用します
オープン tXDEV :( $ ZF (-6、 $$$ XSLTLibrary 、12): "" : "S" :/ HOSTNAME = "XSLT" :/ IOT = $ Select ( $$$ IsUnicode : "UTF8" 、1: " RAW " ):/ IBU = 16384:/ OBU = 16384)
tXDEVを 使用する

終了 : $$$ ISERR ( obj 。 %ToJSON (、 "aeloiwu" ))

//残りの出力をフラッシュします
書き込み * -3

//ここで文字列を読み戻します(最大長まで、32kまたは長い文字列の場合は約4MB)
s = ""を 設定
while (1) {
#Dim tChunk As %String
tChunk を読み取り ます :0
終了 : ' $長さ ( tChunk )
s = s _ tChunkを 設定
}

} while (0)
} キャッチ {}

tXDEVを 閉じ ます
tIOを 使用する
終了
}

クラスをコンパイルし(Ctrl + F7)、ブラウザーで表示するために開いたままにします(F5)。
ボタンを連続して押すと、^ tmpグローバルの内容は次のようになります。

^tmp=7
^tmp(1,"OnPreServer")=""
^tmp(2,"Server","longStr")="0:32656:32656"
^tmp(3,"Server","longStr")="0:32656:32656"
^tmp(4,"Server","longStr")="0:32656:32656"
^tmp(5,"Server","longStr")="1:2032:2032"
^tmp(6,"Server","address.city")=""
^tmp(6,"Server","address.postalCode")=101101
^tmp(6,"Server","address.streetAddress")=" ., 101, .101"
^tmp(6,"Server","firstName")=""
^tmp(6,"Server","lastName")=""
^tmp(6,"Server","phoneNumbers.1")="812 123-1234"
^tmp(6,"Server","phoneNumbers.2")="916 123-4567"
^tmp(7,"OnPostServer")=""

demo.WebSocketクラスのソースコードをダウンロードします 。

Source: https://habr.com/ru/post/J181113/


All Articles