ライブラリ添付の sample/http_raw/websocket_echo を参照しながら、WebSocket サーバーの具体的な実装手順を説明する。
サンプルプログラムは、クライアントから送られたデータをそのまま返答するだけのサーバーである。 プログラムの概略は次のようになっている:
Server::init で Serverオブジェクトの初期化を行う。Handler オブジェクトは外部から貰う。
bool Server::init(Handler* h)
{
{
typedef nine::HttpServer super;
nine::HttpServerConfig cfg;
cfg.nAcceptors = 1;
cfg.nCommunicators = 20000;
cfg.keepAliveTimeoutSec = 0;
if (! super::initialize(&cfg)) { return false; }
}
まずは、HttpServer の初期化を行うため、super::initialize() を呼ぶ。 HttpServerConfig の内容は固定的に設定している。
m_pHandler = h; return true; }
HttpHandler と同様の方法で、WebSocket のデータを扱う WebSocketHandler を指定する。
前節の init で記憶しておいた、m_pHandler メンバをそのまま返している。
nine::WebSocketHandler* Server::authorizeWebSocket(int cid, const nine::HttpRequest* pReq)
{
return m_pHandler;
}
virtual void onWebSocketReceive(WebSocketChannel*, const WebSocketHeader*, Buffer* pBuf) = 0;
void Handler::onWebSocketReceive(nine::WebSocketChannel* pChannel, const nine::WebSocketHeader*, nine::Buffer* pIn)
{
size_t size = pIn->getReadableSize();
nine::Buffer* pOut = pChannel->beginFrame00(size);
nine::Buffer::IoVec iov;
while (0 < pIn->getReadBuffers(&iov, 1)) {
pOut->writeArray(iov.ptr, iov.size);
pIn->skipRead(iov.size);
}
pChannel->endFrame();
size_t size = pIn->getReadableSize();関数に与えられた Buffer オブジェクト(pIn) は、受信したフレーム全体が入っている。 よって、getReadableSize() でフレームペイロードのサイズを得ることができる。
nine::Buffer* pOut = pChannel->beginFrame00(size);WebSocket の送信は、beginFrame00() で開始する。 パラメータにはフレームペイロードのサイズを渡す。 戻り値として、指定したペイロードサイズを書き込めるだけの Buffer オブジェクトを受け取る。 後はフレームの内容をこの Buffer へ書けばよい。
なお、Frame00 に書ける内容は 0x00 以外の UTF-8 文字列である。 nine は内容が正しいかどうかを検出しないので、アプリケーションの責任で正しい内容を書き込む必要がある。 このサンプルプログラムでは、受信フレームの内容をそのまま送信している。受信フレームも 0x00 フレームであり、その中身は適切なものになっていると期待できる。
nine::Buffer::IoVec iov;
while (0 < pIn->getReadBuffers(&iov, 1)) {
pOut->writeArray(iov.ptr, iov.size);
pIn->skipRead(iov.size);
size -= iov.size;
}
受信した Buffer 内容を、送信用の Buffer へ書き出している。
pChannel->endFrame();送信フレームの内容を書き終えたら、endFrame() を呼ぶ。