プロトコルの基本概念
プロトコルは通信双方向のデータ構造を定義する約束事です。ソケット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プロトコルがフロー制御やコングェスション制御を管理します。
デーモンプロセスの実装
デーモンの特徴
デーモンプロセスはターミナルから分離され、ログインセッションの終了や親プロセスの終了に影響されません。
デーモン化の手順
fork()で子プロセスを作成し、親プロセスを終了setsid()で新しいセッションを開始- カレントディレクトリを
/に変更 - 標準入出力を
/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);
}
}