アプリケーション層カスタムプロトコルとシリアライゼーションの実装

プロトコルの基本概念

プロトコルは通信双方向のデータ構造を定義する約束事です。ソケットAPIは文字列単位でデータを送受信しますが、構造化データを送信するにはシリアライゼーションが必要です。

バイナリ形式での直接送信は、アーキテクチャの違い(例:リトルエンディアン/ビッグエンディアン)により互換性が失われるため推奨されません。

シリアライゼーションとデシリアライゼーション

基本メカニズム

構造体を文字列に変換するプロセスをシリアライゼーション、文字列を元の構造に戻すプロセスをデシリアライゼーションと呼びます。

例: struct Data { int x; char op; int y; } という構造体を "5 + 3" という文字列に変換する方法がシリアライゼーションです。

JSONを用いたシリアライゼーションの実装

シリアライザの選択

JsonCppライブラリのJson::StreamWriterBuilderを推奨します。

#include <iostream>
#include <sstream>
#include <json/json.h>

int main() {
    Json::Value payload;
    payload["username"] = "Alice";
    payload["age"] = 30;
    
    Json::StreamWriterBuilder builder;
    std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
    std::ostringstream os;
    
    writer->write(payload, &os);
    std::cout << os.str() << std::endl;
    return 0;
}

出力結果:
{
"username" : "Alice",
"age" : 30
}

デシリアライゼーションの実装

Json::CharReaderを使用した安全な解析例:

bool parse_data(const std::string& json_str, Json::Value& result) {
    Json::CharReaderBuilder builder;
    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
    std::string errors;
    
    return reader->parse(json_str.c_str(), 
                       json_str.c_str() + json_str.size(),
                       &result, &errors);
}

TCPの全二重通信メカニズム

TCP接続には送信バッファと受信バッファが存在します。この構造により、送信と受信を同時に処理できるため全二重通信が実現されます。

データの管理は、送信キューと受信キューの2つのリスト構造で行われます。各キューはデータの順序を保持し、TCPプロトコルがフロー制御やコングェスション制御を管理します。

デーモンプロセスの実装

デーモンの特徴

デーモンプロセスはターミナルから分離され、ログインセッションの終了や親プロセスの終了に影響されません。

デーモン化の手順

  1. fork()で子プロセスを作成し、親プロセスを終了
  2. setsid()で新しいセッションを開始
  3. カレントディレクトリを/に変更
  4. 標準入出力を/dev/nullにリダイレクト

実装例

#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

void create_daemon(bool change_cwd, bool close_fds) {
    signal(SIGCHLD, SIG_IGN);
    if (fork() > 0) exit(0);
    setsid();
    
    if (change_cwd) chdir("/");
    
    if (close_fds) {
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
    } else {
        int null_fd = open("/dev/null", O_WRONLY);
        dup2(null_fd, STDIN_FILENO);
        dup2(null_fd, STDOUT_FILENO);
        dup2(null_fd, STDERR_FILENO);
        close(null_fd);
    }
}

タグ: jsoncpp Daemon TCP Serialization linux-system-programming

7月17日 22:20 投稿