柔軟性、速度、開発の容易さを失うことなく、「異なる方法で」ソケットを使用することは可能ですか?
もちろん、既存のopen 、 use 、 closeなどのコマンドの周りにオブジェクトラッパーを書くだけです。 OOPに関しては、これはカプセル化です。
幸いなことに、そのようなクラス、さらにはクラス(サーバー側とクライアント側)はすでに作成されており、少なくともバージョン5.2が付属しています。
- %IO.ServerSocket-サーバー側で動作します。
- %IO.Socket-クライアント側での作業用。
それらを使用した2つの簡単な例を考えてみましょう。
例1
サーバーコード
#dim sock As %IO.ServerSocket = ##class ( %IO.ServerSocket ). %New ()
set sock. TranslationTable = "UTF8"
write #, " ..." , !
#; : pPort, pTimeout, pSC
do sock. Open (7001, 1, .sc)
if $$$ISOK (sc) {
do sock. Listen ()
do {
#; : pMaxReadLen, pTimeout
set s=sock. ReadAny (250, 5)
write s,!
#; : pData, pFlush
do sock. Write ( $$$FormatText ( " %1" , $$$quote (s)), $$$YES )
} while (s'= "bye!" )
do sock. Close ()
write " ..." , !
}
クライアントコード
#dim sock As %IO.ServerSocket = ##class ( %IO.Socket ). %New ()
set sock. TranslationTable = "UTF8"
write #, " ..." , !
#; : pHost, pPort, pTimeout, pSC
do sock. Open ( "127.0.0.1" , "7001" ,1,.sc)
if $$$ISOK (sc) {
#; : pData, pFlush
do sock. Write ( " " , $$$YES )
#; : pMaxReadLen, pTimeout
write sock. ReadAny (250, 5),!
hang 2
do sock. Write ( "bye!" , $$$YES )
write sock. ReadAny (250, 5),!
do sock. Close ()
write " ..." , !
}
- サーバーでの結果:
... bye! ...
- クライアントでの結果:
... " " "bye!" ...
すべてが非常に透過的であるため、例を詳細に説明することは意味がありません。
「 bye! 」停止行がクライアント部分から受信されると、サーバー部分がその作業を終了することにのみ注意してください。
例2
コード:
Class demo.socket Extends %RegisteredObject
{
ClassMethod Server2()
{
#; do ##class(demo.socket).Server2()
#dim sock As %IO.ServerSocket = ##class ( %IO.ServerSocket ). %New ()
write #, " ..." , !
do sock. Open (10081, 1, .sc)
if $$$ISOK (sc) {
do sock. Listen (-1, .sc)
do {
set s=sock. ReadAny ( $$$MaxLocalLength ,, .sc)
write s,!
do sock. Write (s, $$$YES , .sc)
} while (s'= "bye!" )
do sock. Close ()
write " ..." , !
}
}
ClassMethod Client2( end As %Boolean = { $$$NO })
{
#; do ##class(demo.socket).Client2(1)
#dim sock As %IO.ServerSocket = ##class ( %IO.Socket ). %New ()
write #, " ..." , !
do sock. Open ( "127.0.0.1" , "10081" ,1,.sc)
if $$$ISOK (sc) {
set time= $zhorolog
for i=1:1:10 {
do sock. Write ( "1234567890" , $$$YES , .sc)
set s=sock. ReadAny ( $$$MaxLocalLength ,, .sc)
write s,!
}
write "= " , $zhorolog -time, " ." ,!
do :end sock. Write ( "bye!" , $$$YES , .sc)
do sock. Close ()
write " ..." , !
} else {
write $system .Status . GetErrorText (sc, "ru" ),!
}
}
}
WebSocketプロトコルをサポートするサーバーパーツの実装例
サーバー部分は一度に1つのクライアント接続のみを処理できるため、上記の2つの例は非常に単純です。
数十/数百のクライアント接続を同時に処理する必要がある場合はどうなりますか
これを行うには、 %IO.ServerSocket:ListenJob()メソッドを使用します
より複雑な例を作成してみましょう。たとえば、CachéDBMSにWebSocketプロトコルサポートを追加します。
これは特に当てはまります。CachéDBMSはWebアプリケーション作成テクノロジー( CSP )を提供し、ビジュアル、MVC、およびその他のコンポーネント( ZEN )の豊富なセットを備えた既製のフレームワークも備えています。
また、CachéDBMSは、OOPとSQLのサポートに加えて、NoSQLリポジトリとしても機能することを覚えておいてください...
インターネット上のWebSocketプロトコル自体について多くのことが書かれています。 それに基づいて、チャットルーム、オンラインゲーム、およびサーバー(データベース)との高速データ交換が必要なすべてのものが記述されます。
このデモでは、プロトコルの2つのバージョン(76と7)のみがサポートされることに注意してください。
テストには、WebSocketをサポートする最新のブラウザーの1つが必要です。
IE 8などの古いブラウザーでは、Flashの上で実行される特別なプラグインを使用できます。
興味のある方は、記事の最後にあるソースコードでプロトコル実装の技術的な詳細を見つけることができます。
ここでは、最も単純なWebチャットを実装する小さな例を検討します。
Note:CachéObjectScriptでWebSocketプロトコルを実装するためのソースコードは、情報提供のみを目的としています。
だから:
- Studioで 、「SAMPLES」などのWebアプリケーションのサポートが構成されているエリアにプロジェクトをインポートします。
- net.WebSocketEventsクラスを継承してサーバー側イベントハンドラクラスを作成し、 そのクラス内の対応するメソッドをオーバーライドします。
例:
/// .
Class demo.Server Extends net.WebSocketEvents
{
/// onbeforeconnect.
/// <br> .
Method onbeforeconnect() As %Boolean
{
set ^tmp( $i (^tmp), "onbeforeconnect" )= $lb (.. WebSocketGET ,.. WebSocketHost ,.. WebSocketOrigin ,.. WebSocketVer )
q $$$YES
}
/// onconnect.
Method onconnect()
{
set ^tmp( $i (^tmp), "onconnect" )= ""
}
/// onmessage.
/// <br> :
/// <br><var>msg</var> - , .
Method onmessage( msg As %String )
{
set ^tmp( $i (^tmp), "onmessage" )=msg
#; .
/*
do ..send("Cachéasd"_$random(1000)):msg="get",
..send($replace($j("",32000)," ","é")):msg="getBig",
..sendBroadcast("from Caché to All"):msg="toAll",
..sendBroadcast($$$FormatText(" : %1",$$$quote($p(msg,"^",2)))):$e(msg)="^"
*/
if msg= "get" {
do .. send ( "Cachéasd" _ $random (1000))
} elseif msg= "getBig" {
do .. send ( $replace ( $j ( "" ,32000), " " , "é" ))
} elseif msg= "toAll" {
do .. sendBroadcast ( "from Caché to All" )
} elseif $e (msg)= "^" {
do .. sendBroadcast ( $$$FormatText ( " : %1" , $$$quote ( $p (msg, "^" ,2))))
}
}
/// onclose.
Method onclose()
{
set ^tmp( $i (^tmp), "onclose" )= ""
}
}
- Flashを使用するプラグイン(上記を参照)を使用する場合、ここからダウンロードできる最新バージョンのFlash Playerをプレインストールします 。
注:デフォルトで無効になっているブラウザ(Operaなど)でWebSocketを有効にすることを忘れないでください。
- サーバーでWebSocket接続を処理するためにサーバーを起動します。
SAMPLES>do ##class(net.WebSocketServer).Start("demo.Server")
- ブラウザ(FireFox、ページ(クラス) demo.webclientなど)で起動します 。そのソースコードは次のとおりです。
Class demo.webclient Extends %ZEN.Component.page
{
/// If true, then attempt to refresh this page when its session timeout period has expired.
/// This will cause a login page to display if the current session has ended
/// and security is set to require login.
Parameter AUTOLOGOUT As BOOLEAN = 0 ;
/// Comma-separated list of additional JS include files for the page.
Parameter JSINCLUDES As STRING = "websocket/swfobject.js,websocket/web_socket.js" ;
XData Contents [ XMLNamespace = "www.intersystems.com/zen" ]
{
< page xmlns = "www.intersystems.com/zen" title = "" >
< vgroup labelPosition = "left" >
< label id = "lb" label = " :" />
< text id = "usr" label = ":" />
< button caption = "" onclick = "zenPage.start();" />
< button caption = " 1" label = "Hello123xcf789" onclick = "ws.send('Hello123xcf789');" />
< button caption = " 2" label = "toAll" onclick = "ws.send('toAll');" />
< button caption = " 3" label = "get" onclick = "ws.send('get');" />
< button caption = " 4" label = "getBig" onclick = "ws.send('getBig');" />
< button caption = "" onclick = "ws.close();" />
</ vgroup >
</ page >
}
ClientMethod start() [ Language = javascript ]
{
ws = new WebSocket( "ws://127.0.0.1:10081/asd s HTTP/1.1///" );
ws.onopen = function () {
zenSetProp( 'lb' , 'value' , 'open' );
ws.send( '^' + zenGetProp( 'usr' , 'value' ));
};
ws.onmessage = function (e) {
zenAlert( 'onmessage' , ' length=' ,e.data.length, ' data=' ,e.data);
};
ws.onclose = function () {
zenSetProp( 'lb' , 'value' , 'close' );
};
ws.onerror = function () {
zenAlert( 'onerror' );
};
}
/// This client event, if present, is fired when the page is loaded.
ClientMethod onloadHandler() [ Language = javascript ]
{
// Let the library know where WebSocketMain.swf is:
WEB_SOCKET_SWF_LOCATION = "websocket/WebSocketMain.swf" ;
WEB_SOCKET_DEBUG = false;
}
}
- 最初に「開始」ボタンを押します。 ブラウザからCachéDBMSへの接続に成功すると、ステータスが「open」になります。
接続がない場合は、 トラブルシューティングのセクションを確認してください。 ローカルマシンでは、すべてが問題なく動作するはずです。 - 接続が正常に確立されたら、さまざまなテストを試すことができます-例では4つのテストがあります-任意の順序で。
- テストの最後に、「停止」ボタンをクリックすることを忘れないでください。
- 複数のブラウザを実行して、手順6〜8を繰り返します。
サーバーのログの内容を見てみましょう。
- Firefox 12
USER>zw ^tmp ^tmp=8 ^tmp(1,"onbeforeconnect")=$lb("/asd%20s%20HTTP/1.1///","127.0.0.1:10081","","hybi-10") ^tmp(2,"onconnect")="" ^tmp(3,"onmessage")="^sdsdf" ^tmp(4,"onmessage")="Hello123xcf789" ^tmp(5,"onmessage")="toAll" ^tmp(6,"onmessage")="get" ^tmp(7,"onmessage")="getBig" ^tmp(8,"onclose")=""
- IE 9
USER>zw ^tmp ^tmp=8 ^tmp(1,"onbeforeconnect")=$lb("/asd s HTTP/1.1///","127.0.0.1:10081","","hybi-10") ^tmp(2,"onconnect")="" ^tmp(3,"onmessage")="^dfg" ^tmp(4,"onmessage")="Hello123xcf789" ^tmp(5,"onmessage")="toAll" ^tmp(6,"onmessage")="get" ^tmp(7,"onmessage")="getBig" ^tmp(8,"onclose")=""
WSの代わりにセキュアWSプロトコルを使用する
これには、次のものが必要です。
- ポータルでSSLサーバー構成をWebSocketSSLなどの名前で構成し、「 ピア証明書検証レベル 」を「 なし 」に設定します。
Note:CachéDBMSでのSSLの構成の詳細は、このブログの以前の記事の 1つに記載されています。
- WebSocketデーモンを起動するときに、SSL構成を指定する必要があります。
SAMPLES>do ##class(net.WebSocketServer).Start("demo.Server",,"WebSocketSSL")
- Flashを使用するプラグイン(上記を参照)を使用している場合、最初にポート843でポリシーファイルデーモンを実行します。
SAMPLES>do ##class(net.WebSocketServer).StartPolicy()
- 最初に行を変更してdemo.webclientクラスを再コンパイルします
「ws://127.0.0.1:10081 ...」
に「wss://127.0.0.1:10081 ...」
- 上記を参照してください。
結果として、CachéDBMS内に排他的に実装されたWebアプリケーション、サーバーパーツ、およびデータベースがあります。
ソースプロジェクト