ビルド環境の準備
Debian系OSを基盤とし、ネットワーク通信とJSON処理に必要な開発パッケージを導入します。
# キャッシュ更新
sudo apt update
# コアビルドツールチェーンのインストール
sudo apt install build-essential
# コンパイラバージョン確認
gcc --version
# OpenSSL版libcurl開発ファイルの状態確認
apt list --installed 2>/dev/null | grep libcurl4-openssl-dev
# JSONパーサー用ライブラリの追加
sudo apt install libcjson-dev
検証用エンドポイント作成
Cコードとの疎通確認のため、Python製軽量サーバーを準備します。受信したペイロードをそのまま返却する構造にしています。
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/api/echo', methods=['POST'])
def echo_handler():
received = request.get_json(silent=True) or {}
response_payload = {
"code": 200,
"acknowledge": True,
"captured_input": received.get("user_id", "N/A")
}
return jsonify(response_payload), 200
if __name__ == "__main__":
app.run(host="127.0.0.1", port=9900)
クライアント実装(C言語)
以下のコードでは、コールバック関数内でのメモリ再確保を構造体経由で処理し、エラー経路におけるリソース解放を一元化しています。変数名および関数分割は保守性を考慮して変更しています。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <cjson/cJSON.h>
/* レスポンス蓄積用メモリ構造体 */
typedef struct {
char *data;
size_t size;
} NetBuffer;
/* libcurl通信終了毎のデータ取得フック */
size_t stream_handler(void *contents, size_t size, size_t count, void *ctx) {
NetBuffer *buf = (NetBuffer *)ctx;
size_t block_size = size * count;
char *temp_ptr = realloc(buf->data, buf->size + block_size + 1);
if (!temp_ptr) {
fprintf(stderr, "ヒープ領域枯渇\n");
return 0;
}
buf->data = temp_ptr;
memcpy(&buf->data[buf->size], contents, block_size);
buf->size += block_size;
buf->data[buf->size] = '\0';
return block_size;
}
int dispatch_post_request(const char *endpoint, const char *payload_str) {
CURL *session = NULL;
CURLcode transport_status;
long response_code = 0;
NetBuffer response_buf = {NULL, 0};
curl_global_init(CURL_GLOBAL_ALL);
session = curl_easy_init();
if (!session) {
fprintf(stderr, "CURLハンドラ生成不可\n");
return -1;
}
/* 通信パラメータ設定 */
curl_easy_setopt(session, CURLOPT_URL, endpoint);
curl_easy_setopt(session, CURLOPT_POST, 1L);
curl_easy_setopt(session, CURLOPT_POSTFIELDS, payload_str);
/* カスタムヘッダー付与 */
struct curl_slist *custom_headers = NULL;
custom_headers = curl_slist_append(custom_headers, "Content-Type: application/json");
custom_headers = curl_slist_append(custom_headers, "Accept: application/json");
curl_easy_setopt(session, CURLOPT_HTTPHEADER, custom_headers);
/* 受信バッファ紐付け */
curl_easy_setopt(session, CURLOPT_WRITEFUNCTION, stream_handler);
curl_easy_setopt(session, CURLOPT_WRITEDATA, &response_buf);
/* クエリ発行 */
transport_status = curl_easy_perform(session);
if (transport_status != CURLE_OK) {
fprintf(stderr, "転送異常: %s\n", curl_easy_strerror(transport_status));
goto release_resources;
}
/* ステータスコード抽出 */
curl_easy_getinfo(session, CURLINFO_RESPONSE_CODE, &response_code);
printf("[NETWORK] 応答ステータス: %ld\n", response_code);
printf("[RAW_DATA]\n%s\n\n", response_buf.data);
/* JSON構造体への変換 */
cJSON *tree = cJSON_ParseWithLength(response_buf.data, response_buf.size);
if (tree) {
cJSON *flag_node = cJSON_GetObjectItemCaseSensitive(tree, "acknowledge");
if (cJSON_IsBool(flag_node)) {
printf("[PARSER] ackフラグ判定: %s\n", flag_node->valueboolean ? "true" : "false");
}
cJSON_Delete(tree);
} else {
fprintf(stderr, "[ERROR] JSON構文解析失敗\n");
}
release_resources:
curl_easy_cleanup(session);
curl_slist_free_all(custom_headers);
free(response_buf.data);
curl_global_cleanup();
return (int)response_code;
}
int main(void) {
const char *target = "http://127.0.0.1:9900/api/echo";
const char *body = "{\"user_id\": \"c_impl_001\", \"action\": \"verify\"}";
int exit_code = dispatch_post_request(target, body);
return exit_code;
}
コンパイル手順
リンカーオプションに両方のライブラリを指定し、警告レベルを厳格に設定してオブジェクトを生成します。
gcc -std=c99 -O2 -Wall -Wextra -o net_client post_example.c -lcurl -lcjson
動作検証
バックグラウンドでPythonサーバーを稼働させた状態で、コンパイル済みのバイナリを実行します。
# サーバー起動(別セッション)
$ python3 http_server.py
# クライアント実行(メインセッション)
$ ./net_client
[NETWORK] 応答ステータス: 200
[RAW_DATA]
{"acknowledge": true, "captured_input": "c_impl_001", "code": 200}
[PARSER] ackフラグ判定: true