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

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

前回はWebSocket クライアント を作成したので
今回はWebSocket サーバー を作成する。

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

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

Sec-WebSocket-Accept は リクエストの Sec-WebSocket-Key の値(文字列)に
固定の文字列("258EAFA5-E914-47DA-95CA-C5AB0DC85B11") を連結したものの SHA1ハッシュ値Base64 エンコードしたもの

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

void gen_accept_key(char* seckey)
{
    const char GUID[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
    size_t guid_len = strlen(GUID);
    size_t seckey_len = strlen(seckey);
    size_t buf_len = seckey_len +  guid_len;
    char buf[buf_len+1];
    memcpy(buf, seckey, seckey_len);
    memcpy(&buf[seckey_len], GUID,  guid_len);
    unsigned char digest[SHA_DIGEST_LENGTH];
    gen_sha1( (char *)buf, buf_len, (unsigned char *)digest);
    char* key = base64Encode( (char *)digest,  SHA_DIGEST_LENGTH);
    strcpy(accept_key, key);
    free(key);
}

C言語にて Openssl を使って SHA1 ハッシュを計算する

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

Sec-WebSocket-Key の値が
"dGhlIHNhbXBsZSBub25jZQ=="
のとき
Sec-WebSocket-Acceptの値は
"s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
となる。

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

次は text(文字列) のエコーバック

ペイロードは XOR 暗号化されているので 復号する。

XOR 暗号化と同じ処理を行うと復号さ れる。

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

parse_payload(char* data,  size_t data_size)
{
    size_t payload_len = data[1] & PAYLOAD_LEN_MASK;
    char mask_bit = data[1] & B_MASK;
    char payload[payload_len];
    if(mask_bit){
        for(int i=0; i<payload_len; i++){
                int j1 = FRAME_BASE_SIZE + FRAME_MASK_SIZE + i;
                int j2 =   FRAME_BASE_SIZE + (i % 4);
                payload[i] = data[j1] ^ data[j2];
        }
    } else {
        memcpy(payload, &data[FRAME_BASE_SIZE], payload_len);
    }
}

クランアントから任意のタイミングで任意のメッサージが送られてくる。
ループ処理で待ち受けする。

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

for(int i=0; i<LOOP; i++){
read_size = tcp_read( sockfd_client, read_data,  DATA_SIZE);
if(read_size >= 2){
char opcode = parse_frame(read_data, read_size);
if(opcode == OP_TEXT){
  proc_text(sockfd_client, read_data, read_size );
} else if(opcode == OP_CLOSE){
  proc_close(sockfd_client, read_data, read_size );
} else if(opcode == OP_PING){
  proc_ping(sockfd_client, read_data, read_size );
}
}
}

前回作ったクライアントから接続して動作確認する。
無事動いた。

Web Brawser から接続したとき JavaScript でクライアントを記述した html を返送する処理を追加する。

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