本稿ではC++でHTTP通信を行い, 取得したデータをユーザーに表示する方法について解説する.
docker-composeとGoのWebフレームワークであるEchoを用いてHTTPサーバーを起動した例は以前の記事で紹介したことがあると思う. 今回はGo言語のサーバーを実装し, C++側はVisual Studioを用いてlibcurlをリンクし, GETおよびPOSTリクエストを送信してレスポンスを表示するまでの流れを示す.
完成目標
今回は, "/"エンドポイントにアクセスすると"Hello, World"を返し, "/json"エンドポイントにアクセスすると{"message":"Hello from Echo server"}というJSONレスポンスを返すサーバーを実装する。
さらに, "/json"エンドポイントへPOSTリクエストを送った場合は, 受け取ったJSONデータを加工してレスポンスを返す実装例を示す.
docker-composeを用いてこのEchoサーバーをコンテナ上で起動し, クライアント側でlibcurlを用いてGETとPOSTを試してみる.
1.Echoを用いたGoサーバーの実装例
Echoフレームワークは
go get github.com/labstack/echo/v4
go mod tidy
で取得可能である。
package main
import (
"encoding/json"
"net/http"
"github.com/labstack/echo/v4"
"io"
)
type RequestData struct {
Data string `json:"data"`
}
type Response struct {
Message string `json:"message"`
}
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World")
})
// GET時は{"message":"Hello from Echo server"}を返す
// POST時は受け取った{"data":"..."}を元に{"message":"Received: ..."}と返す
e.Any("/json", func(c echo.Context) error {
if c.Request().Method == http.MethodGet {
res := Response{Message: "Hello from Echo server"}
return c.JSON(http.StatusOK, res)
} else if c.Request().Method == http.MethodPost {
body, err := io.ReadAll(c.Request().Body)
if err != nil {
return c.JSON(http.StatusBadRequest, Response{Message: "Failed to read request"})
}
var reqData RequestData
if err := json.Unmarshal(body, &reqData); err != nil {
return c.JSON(http.StatusBadRequest, Response{Message: "Invalid JSON"})
}
res := Response{Message: "Received: " + reqData.Data}
return c.JSON(http.StatusOK, res)
} else {
return c.JSON(http.StatusMethodNotAllowed, Response{Message: "Method not allowed"})
}
})
e.Logger.Fatal(e.Start(":8080"))
}
上記ではGET時とPOST時でレスポンスを分けている。GETで/jsonへアクセスすると{"message":"Hello from Echo server"}が返り, POSTで{"data":"test"}を送れば{"message":"Received: test"}というレスポンスを返す。
2.Dockerfileとdocker-compose.yml
docker-composeでGo+Echoのコンテナを起動するため, 以下のファイルを用意する。
Dockerfile
goのバージョンは適宜変更してください
FROM golang:1.23-alpine
WORKDIR /app
COPY . .
RUN go mod tidy && go build -o server server.go
EXPOSE 8080
CMD ["./server"]
docker-compose.yml
version: '3'
services:
go_server:
build: .
ports:
- "8080:8080"
サーバー側の実行イメージとして,Dockerコンテナを起動したら http://localhost:8080
および http://localhost:8080/json
へアクセス可能となり,curlなどで確認すると, /
エンドポイントではHello, World
, /json
エンドポイントではGET時{"message":"Hello from Echo server"}
, POST時に{"data":"test"}
を送れば{"message":"Received: test"}
が返る。
3.Visual StudioでのlibcurlセットアップとC++クライアントからのHTTP通信
windowsマシンにvcpkgを使ってlibcurlをインストールする.コマンドプロンプトに以下のコマンドを入れる
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg.exe install curl:x64-windows
インストールが成功すると,以下のディレクトリに curl ライブラリがインストールされる.
ライブラリファイル
C:\vcpkg\installed\x64-windows\lib\libcurl.lib
ヘッダーファイル
C:\vcpkg\installed\x64-windows\include\curl\curl.h
インストール後、Visual Studio プロジェクトに以下を設定する
「プロジェクトを右クリック > プロパティ > 構成プロパティ > C/C++ > 全般」
に移動。
「追加のインクルードディレクトリ」 に次を設定:
C:\vcpkg\installed\x64-windows\include
「構成プロパティ > リンカー > 全般」 に移動。 「追加のライブラリディレクトリ」
に次を設定:
C:\vcpkg\installed\x64-windows\lib
「構成プロパティ > リンカー > 入力」 に移動。 「追加の依存ファイル」
に次を設定:
libcurl.lib
プロジェクトにdllファイルを移動する必要もあるのでコマンドプロンプトで移動させる.
copy C:\vcpkg\installed\x64-windows\bin\libcurl.dll C:\Path\To\Your\Project\x64\Debug
他のdllファイルも見つけられないようなエラーが出たら同様にそのファイルもcopyコマンドを実行すること
4.GETリクエスト例(client.cpp)
以下はhttp://localhost:8080/json
へGETリクエストを送り, レスポンスを表示するコード例である。
docker-composeでEchoサーバーが起動している状態で実行すると, {"message":"Hello from Echo server"}
が表示される。
#include <iostream>
#include <string>
#include "curl/curl.h"
size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
size_t totalSize = size * nmemb;
std::string* str = static_cast<std::string*>(userp);
str->append(static_cast<char*>(contents), totalSize);
return totalSize;
}
int main() {
const std::string url = "http://localhost:8080/json";
CURL* curl = curl_easy_init();
if (!curl) {
std::cerr << "Failed to init curl" << std::endl;
return 1;
}
std::string responseBuffer;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBuffer);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
} else {
std::cout << "Response from server:" << std::endl;
std::cout << responseBuffer << std::endl;
}
curl_easy_cleanup(curl);
return 0;
}
5.POSTリクエスト例(client_post.cpp)
次にPOSTリクエストを送る例である。
以下はhttp://localhost:8080/json
へ{"data":"test"}
をPOSTで送り, {"message":"Received: test"}
というレスポンスを取得する。
#include <iostream>
#include <string>
#include "curl/curl.h"
size_t WriteCallback2(void* contents, size_t size, size_t nmemb, void* userp) {
size_t totalSize = size * nmemb;
std::string* str = static_cast<std::string*>(userp);
str->append(static_cast<char*>(contents), totalSize);
return totalSize;
}
int main() {
const std::string url = "http://localhost:8080/json";
const std::string postData = "{\"data\":\"test\"}";
CURL* curl = curl_easy_init();
if (!curl) {
std::cerr << "Failed to init curl" << std::endl;
return 1;
}
std::string responseBuffer;
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postData.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, (long)postData.size());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback2);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &responseBuffer);
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
CURLcode res = curl_easy_perform(curl);
if (res != CURLE_OK) {
std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror(res) << std::endl;
} else {
std::cout << "Response from server:" << std::endl;
std::cout << responseBuffer << std::endl;
}
curl_slist_free_all(headers);
curl_easy_cleanup(curl);
return 0;
}
この後にdocker-compose.yml
があるディレクトリで以下のコマンドを実行してクライアント側のファイルを実行すればOK
docker compose up
wslで上記のコマンドを実行するにはdocker desktopとwslを連携させる必要がある