0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

C言語のlibcurlをDockerで試すまで

Posted at

きっかけ

Linux環境でC言語のlibcurlの動作確認をしたかった。
調べたらWindows10 HomeでもDocker使えるようなので試してみる。

やったこと

  • Dockerインストール
  • Docker Hubへの登録
  • ContOSコンテナを作成
  • コンテナ内でC言語コンパイル&実行できるようにする
  • コンテナにlibcurlをインストールする
  • POSTの動作確認

Dockerの準備

Dockerのインストール

https://docs.docker.com/desktop/windows/install/
からDocker Desktop Installer.exeをDLしてインストール。

WSL2のインストール

ポップアップが出てWSL2をアップデートしてと言われるので
画面に従ってURLからインストールしてPC再起動。
https://aka.ms/wsl2kernel
→wsl_update_x64.msi

WSL2とは?

Windows Subsystem for Linux 2。
Windows 10上でLinuxを動作させるための仕組みのことらしい。それのバージョン2。

WSL2インストール後、Dockerのチュートリアルが始まるのでやってみる。

Docker Hubのアカウント作成

DockerイメージをプルしたりするにはDocker Hubへの登録が必要なのでしておく。

Docker Hub
https://hub.docker.com/

Dockerイメージの取得~コンテナ起動

今回はCentOS7の公式イメージを使うことにする。
Docker Hubで「Official Images」にチェックして「centos」で検索すると
公式のリポジトリが見つかる。
リポジトリを見ると以下の形式でCentOS7が取れそうなことがわかる。

Dockerイメージの取得
$ docker pull centos:7

ターミナルから試して無事取得できた。

Dockerイメージ一覧の取得
$ docker images
REPOSITORY               TAG       IMAGE ID       CREATED        SIZE
~省略~
centos                   7         eeb6ee3f44bd   4 months ago   204MB

取得したイメージからコンテナ作成&起動

コンテナを作成して起動する
$ docker run -it -d --name centos7c centos:7

-it:ログインして入出力可能にする
-d:コンテナから抜けてもバックグラウンドで起動したままにする
--name:コンテナに名前をつける

起動できた。

起動しているDockerコンテナを表示
$ docker ps
CONTAINER ID   IMAGE            COMMAND       CREATED          STATUS             PORTS     NAMES
df8c294e015f   centos:7         "/bin/bash"   13 seconds ago   Up 12 seconds                centos7c   

入ってみる。

Dockerコンテナに入る
$ docker exec -it centos7c /bin/bash
[root@df8c294e015f /]# ←コンテナに入った 

似たようなコマンドにdocker attach ~があるが、
これはexitで終了時にコンテナも終了してしまうよう。

その他のDockerコマンド

コンテナの一時停止
$ docker stop centos7c
一時停止中のコンテナ含めてプロセス確認
$ docker ps -a
コンテナ再起動
$ docker start centos7c

使用しなくなったコンテナはdocker rm centos7cで削除できる。
削除するとdocker ps -aでも表示されなくなる。

削除したらもちろん今までコンテナ内で行ったことはすべてなくなるので注意。

コンテナ内にC言語環境

gccのインストール

gccが入っているか確認

[root@df8c294e015f /]# rpm -qa gcc
[root@df8c294e015f /]# 

入ってなかったのでインストール

[root@df8c294e015f /]# yum -y install gcc
~省略~
Dependency Updated:
  glibc.x86_64 0:2.17-325.el7_9                  glibc-common.x86_64 0:2.17-325.el7_9

Complete!
[root@df8c294e015f /]# 

インストールされた。

[root@df8c294e015f /]# rpm -qa gcc
gcc-4.8.5-44.el7.x86_64

作業用ユーザ作成

[root@df8c294e015f /]# adduser user1
[root@df8c294e015f /]# su - user1
[user1@df8c294e015f ~]$ pwd
/home/user1

GCCでコンパイル

コンパイルできるか試してみる。

