次世代SKKサーバ通信プロトコル SKKNet
SKKNet - Next generation SKK network transmission protocol
- 力が欲しいか?
- ――力が欲しいのなら……くれてやる!
ダウンロード
概要
- SKKサーバについて
SKKはその独特な実装によりさまざまな進化を遂げてきた。SKKサーバもその進化の1つである。シンプルな設計思想により、軽快な検索応答性能を実現し、SKK本来の操作性を妨げることなく円滑な日本語入力を実現した画期的な技術であった。リモートのSKKサーバを利用することで、ローカルマシンの処理負荷を劇的に軽減することができ、集中管理による辞書の運用を可能としていた。かつて多くの日本語入力システムにとって、日本語辞書の処理というのは膨大な計算能力が必要な重労働であり、SKKもその例外ではなかったことの名残りであるとも言える。
日本語入力方式であるSKKが産まれて数十年。計算機の目覚ましい性能向上により、今や一般的なSKKの日本語辞書は潤沢な主記憶上に展開できるようになり、組み込み用途でもない限り、処理負荷はほぼ無視できる時代となってしまった。SKKサーバを使ってネットワークとファイルシステムのオーバヘッドに甘んじてただ単なる辞書検索を行なうことに、もはや技術的なメリットは失われつつある。
そこでネットワークを利用する目的と利点をさらに強化することで、技術の陳腐化に対抗する。
言い換えるなら
運用管理機能の強化こそが現代のSKKサーバにおける最優先課題であるということである。
では、運用管理の強化とはどういうことなのだろうか?簡単に言うとユーザ辞書をサーバ管理したいということ。要するに……
SKK Online
…の実現へ向けた第一歩である!
レッツ人間電池!ヒャッハー
しかし、従来のSKKサーバでは単純な辞書処理しかサポートされておらず、その互換性にしがみついている限り、ユーザ辞書の学習をサーバ側に置くような運用は永遠に実現できないというのもまた然り。
てなわけで、従来のSKKサーバとはまったく異なる、以下の機能を持つ
次世代SKKサーバ通信プロトコルSKKNetを試作した。
- ・送りがな指定付きの検索 (サーバ側にユーザ辞書が存在する場合、候補順序に影響)
- ・確定と削除の通信を拡張 (ユーザ辞書の運用では必須)
- ・送受信処理を対称に記述できる単純な通信プロトコル
SKKNetは
SKKGateの一種(純粋なプロキシ動作のゲート)として実装しているため、何かゲートを作った瞬間『そのゲートは
そのままサーバとしても動作させることが可能になった』ということである。
ゲート製作者はサーバのことを考える必要がなくなるため開発の敷居を大きく下げることができるはずである。
基本的にローカルホスト内または同一サブネット内のみで使うことを想定している。この想定に加え、レスポンス速度が最重要であるSKKには、UDPこそが最適であるとの信念からSKKNetではデフォUDPとした。パケット到達性?SYNだのACKだのゴチャゴチャうるせぇ。変換時に問題が置きたら人間が即座に運用でカバーできるからいいのだ。だいたいローカルホスト内の通信にまでTCPを持ち込もうだなんて世界のコンピュータの性能劣化を企てるバークレイの陰謀。でもTCPにも対応しちゃったビクンビクン
今回は実験用途なので、通信はすべて平文で行なわれることにも注意(そもそも従来のSKKサーバ実装と同じ)。セキュアな経路が必要ならsshなりUT-VPNなりmoshなりを適当にかませばオーケー。ワシは後ろを向いておるよ。ワシに聞い〜ても知ら〜ん。
あと、従来のサーバとも相互接続ができるので、気合いがあれば両刀使いも可能なはず!
あとは任せた
詳細はソース参照。C#と
SKKGateでテキトーに書いた。
skkgate.js同様、ライセンスフリー(CC0 PUBLIC DOMAIN)なので、各自改造して遊ぶべし。
インストール方法
このへん内容が古いんでそのうち書き直さないとなー。
作業用フォルダにskknet.csとskknet.batを置いて
skknet
と実行するとskknet.exeが生成されます。でもってついでに実行されてサクっと常駐します。
常駐させたくない場合や、常駐したものを停止させたい場合は再度実行するか
skknet stop
などと実行するとよいでしょう。
インストールする場合は、生成されたexeファイルをパスの通ったフォルダに置いてください。
パスとかよくわかんない時はC:\Windowsにでも適当に放り込んでしまえばOK。
インストールせずお試しで使ってみたいという場合はこのままこのexeファイルをカレントディレクトリに置いて使ってください。
特にレジストリなどは使っていないので、不要になったらファイルを削除すればアンインストールできます。
コマンド書式
プログラムに -h とか /h みたいなオプションをつけて起動すると、以下の使用法が表示されます。
SKKサーバ通信プロトコル
usage: skknet [udp/tcp/legacy] [client/server/send/recv] [stop] [...]
gate 数値(2)
host 名称(localhost)
port 数値(1178)
timeout 数値(500)
encode 名称(utf-16)
プログラムを何も引数をつけずに起動すると、ゲート3のUDP自律分散モードで起動します。
以下の2つのコマンドを同時に起動した場合と同じ動作になります。(自律モードは1プロセスしか消費しないのでメモリ使用量が少なく、コマンド一発で起動と停止ができるというメリットがあります)
skknet send gate 3 udp 255.255.255.255 port 1179
skknet recv gate 1 udp port 1179
引数が「udp」「tcp」「legacy」の場合は接続プロトコルの指定となります。(デフォルト: udp)
legacyを指定した場合は、旧SKKサーバのプロトコルを使用します。
引数が「server」の場合はサーバ動作を行います。「recv」の場合は受信ノード動作を行います。ローカルホストの指定ポートを使って待ち受けを行います。
「数値」または「gate 数値」を指定した場合、ゲート番号の指定となります。(デフォルト: 2)
「stop」と併用すると該当ゲートを停止させます。
ゲートを明示的に指定した場合「client」と指定していなくても、サーバ起動のパラメータが含まれていなければクライアント動作を行ないます。「send」の場合は送信ノード動作を行ないます。
「文字列」または「host 文字列」を指定した場合、接続先ホスト名の指定となります。(デフォルト: localhost)
このオプションを指定した場合は(サーバ起動のパラメータを指定しなかった場合は)、クライアント動作を開始します。
「port 文字列」を指定した場合、接続先ポート番号の指定となります。(デフォルト: port 1178)
「timeout 数値」を指定した場合、タイムアウト時間(ミリ秒単位)の指定となります。(デフォルト: timeout 500)
デフォルト値はローカルネットワーク内でのUDPによる利用を想定した短い間隔に設定されています。
地球を半週したり衛星軌道間で命のやり取りをする程度の距離や、TCPのような無駄にまみれたプロトコルをわざわざ使ってまで運用しなければならない特殊な場合においては、調整が必要となる可能性が生じることに留意してください。つか伸ばせば伸ばすほどエラー時の反応が鈍化するだけ。この値を弄るくらいなら、もっと別の方法(事前に辞書を転送しておくとか)を考えるべき。オーケー?
引数が「encode 名称(utf-8, sjis等)」の場合は文字エンコーディング指定となります。(デフォルト: utf-16)
サーバ・クライアント両者のエンコードは必ず一致させておかないと
エターナルフォースブリザードが吹き荒れます。相手は死ぬ。
文字セットは.NET Frameworkの内部処理に完全に依存しているので注意が必要です。euc-jpを指定することもできますが、.NET Frameworkの文字エンコードは世間一般のEUC-JPとは異なるので文字化けに注意してください。
- 警告 .NET FrameworkのEUC-JPに注意!
.NET Frameworkが言うところのEUC-JPの文字セットは、SKKが使っているEUCとは微妙に違うような気がするので、あんまり過信しちゃダメよ❤
というか……絶対間違いなく確実に微妙で気づきにくい上にとんでもない文字化けを起こすのは確定的に明らかなので、EUC-JPの使用は実験用途のみに限定し、一般業務など重要な用途には絶対に使わないようにしてください。
通常の用途には、文字化けが理論上発生しないutf-7/utf-8/utf-16/utf-32等を指定するようにしてください。
つか.NET Frameworkはいい加減まともなEUCのエンコーダ・デコーダを標準搭載してくれ……
他にもいろんな指定ができるようになってるんだけど(例えば、serverという名前のホストに接続しようとするとサーバ起動モードになっちゃうので、回避するためにhost serverと指定する、みたいなテクニックとか)、詳しくはソース参照ってことで。
コンパイルについて
このプログラムはC#で書かれているため、前述のインストール手順で書いたように、実行前に一度コンパイルを行う必要があります。
.NET Frameworkさえインストールされていれば誰でも簡単にコンパイルできます。
- C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe
などのコンパイラが大抵のパソコンには標準で入っています。
ソース修正時などに便利な自動Make機能つきバッチファイルを用意してあります。
skknet.bat経由で実行すれば必要に応じて適当なコンパイラを探して実行してくれます。
要するに、VisualStudioとか持ってなくても、普通に普段通りコマンドを実行するような形で
skknet tcp server
……みたいに実行すれば、skknet.batがソースを自動でコンパイルしてそのままexeファイルが起動して、localhostの1178番ポートでTCPサーバが起動するので安心ってことじゃよ!
ファイヤーウォールの設定確認ダイアログについて
ちなみに、ネットワーク関連のプログラムなので、サーバ動作をさせた場合、初回実行時はファイヤーウォールの通信許可の確認ダイアログがニョッキリ出てくるはずです。
慌てず騒がず許可ボタンを押しましょう。
よし、許可したな?あとは自己責任だぜ?
SKKGateの基礎知識
現在の版はもっと簡単に使えるようになってるので、とりあえず2017.03.27の開発履歴を参照して使ってみるべし。
SKKNetを使うにはSKKGateの知識が不可欠です。まずはSKKGateの動作について説明します。
- 図1.まだない
図1はSKKFEPを導入した直後の状態を示しています。
SKKFEPはアプリケーション上に、入力プロセッサとして実装されています。
アプリケーションで漢字変換を行う際、SKKFEPはゲート0→ゲート1の順に接続可能なゲートを探し、最初に見つかったゲートに接続してプロセス間通信(IPC)を行います。(ちなみに、ここでいう「通信」とは、プロセス間通信(IPC)のことでありTCP/IPによる通信とは異なります。以降はIPCと記述して区別します)
SKKFEP導入直後は、ゲート0は存在せず、ゲート1で辞書プロセスが動作している状態です。
このため、SKKFEPは常にゲート1の辞書プロセスとIPCを行ないます。
- 図2.まだない
図2はその後SKKGate拡張スクリプトを導入した直後の各ゲート間の接続状況を示しています。
SKKGate拡張スクリプトはゲート0として動作します。
よって、拡張スクリプトが起動すると、SKKFEPはゲート1の辞書プロセスとは直接のやり取りを行わず、ゲート0の拡張スクリプトとIPCを行うようになります。
SKKFEPから変換などの指示をもらったSKKGate拡張スクリプトは、必要に応じてゲート1の辞書プロセスと通信を行い、結果をゲート0のSKKFEPに報告します。またその際、数値変換やスクリプト実行変換などを行って加工した結果をゲート0に返すことで、さまざまな追加機能を実現します。
SKKGate拡張スクリプトには、「ゲート1の変換結果が空だった場合、ゲート2に接続して変換してみる」という機能が搭載されています(これはオンライン検索機能よりも前から搭載されている仕様で、過去にはここにオンライン検索機能を接続するようになっていました)。
辞書プロセスで変換失敗した時、図2の状況では、ゲート2には何もないため、SKKGate拡張スクリプトはゲート2とのIPCに失敗し何も起こりません。
要するに――
- 「どうしよう。辞書データが見つからなくて変換できない。このままでは辞書登録モードになっちゃう。はやく助けて?」
というドジっ子SKKFEPちゃんからの救難信号が人知れずゲート2に向けて投げられていたのです。
だったら
外部のSKKサーバに接続する機能を持ったゲート2を用意して救援要請を投げつけてやればいいじゃん?というのが物語の第一章になります。
第二章では舞台は世紀末となり、SKKGateや辞書プロセスが滅亡した後の荒廃した世界に死兆星が輝きます。SKK辞書データを一切持たない、全ての記憶を失ったピュア端末マシンに果たして再び日本語入力ポエムが書けるようになる日は訪れるのか?待て!次号!
コマンド実行例『起動編』
具体的な接続形態と、コマンドの実行例を紹介する。
まず、一番簡単な例として、
端末alice♺上にゲート2を増設し、サーバbob上のSKKNetサーバへ接続する例を図3に示す。
- 図3. やっぱりまだ書いてないけどな
以下の2つのマシンを準備する。pingコマンド等を実行し、通信の疎通を確認しておく。
- サーバbob(IPv4アドレス 10.0.0.1)
- 端末alice(IPv4アドレス 10.0.0.2)
まず、サーバマシンbob上にSKKFEPとSKKNetをインストールする。
次に、サーバマシンbobの辞書フォルダに、顔文字辞書やニコニコ大百科辞書などを追加した状態にしておく。これにより、サーバマシンbob上のゲート1(辞書プロセス)には、端末aliceよりも多くの辞書データが搭載された状態となる。
この状態で、サーバマシンbob上でシェル(コマンドプロンプト等)を起動しSKKNetサーバを起動してみよう。
bob> skknet server 1
これにより、bobはSKKNetプロトコルでUDPパケットを受け付け、ゲート1(bobが内蔵しているSKKFEPの辞書プロセス)に接続するようになる。以降はサーバbobと呼ぶことにする。
ついでに、さまざまなプロトコルで通信を受け付けられるように、何枚かシェルを起動し他のポートにSKKサーバを起動してみよう。
bob> skknet tcp server gate 1
bob> skknet legacy server port 1178 encode euc-jp gate 1
こうして、サーバbob上には、3つのサーバプロセスが起動した状態となった。
- bob(10.2)のUDPポート2000: utf-16のSKKNetサーバ
- bob(10.2)のTCPポート2000: utf-16のSKKNetサーバ
- bob(10.2)のTCPポート1178: euc-jpの旧SKKサーバ
もし、上記のポートがインターネットに公開されている場合、世界中からこのゲート1に接続できてしまうわけである。
- ご注意
一般家庭向けのインターネットサービスを利用している場合、ホームゲートウェイ内に隔離された状態でコンピュータが動作しているため、ここの例に書かれているコマンドだけでは、家の外からは簡単には接続できません。
どうしてもインターネット側と接続したい場合は、ホームゲートウェイの設定変更、いわゆるポート開放などを行う必要があります。他にもVPNとかいろんな技術があってたのしく侵入経路を増やせるよ!
SKKサーバは簡単に増設できる、ということを示すのがこの章の狙いであるため、この例はあまり実用的ではないかもしれない。
ぶっちゃけどれか1つあれば十分なので、実際に使う場合は、どれか1つ好きなものを選んで使うとよい。
今回の例では、学習状態のやりとりはネットワーク上に出現しないため、旧式のSKKサーバでも十分に活用することができる。
次に、端末alice上に、サーバbobに接続するためのSKKNetクライアントを起動する。
端末alice上には、SKKFEPおよびSKKGate拡張スクリプトをインストールしておく。
この状態だと、前章の解説にあるように、端末alice上の辞書に存在しない単語を変換した場合、ゲート2へに対して救援要請(検索)が発生する。この接続をサーバbobに中継してやることで、サーバbob上の豊富な辞書データを端末aliceが受信することができるようになる。
alice> skknet 10.2
もしくは
alice> skknet tcp 10.2
もしくは
alice> skknet legacy 10.2 port 1178 encode euc-jp
上記3つは、それぞれbob上の3種類のサーバに対応したクライアントの起動方法である。
3つの記述は、どれもすべてデフォルトのゲート2として起動する。
ゲートは同時利用はできないため、一度に実行できるのはどれか1つだけとなる。(実際に使う場合は、応答性能の良いUDPクライアントがお勧めである)
ここで1つ使用上の注意がある。skknetのプロセスは一度起動したら、基本的には通信エラーなどが発生しようが何が起ころうが電源を切るまで停止せず、ずっと走り続けてしまう。
ゲートの利用は排他なので、1度起動したskknetのプロセスはずっとそのゲートを占有してしまう。
上記3種類のコマンドのように、何度か実行しなおして1つのゲートを違う設定で使いたいという場合は、シェル上で
CTRL+
Cと入力して強制的にskknetのプロセスを停止し、再度実行する手順が必要となる。
つまり、上記3種類のコマンドを切り替えて試してみる場合、コマンドを実行しては
CTRL+
Cで停止、という操作手順を繰り返すことになるわけだ。
さて、これで準備が整ったので、端末alice上でサーバbob上の辞書にしかない単語を変換してみると、bob上の辞書を読み取れていることが理解できるはずだ。
遠い未来、人類が滅亡のピンチに陥ってしまった時、衛生軌道上の人工知能aliceやbobと通信する時にはこの操作手順が役立つかもしれない。
それでは通信を終える。
- 一方ロシアは全ての辞書ファイルをローカルマシンにコピーした。
もっとぶっちゃけると、そもそもこんなめんどくさい通信が必要な大仰なシステムなんてものは、メンテが大変で使いづらいだけの頭でっかちなシステムなので、こんなものを使わなくて済むようなシステム構成を考えることに頭を使うべきである、というのは言うまでもない。
コマンド実行例『世紀末編』
20XX年、世界はクラウドの闇に包まれた――
- だが、SKKNetは滅亡していなかった!
……とかやってると永遠に終わりそうにないので、簡単にテスト手順など。
ここはまだ実験中の段階なので、そのうち動作内容が変わったりするかもしれないけど勘弁な。
テスト手順
前章のサーバbobの設定を行なった状態にしておく。
端末alice上でSKKGateを停止させ、ゲート0を開放する
ひとまず、SKKGateのセットアップで停止を選択し、拡張スクリプトの実行を止める。
この状態では、ゲート0が停止した状態となっている。
ゲート1は生きているので最低限の日本語入力自体はできるが、オンライン変換などができない状態になっていることを確認する。
ここでおもむろに、alice上にゲート0を設置し、通信相手にbobを設定する。
alice> skknet 10.2 gate 0
これで、かつて拡張スクリプトが動作していたゲート0のかわりに、SKKNetによるゲート0が動き出す。
この状態では、SKKFEPの
変換・学習の要求すべてがゲート0を経由してサーバbobに転送される。
変換・学習すべての動作がUDPのデータグラムとしてネットワーク上を流れていることが観測できるはずだ。
この状態ではゲート1は一切使われないため、SKKFEPのセットアップで「メンテ」を押してローカルマシン上の辞書プロセスが完全停止した状態にしても、変換・学習が行えるという爽快なアンデッド気分を体験できたりもする。
alice> skknet bob gate 0 tcp
とやると、tcpプロトコルを使って通信が行なわれることが確認できる。
alice> skknet bob gate 0 legacy port 1178 encode euc-jp
とやると、旧SKKサーバは変換だけはできるけど学習が使えず、残念ながらこの用途では使いものにならないことも確認できる。
さて、ここまでは通信相手が外部にいる場合で試していたが、どうせテストなので全部alice1台でサーバ・端末両方を実行してみよう。
新しくシェルを3つ開いて以下のコマンドをそれぞれの画面で実行する。
alice> skknet server gate 1
alice> skknet tcp server gate 1
alice> skknet legacy server port 1178 encode euc-jp gate 1
これは要するにbob上で動かしてたのとまったく同じ指定で、aliceとbobは辞書内容以外は同じ動作をするようになる。どちらのマシン上でも、3種類のサーバプロセスが待機状態に入ったことになる。
あとは端末aliceからサーバaliceに対して接続するよう、接続先を10.2(bob)から127.1(localhost、つまりalice自身)に置き換えるだけでよい。
実はlocalhostの指定はデフォルト値として省略できるので、ゲート番号だけを書いてやればクライアントが起動する。以下のどれか1つを使えば、上で用意したlocalhost(自分自身)上の3種類のサーバに接続できるはずだ。
alice> skknet 0
もしくは
alice> skknet tcp 0
もしくは
alice> skknet legacy port 1178 encode euc-jp 0
ゲートの利用は排他なので、これも上記3つのどれか1つだけしか1度には実行できないので注意。
1台だけしかマシンがない時はこんな感じで使えるというサンプルということで。
通信プロトコル設計のための指標
過去のしがらみを断ち切り、SKKにおける通信のありかたをゼロから再設計するにあたり、以下の観点から実装を決定した。
通信の高速性
SKKは文節毎に人間が目視で内容を確認する操作スタイルであり、UIのレスポンスタイムが最重要である。このレスポンスタイムというのは通信時間だけではなく、文節の入力時間、解析時間、表示までを含む。そのため、将来複雑な日本語処理を行なうことを見越して通信時間がボトルネックとならないよう、最短時間で通信を完了させる通信レイヤ(プロトコルや物理層も含む)を確保できるよう設計することが望ましい。
通信の匿名性(への対応を見越した通信プロトコルの最適化)
ユーザ辞書の運用管理をネットワーク外で行うと想定した場合、通信の匿名化は最重要課題となる。これについては今回の試作では対象外とする。が、将来の実装を見越して次で述べる観点(最小サイズの通信量の実現)に注力することで、将来のオーバレイ化されたネットワーク環境下でも最速で動作できる下地を作っておく。
MMO化(SKK Online)を見越したスケール拡張性
認証、暗号化などを大規模マルチユーザで実現するために今できることは、とにかく通信効率を最適にしておくこと。これも次の観点の見直しによってその下準備とする。
モダンな通信機器における文字コード変換ロスの排除
文字コード変換ロスの問題。これは現状の日本語処理を行なう通信機器における「世界の歪み」ともいえる最大の問題である。
様々な利用ユースケースへの対応
- 位置情報の利用
- 近距離にあるモバイル機器の処理能力をサーバとして引き出す
- IPにべったりと依存せず、センサネットワーク用の物理層(Zigbee/Bluetooth等)でも使うにはどうしたらいいか?
余力のある環境では文字コードを自由に選択できる拡張性
既存のSKKサーバとの混在
UTF-8やEUC等の文字セットを自由に選べるようにした。特にUTF-8であればパフォーマンスを極力落とさずに実現できるはず。通信量が増えるのはUTF-xx系がラテン語圏優先でデザインされているため仕方ないが、回りがみんな使ってるとかいう安易な理由だけでUTF-8を最終解にしてはいけない。向こう10年の期間を考える限り、UTF-16以外の文字セットを日本語処理のRPC用途に使うのは愚の極み以外の何者でもない。
どんな文字セットも選択できる柔軟性を持つことが必要である。EUC固定だった従来のSKKサーバとは一線を画す幅広い接続性を、省電力かつ高性能かつ大規模に実現することができるのだ。
送受信処理を対称に書ける(最近の処理系で処理を共通化しやすい)単純な処理プロトコル
従来のサーバとの互換性やGoogle等の営利企業サービスとの接続性
UTF-8至上主義との戦い
UTF-8の最大の欠陥は日本語処理における通信量の増大にある。内部表現と比べ、日本語に関するデータ量はほぼ1.5倍に増加する。通信量が増えるということは、その前段階である変換処理のためのバッファサイズも増えるということを意味する。こうした前提条件は、メモリ残量がプログラマーの命より重いワンチップマイコン(家電やセンサ系)などの組み込み系の環境での開発コストや、データセンタ規模まで処理スケールを拡大した際のネットワークの構築・運用管理コストに直接影響するものである。じり貧のSKK処理系が巨大企業を低コストで打倒するためには、通信の最適化が最大の鍵となると言っても過言ではない。
とはいえ、UTF-8はUTF-16との相互変換が容易であり、変換におけるバッファメモリのサイズを無視できる環境であれば十分実用的な速度で動くことが期待される。よって処理負荷の問題の半分は解決できる。
日本語を理解しようとも思わないような外人連中(ひらがなが何たるかも判ってないようなUTF-8の仕様を決めたラテン語至上主義の連中)が作った処理系を無改造で使う場合などにおいて、UTF-8は絶大な威力を発揮するというのもまた事実である。電源が潤沢かつローカルネットワーク内のような小規模な運用においては悪くない選択であると考えられる。
なお前述の通り、現在ではモダンな処理系のほとんどは内部UTF-16への移行が完了しており、処理の8ビット化を強いられるという感覚はもはや時代遅れである。高速で大量の日本語処理(を含む多言語対応システム)の内部処理系を新規に作る場合において、UTF-8にこだわるのは愚の極みであると言えるだろう。
また、ネットワークにUTF-8を垂れ長すという選択はGoogleなどのWeb企業を増長させる結果にも繋りかねないため、選択は慎重に行う必要がある。
UTF-8の日本語文字列のサイズ肥大化をまるっと解決できる可能性を秘めたUnicode標準圧縮符号化
SKKサーバは、従来方式も今回提唱している新方式も、どちらも文字を使って通信文の区切りなどの情報をやりとりしている。
つまり、通信文が極端に短い場合(本文よりもヘッダが占める割合が高くなる稀なケース)ではUTF-16よりも通信効率が良くなる可能性がある。もっとも、打鍵数の少ない状態では補完候補のやりとりでかなりの回数と量の通信が発生するため、通信量が多くなるほどUTF-16のほうが有利になっていくことに変わりはない)
UTF-8のように、ヘッダや候補の区切り文字を1バイトで表し、かつSKKの変換のやりとりで多用されるひらがなをシフトJISやEUC-JPのように2バイト以下で表現し、かつUnicodeの全文字を正確にマッピングできる手法。そんな都合のよい夢のようなエンコーディングなんて、果たして存在するのだろうか?
あるんだな、これが。
その名は
Standard Compression Scheme for Unicode(SCSU)――
Unicodeの標準圧縮方式である。
これを使うことで、SKKの変換の読み(見出し)として使われることが多いひらがなの連続パターンは、先頭部分以外をほぼ1バイトで表すことができるようになる。また、アスキーコード0x7F以下の文字については、例外なくそのままの文字コードとして出力されるため、日本語部分の前後に生じる区切り部分は、デコードしなくてもバイナリ列を判別しやすい(これは、ビットエラーやパケット欠損などの通信障害発生後の復旧が、他のバイナリ圧縮方式よりも容易であることを意味する)。
SKK辞書サーバでの通信量削減を考えた場合、理想的といってよい性能を持つ符号化方式である。
今回の実装では未対応だが、将来.NET Frameworkの改良が進み、SCSUが標準のエンコードの1つとして取り込まれる世界線に到達できれば、現在のソースそのままで、エンコード名称をSCSUとするだけで動くようになってしまう可能性だって微粒子レベルで存在する……かもしれない。
ひとまず、ファイルレベルでのSCSUやBOCU-1の扱いについては
汎用文字コード圧縮ツールucの実装を用意してあるので、どんなものか興味がある人はぜひ使ってみて欲しい。
通信フォーマット
暫定版。この先生キノコるためにはいくらでも修正してやるッ!
クライアント要求 | サーバ応答 | 内容 |
0 | (切断) | 遺産 |
1読み\x20 | 成功時: 1/候補[/候補...]/\n 失敗時: 4\n | 〃 |
2 | 名称\x20 | 〃 |
3 | 番地\x20 | 〃 |
4読み\x20 | 成功時: 1\x20候補[\x20候補...]\x20\n 失敗時: 4\n | 〃 |
|
[予約...]\0 | [予約...](切断) | 判定 |
\1読み[\1送りがな][\4属性]\0 | \1[予約...][\3候補[\2註釈][\3候補...]]\0 | 変換 |
\2読み[\1送りがな][\2読み...][\4属性]\0 | \2[予約...][\3候補[\2註釈][\3候補...]]\0 | 補完 |
\3読み[\1送りがな]\3候補[\2註釈][\4属性]\0 | \3[予約...]\0 | 確定 |
\4読み[\1送りがな]\3候補[\2註釈][\4属性]\0 | \4[予約...]\0 | 削除 |
- 自律分散モード
送信ノード(クライアント) | 受信ノード(サーバ) | 内容 |
[予約...][\5ノード名...]\0 | 実装不要 (自ノードを追加して転送可) | 離脱 |
\1読み[\1送りがな][\2予約...][\3結果...][\4属性][\5ノード名...]\0 | 実装不要 (自ノードを追加して転送可) | 変換 |
\2読み[\1送りがな][\2読み...][\3結果...][\4属性][\5ノード名...]\0 | 実装不要 (自ノードを追加して転送可) | 補完 |
\3読み[\1送りがな][\2予約...]\3候補[\2註釈][\4属性][\5ノード名...]\0 | 応答不要 (自ノードを追加して転送可) | 確定 |
\4読み[\1送りがな][\2予約...]\3候補[\2註釈][\4属性][\5ノード名...]\0 | 応答不要 (自ノードを追加して転送可) | 削除 |
このモードは全てのノードが独自の辞書を持ち自律分散処動作していることを前提としている。
変換と補完は自ノード内で動作が完結するため、通信の必要はない。
- 候補内に使用不可能なコントロールコード (0x00〜0x07)
0x00 | 通信の区切り |
0x01 | 送りがなの区切り |
0x02 | 注釈の区切り |
0x03 | 候補の区切り |
0x04 | 属性の区切り (SKKNetのみ) |
0x05 | 経路の区切り (SKKNetのみ) |
0x06〜0x07 | 予約 |
- 候補内に使用可能なコントロールコード (0x08〜0x1F)
0x08 BS | \b |
0x09 HT | \t |
0x0A LF | \n |
0x0B VT | \v |
0x0C LF | \f |
0x0D CR | \r |
0x0E〜0x1F | 利用可 |
- 通信プロトコルを設計する上での注意点
- SKKGateの命令評価や連文節変換への対応などで空白文字も必要となることを考慮すること
- 候補に改行やタブを含める利用形態を考慮する(候補の区切りにはこれらの文字を使ってはならない)
- 一部のJavaScriptの処理系ではBSを含む文字列のSplitが正常に動作しないので区切りに使わない
TCP向けに用意されているサーバ切断のプロトコルは資源の無駄以外の何者でもない。よってSKKNetクライアントでは切断コマンドの類は一切サポートしない。
こんなプロトコルを通すためにTCP層よりも上まで無意味なデータを垂れ流すよりも、とっととFINを叩き付けてTCP層以下ですべてを完結させるべきである。
こんな切断方式をクライアント側で採用するようなセンスの悪い奴は今すぐ腹を切るべき。あっ、そういえばSKKNetも
CTRL+
@連打で切れるようにしておいたので――アッー(腹ブシャー)
更新履歴
2012.11.29
ゆるふわC#試作版爆誕。
2012.11.30
クライアント動作時にゲート指定が無視されるバグを修正。
フラグメントパケットのルーティングが禁止されている環境でも通信できるよう対抗処理を追加。
2012.12.01
引数のエラーチェックを追加。バイナリサイズを短縮。
2012.12.04
タイムアウト指定を追加。
2013.01.07
属性を追加。
2013.01.24
コメントの間違い(更新→削除)を修正。
2016.02.13
あれから3年――
SKKGateの仕様変更(改行対応)に合わせてプロトコルを変更。
従来のUDPに加え、TCPによる通信処理も実装。
旧SKKサーバ互換のサーバ/クライアントを実装。
ちなみに旧SKKサーバ/クライアント互換機能はSKKNetの通信エンジンを経由して実現している。
そのおかげで、本来1バイト単位の扱いを基本とする限定的な文字セットしか考慮されていなかった旧SKKサーバを、UTF-16やUTF-32などの複数バイト単位の文字セットで駆動する、なんていう前代未聞な挙動にも対応しちゃってたりする。
でも、まー、そこまでするならSKKNet本来のプロトコルを使うべしってことで。
2017.03.12
SKKGateの仕様変更に合わせてプロトコルを変更。
属性値(0〜31)が10以上の場合はアルファベットで補い36進数1桁として表記できるよう変更。従来の10進数表記も解釈できるようになっている。
ブロードキャストやP2Pといった自立分散動作するノード間での通信方式を追加。
デフォルトのポート番号を1178に変更。
2017.03.13
アイコンを追加。
2017.03.14
自動コンパイル用バッチファイルをさらに小型化。
2017.03.27
何も引数をつけずに実行すると、自律分散モードで常駐するよう改良。オプションを指定して起動した場合は従来通りの動作となる。
付属バッチファイルによるデフォルトのコンパイルオプションを変更し、他のSKKFEP関連拡張ユニットと同様にGUIアプリとしてバックグラウンド動作できるようにした。
さらに、ゲートが既に動作中の場合はstopを引数につけなくても、同時起動時に停止メニューを出すようにした。
これにより、エクスプローラでダブルクリックするだけで使えるようになった。スタートアップなどに登録すればそのまま常用もできる。
自律分散モードで起動すると
SKKGate用の拡張ゲート3と接続し、UDPポート1179のブロードキャスト送受信機として機能するようになる。
拡張スクリプトと連動し、
同一サブネット内の隣接する端末間でリアルタイムに学習状態を共有できるようになる。
実行は拡張モジュールのマルチスレッド化が必須なので、
SKKFEP本体と拡張スクリプトの両方を最新版に更新する必要があるので注意。
例えば、同じブロードキャストドメインにSKKNetが動作しているノートPCが複数台いる場合、どれか1台が学習を行うと学習内容がUDPブロードキャストで他のマシンに伝搬し、全機が同時に学習するようになる。
一切の設定なし、かつ、最小限の通信量でサブネット内の全ての端末の変換動作を同一に保つことができるようになるというわけだ。このシステムデザインは、
ユーザ辞書ファイルの同期を用いるSKKSyncとは対極のコンセプトであると言えるだろう。
ネットワーク屋とかが見たら当たり前の挙動なのかもしれないけど、実際に動かしてみると「お前達は同化される」「抵抗は無意味だ」みたいな光景が見れる。操作してないから何も覚えていない……はずだった端末が、少量のパケットだけで予測変換の挙動まで一致するほど同期している様子はわりと衝撃的かもしれない。
DHCP♺などと同じようにUDP平文で動作するので、ホームGWとかWiFi Ad-Hocネットワーク等の環境で実験用として使うべし。
co (Twitter♺)