18
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Delphi 用 WebSocket 作った

Last updated at Posted at 2019-04-10

結論を急ぎたい人は↓へ

WebSocket のリポジトリ
https://bitbucket.org/freeonterminate/websocket/

Discord のボット作りたい

「Discord のボット作りたい!」その思いから僕の旅は始まった…

Discord Gateway API は WebSocket を使う

Delphi にも WebSocket ぐらい作ってる人いるっしょ!見たことあるし!楽勝!楽勝!
あったあった

DelphiWebsockets
https://github.com/andremussche/DelphiWebsockets

ん?何か書いてあるぞ…

原文
Not active anymore
Unfortunately I don't have time to support this project anymore.
Also the websocket protocol has changed in the meantime, so it won't work with browser and other modern implementations.
翻訳
もうアクティブではありません
残念ながら、私はもうこのプロジェクトをサポートする時間がありません。
また、その間にwebsocketプロトコルも変更されたため、ブラウザやその他の最近の実装では機能しません。

ほげえ!
あ、でも代替手段が書いてある!

Please take a look at the free (but closed) 3rd party component:

http://www.esegece.com/websockets/download
http://www.esegece.com/download/sgcWebSockets_free.zip

よっしゃー
アクセスや!

image.png

image.png

デスヨネー…
個人的使用であれば安いし(€199.00 ⇒ 約25000円)普通に買うんだけど…Discord Bot のソースはオープンにしたいんだよなあ。
これじゃあなあ…

WebSocket を実装する

こうなったら自分で実装するしかあるまい…
幸い RFC 6455 は日本語訳がある!

RFC 6455, The WebSocket Protocol (非公式日本語訳)
https://triple-underscore.github.io/RFC6455-ja.html

WebSocket の詳細については上記の文書に記載されているので詳しくは書きませんが、実装時についての注意点をいくつか。

1. IdTCPClient を使う

WebSocket は HTTP を拡張した物ですが、ネゴシエーションを HTTP ヘッダで行います。
そのため通常の THttpClient は使えず自分で HTTP を偽装しなくてはなりません。
そこで TCP Socket の出番ですが、Delphi RTL で用意されている TSocket は SSL 対応されていないため、使えません。
そこで、Indy の TIdTCPClient を使います。
ただ、TIdTCPClient で SSL 通信するためには以下のライブラリが必要です。

OS ライブラリ1 ライブラリ2
Windows libeay32.dll ssleay32.dll
macOS libcrypto.dylib libssl.dylib
iOS libcrypto.a libssl.a
Android libcrypto.so libssl.so
Linux libcrypto.so libssl.so
※iOS は静的リンクの必要あり

これらの必要なライブラリはこのリンクから落とせます
ただ、将来的に RTL の TSocket が SSL 対応されれば、そちらを使いたい(DLL が要らないため)ので、TIdTCPClient をラップして使っています。

2. ws, wss を http に偽装する

WebSocket のスキーム ws, wss は本質的に http, httpsです。
上記の TIdTCPClient で接続する時は ws://wss://http://, https:// に置換します。

3. 終了時に Close コードを送出

WebSocket は終了時に Close コード 8 をコネクション相手に送る必要がありますが、これがまた難しい。
FormDestory ⇒ WebSocket.DisposeOf ⇒ SocketThread.Terminate ⇒ 8 の送出、と手順を踏みますが、Socket の都合上 Thread から 8 を送出するようになっています。
それによって、8 送出より先に WebSocket のインスタンスだったり Form のインスタンスが削除されてしまいエラーになります。
この問題を解決するため終了処理がえらいめんどくさい事になっています。

4. コンソールでは TThead.Queue は使えない

GUI アプリで言うところのメインスレッドが無いため TThread.Queue は使えません。
そのため、CreateAnonymousThread を使って遅延処理を実現しています。

5.手抜きポイント

複数フレーム対応は省略しています。
WebSocket は「フレーム」という単位で通信するのですが、送るデータの量によっては複数のフレームに分けて転送します。
分ける分けないは自由ですがフレームで保持できるデータ量を超えた場合は必ず分けないといけません。
…ですが、その分ける値というのは Unsigned Int64 で表せる 18446744073709551615= 15 [EiB] です。
これ超える事ある!?!?ということで、送信時の複数フレームに対する対応はしていません。

他にも 126 文字以上になった時の文字列長データの並び順とかいくつかはまったポイントがありました。

WebSocket の使い方

そんなこんなでできあがった WebSocket の簡単な使い方を紹介します。

生成と破棄
// WebSocket の作成
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  FWebSocket := TWebSocket.Create(Self);
  FWebSocket.OnError := WebSocketError; // エラーイベント
  FWebSocket.OnText := WebSocketText;   // 文字列受信イベント
end;

// WebSocket の破棄
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
  FWebSocket.DisposeOf;
end;
接続と通信
// 接続
procedure TfrmMain.Button1Click(Sender: TObject);
begin
  FWebSocket.Connect('wss://echo.websocket.org');
end;

// 送信
procedure TfrmMain.Button2Click(Sender: TObject);
begin
  FWebSocket.Send('Hello, WebSocket !');
end;

// 受信
procedure TfrmMain.WebSocketText(Sender: TObject; const iText: String);
begin
  Memo1.Lines.Add(iText);
end;
エラー処理
// エラー処理(ログに出すだけ…)
procedure TfrmMain.WebSocketError(Sender: TObject; const iMessage: String);
begin
  Memo1.Lines.Add('ERROR: ' + iMessage);
end;

まとめ

一度は自分で車輪を作ってみるのもいいよ!

18
15
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?