[user1@df8c294e015f ~]$ mkdir test01
[user1@df8c294e015f ~]$ cd test01/
[user1@df8c294e015f test01]$ cat test.c
# include<stdio.h>
int main(){
        printf("hello\n");
}
[user1@df8c294e015f test01]$ gcc test.c 
[user1@df8c294e015f test01]$ ./a.out
hello

無事コンパイルと実行ができた。

curl公式
https://curl.se/

libcurl込みでコンパイルしてみる

とりあえずコンパイルしてみるとcurl.hが読み込めない
[user1@df8c294e015f test01]$ cat post.c 
# include<stdio.h>
# include <curl/curl.h>
~省略~
[user1@df8c294e015f test01]$ gcc test.c -lcurl
fatal error: curl/curl.h: No such file or directory

-lオプションでリンクしたいライブラリ名を指定している。

libcurlを入れてみる

いろいろ調べてみるとC言語から#include <curl/curl.h>するには
libcurl-develのインストールが必要のよう。
https://stackoverflow.com/questions/69633205/libcurl-c-how-to-correctly-install-and-use-on-centos-7

ということでlibcurl-develを入れる

libcurl-develのインストール
# yum install libcurl-devel
~省略~
Downloading packages:
libcurl-devel-7.29.0-59.el7_9.1.x86_64.rpm                                      | 303 kB  00:00:00     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : libcurl-devel-7.29.0-59.el7_9.1.x86_64                                              1/1 
  Verifying  : libcurl-devel-7.29.0-59.el7_9.1.x86_64                                              1/1 

Installed:
  libcurl-devel.x86_64 0:7.29.0-59.el7_9.1

Complete!

インストール完了したら再度コンパイル。

[root@df8c294e015f test01]# gcc test.c -lcurl
[root@df8c294e015f test01]# ./a.out

コンパイルが通った!

モックサーバの用意

POSTのテストをしたいので何かよさそうなものはないか探してみると以下2つが簡単そうなのでそれぞれ試してみる。

  • json-server
  • httpbin.org

json-server

npmでインストール。npm自体のインストールについては割愛。

インストール
$ npm install -g json-server

リクエストした際に返すレスポンスをjsonで定義しておく。

db.json
{
  "user": {
    "name": "user1",
    "age": 30
  }
}

作成したjsonを指定して起動。

起動
$ json-server --watch db.json

ブラウザから以下にアクセスすると
http://localhost:3000/user
db.jsonで定義したuserオブジェクトの中身を返してくれる。
すごく簡単。

ただPOSTを試してみると、用意したdb.jsonを本当にPOSTしたデータで書き換えてしまう。
POSTで書き換えないようにするにはもうひと手間いるよう。(Javascriptでの実装が少し必要)

httpbin.org

こちらは簡単なHTTPリクエストを試せるWebサービス。
http://httpbin.org
Dockerイメージも用意されているのでPullしてローカルで動かせば外部に通信する必要もない。

いったんDockerイメージではなくWebサービスをそのまま使ってみる。

GETの場合
$ curl -X GET "http://httpbin.org/get?name=user1&age=30"
{
  "args": {
    "age": "30",
    "name": "user1"
  },
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.79.1",
    "X-Amzn-Trace-Id": "Root=1-61ed8bac-11b4622d4e26172d4ab0b672"
  },
  "origin": "133.200.49.161",
  "url": "http://httpbin.org/get?name=user1&age=30"
}

GETの場合は"args"にリクエストパラメータを設定して返してくれる。

POSTの場合
$ curl -X POST "http://httpbin.org/post" -d "name=user1&age=30"
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "age": "30",
    "name": "user1"
  },
  "headers": {
    "Accept": "*/*",
    "Content-Length": "17",
    "Content-Type": "application/x-www-form-urlencoded",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.79.1",
    "X-Amzn-Trace-Id": "Root=1-61ed8be5-4814397c319394335e8739a0"
  },
  "json": null,
  "origin": "133.200.49.161",
  "url": "http://httpbin.org/post"
}

