C言語にてソケット関数を使って WebSocket クライアント を作成する

Boost.Beast で WebSocket を試す
の続きです。

前回は TCP通信のやり取りを解析したので、

今回はC言語のソケット関数を使ってクライアント を作成してみます。

すでにやっている人がいて どこかにコードがあると思ったが、見つからなかった。

下記のプロトコルの説明を参考にして、独自に実装した。

RFC 6455 日本語訳

mozilla: WebSocket サーバーの記述

qiita: WebSocketについて調べてみた

まずはオープニングハンドシェイクから。

GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

Sec-WebSocket-Key は任意の文字列を Base64 エンコードしたもの。

コードは下記のようになる。

gen_ws_key(size_t size)
{
    char rand[size+1];
    for(int i=0; i<size; i++){
        rand[i] = getRandomCharMix();
    }
    char *b64 = base64Encode(rand, size);
}

C/C++ にて ランダムな文字列を生成する

C言語にてバイナリデータを Base64 文字列に変換する

Sec-WebSocket-Key 以外は固定値でよい。

オープニングハンドシェイクは 昔書いたコードの応用でいける。

C言語にて ソケット関数を使って ウエブサーバを作成する

次は text(文字列) の送信。
ここはバイナリのフレーム形式なので、ちと面倒。

0バイト目: FIN ビット + OPコード
1バイト目: MASK ビット + ペイロード
2バイト目から5バイト目: MASK バイト(4バイト)
6バイト目以降: ペイロード(文字列送信する文字列を XOR 暗号化したもの)

wikipedia: XOR暗号

コードは下記のようになる。

build_frame_text(char* text)
{
data[0] =  (unsigned char)(FIN | opcode) ;
data[1] = (unsigned char)(MASK | text_len);
for( int i=0; i<4; i++){
      data[i+2] =  mask[i];
}
for( int i=0; i<  text_len; i++){
      data[i+6] = text[i] ^ mask[i % 4];
}
}

前回試した Boost.Beast の WebSocket サーバーに接続して動作確認する。
無事 動いた。

ちょっとまごついたところ。

プロトコルの表記では FINビットは 0ビット目である。
コードでは 0x80 のように最上位ビット(7ビット目)を立てる。
TCP通信は上位ビットから送るビッグエンディアンである。

電算用語の基礎知識: ネットワークバイトオーダー

コード全文は Github に公開した
https://github.com/ohwada/MAC_cpp_Samples/tree/master/network/ws_client