POSTの場合は"form"にリクエストパラメータを設定して返してくれる。(おそらくformタグでSubmitしたイメージ)
-H "Content-Type: application/json"を指定すれば"data"にPOSTデータをそのまま設定して返してくれる。

curl -X POST "http://httpbin.org/post" -H "Content-Type: application/json" -d '{"name":"user1","age":30}'
{
  "args": {},
  "data": "'{name:user1,age:30}'",
  "files": {},
  "form": {},
  "headers": {
    "Accept": "*/*",
    "Content-Length": "21",
    "Content-Type": "application/json",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.79.1",
    "X-Amzn-Trace-Id": "Root=1-61ed8dbd-672e05e12b0709182676ec4d"
  },
  "json": null,
  "origin": "133.200.49.161",
  "url": "http://httpbin.org/post"
}

↑ちなみに"data"はWindowsコマンドプロンプトだと
"data": "'{name:user1,age:30}'",
DockerコンテナのLinuxだと
"data": "{\"name\":\"user1\",\"age\":30}",
だった。

POST処理の実装

公式ドキュメントを参考に実装。
https://curl.se/libcurl/c/getinmemory.html

post.c
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <curl/curl.h>

# define MAX_BUF 65535

typedef struct
{
	char *memory;
	int size;
} Chunk;

// Callback function
static size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp)
{
	size_t realsize = size * nmemb;
	Chunk *chunk = (Chunk *)userp;

	char *ptr = realloc(chunk->memory, chunk->size + realsize + 1);
	if (!ptr)
	{
		/* out of memory! */
		printf("not enough memory (realloc returned NULL)\n");
		return 0;
	}

	chunk->memory = ptr;
	memcpy(&(chunk->memory[chunk->size]), buffer, realsize);
	chunk->size += realsize;
	chunk->memory[chunk->size] = 0;

	return realsize;
}

int main()
{
	printf("### POST START ###\n");
	CURL *curl;
	CURLcode ret;
	int wr_error = 0;
	curl = curl_easy_init();
	char *post_data = "name=user2&age=31";
	Chunk chunk;
	memset(&chunk, 0, sizeof(Chunk));

	if (curl == NULL)
	{
		fprintf(stderr, "curl_easy_init failed.\n");
		return 1;
	}
	curl_easy_setopt(curl, CURLOPT_URL, "http://httpbin.org/post");
	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);

	// post option
	curl_easy_setopt(curl, CURLOPT_POST, 1);
	curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
	curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(post_data));

	ret = curl_easy_perform(curl);
	printf("ret : %d\n", ret);

	if (ret == 0)
	{
		printf("size : %d\n", chunk.size);
		printf("response :\n%s\n", chunk.memory);
	}
	curl_easy_cleanup(curl);

	printf("### POST END ###\n");
	return 0;
}
実行
[user1@df8c294e015f test04]$ ./a.out 
### POST START ###
ret : 0
size : 413
response :
{
  "args": {},
  "data": "",
  "files": {},
  "form": {
    "age": "31",
    "name": "user2"
  },
~省略~
### POST END ###

その他

Dockerで実装したファイルをホストに渡す

コマンド実行したカレントディレクトリにコピー
docker cp centos7c:/home/user1/test01/test.c .

docker cp <コンテナ名>:<コンテナ内ファイル絶対パス> <コピー先ホストディレクトリ>
の形式

すでにtest.cがコピー先ホストディレクトリにある場合は上書きされるので注意。

ホストからコンテナにファイルを渡す

カレントディレクトリのファイルをコピー
docker cp test.c centos7c:/home/user1/test02

docker cp <コピーするファイル> <コンテナ名>:<コンテナ内ファイル絶対パス>
の形式

あとがき

実は最初VMWareで試そうとしたけど、
CentOS7のISOイメージDLしてVMWareで仮想マシン作成して起動したら
以下のエラーが発生してよくわからなかったのでDocker使ってみました。

Linux install error "dracut Warning: Can't mount root filesystem."

あとでlibcurlインストールまでの作業をDockerfileにまとめてDockerイメージ化したい。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?