目次


cURL と libcurl を使ってインターネット経由でやりとりする

C と Python で libcurl を使う

Comments

アプリケーション層のプロトコル (HTTP や FTP など) を利用するアプリケーションの開発は、非常に複雑なわけではありませんが、簡単でもありません。また、そうしたプロトコルの処理は実際にはアプリケーションの中心ではありません。ほとんどの場合、そうしたプロトコルの上に構築されるものが本当に重要なものだからです。そうした点から、libcurl は非常に興味深いツールです。libcurl を使うことによって、開発のさまざまな側面に時間を使う代わりに、アプリケーションそのものの開発に集中することができます。独自の TCP/IP スタックを作成するアプリケーションは稀であることに注意してください。つまり、再利用可能なものを再利用することで、開発スケジュールを短縮することができ、またアプリケーションの信頼性を高めることができるのです。

この記事では、まずアプリケーション層のプロトコルを簡単に紹介し、次に cURL と libcurl、そしてその使い方を説明します。

Web プロトコル

アプリケーションを作成する場合、その様相は現在と最近までとでは大きく異なっています。今日のアプリケーションには、ネットワークやインターネットを介して通信できること (つまり人間が利用できるネットワーク API またはインターフェースを提供すること) が求められています。また、ユーザー・スクリプトを使用できる柔軟性を備えていることも求められています。最近のアプリケーションは通常、HTTP を使って Web インターフェースをエクスポートし、また SMTP (Simple Mail Transport Protocol) を使って警告の通知を行います。これらのプロトコルを使用することによって、Web ブラウザーで対象機器を指定して構成や状態表示を行ったり、また対象機器からの標準的な E メールを典型的な E メール・クライアントで受信したりすることができます (それぞれ HTTP と SMTP を利用しています)。

こうした Web サービスは通常、ネットワーク・スタックのソケット層の上に作成されます (図 1)。ソケット層が実装する API は BSD (Berkeley Software Distribution) オペレーティング・システムが起源であり、下位にあるトランスポート層やネットワーク通信用の階層プロトコルの詳細を抽象化しています。

図 1. ネットワーク・スタックと libcurl
ネットワーク・スタックと libcurl
ネットワーク・スタックと libcurl

Web サービスは、クライアントとサーバーとの間でのプロトコルに基づく情報交換によって行われます。プロトコルとして HTTP が使われる場合、サーバーとなるのはサービスを提供するインスタンスであり、クライアントとなるのはエンドポイントのブラウザーです。SMTP の場合は、サーバーとなるのはメール・ゲートウェイまたはエンドポイントのユーザーであり、クライアントとなるのはサービスを提供するインスタンスです。プロトコルに基づく情報交換は 2 つのステップ (リクエストとレスポンス) のみで行われる場合もありますが、それ以外の場合にはネゴシエーションと通信のために大量のトラフィックが発生します。このネゴシエーションの動作はかなり複雑ですが、そうした複雑な動作を libcurl などの API を使って抽象化することができます。

cURL の紹介

cURL は元々、さまざまなプロトコル (FTP、HTTP、SCP など) を使ってエンドポイント間でファイルを転送する手段として設計されました。cURL はコマンドライン・ユーティリティーとして始まりましたが、現在は 30 を超える言語へのバインディングを持つライブラリーでもあります。つまり今や、単純にシェルから cURL を使えるだけではなく、この重要な機能を組み込んだアプリケーションを作成することができます。libcurl ライブラリーは移植可能であり、Linux® や IBM® AIX® などのオペレーティング・システム、さらに BSD、Solaris、その他多くの UNIX® のバリエーションをサポートしています。

cURL/libcurl の入手とインストール

どの Linux ディストリビューションを実行しているかによりますが、libcurl の入手とインストールは簡単です。Ubuntu を実行している場合には、apt-get を使うと libcurl のパッケージを容易にインストールすることができます。以下の 2 行は、libcurl と、libcurl 用の Python バインディングをインストールする方法を示しています。

$ sudo apt-get install libcurl3
$ sudo apt-get install python-pycurl

apt-get ユーティリティーを使うことにより、上記プロセスですべての依存関係が確実に満たされます。

cURL をコマンドラインで使う

cURL は当初、URL (Uniform Resource Locator) の構文を使ってデータを転送するためのコマンドライン・ツールとして作られました。ところが、コマンドラインでよく使われるようになったことから、cURL の動作をアプリケーションに組み込むためのライブラリーが作成されました。現在では、コマンドラインの cURL は cURL ライブラリーに対するラッパーです。この記事では、コマンドラインでの cURL の使い方を説明し、次に cURL をライブラリーとして使用する方法を説明します。

cURL のなかで典型的な 2 つの使い方は、HTTP プロトコルを使ったファイル転送と FTP プロトコルを使ったファイル転送です。cURL には、この 2 つのプロトコルや他のプロトコルに対する簡単なインターフェースが用意されています。Web サイトから HTTP を使ってファイルを取得するためには、単純に cURL に対して、その Web ページの書き込み先となるローカルでのファイル名と、その Web サイトの URL、そして取得対象のファイルを指示します。このように言うと大げさに聞こえますが、コマンドラインはリスト 1 のように簡単です。

リスト 1. cURL を使って Web サイトからファイルを取得する例
$ curl -o test html www.exampledomain.com
  % Total    % Received % Xferd  Average Speed    Time    Time     Time    Current
                                 Dload  Upload    Total   Spent    Left    Speed
100 43320  100 43320    0     0  55831       0 --:--:-- --:--:-- --:--:--  89299
$

ここではファイルではなくドメインを指定しているため、ルート・ファイル (index.html) を取得することに注意してください。このファイルを cURL を使って FTP サイトに移動するためには、アップロード対象のファイルを -T オプションを使って指定し、次に FTP サイトの URLと、ファイルへのパスを指定します。

リスト 2. cURL を使って FTP サイトにファイルをアップロードする例
$ curl -T test.html ftp://user:password@ftp.exampledomain.com/ftpdir/
  % Total    % Received % Xferd  Average Speed    Time    Time     Time    Current
                                 Dload  Upload    Total   Spent    Left    Speed
100 43320    0     0  100 43320      0  38946   0:00:01 0:00:01  --:--:--    124k
$

とても簡単だと思いませんか?いくつかのパターンを習得すれば、cURL を使うのはとても簡単です。ただし使用可能なオプションの種類は大量にあり、コマンドラインの cURL からヘルプを実行すると (--help を使います)、129 行のオプションが表示されます。膨大な数ではありませんが、冗長性からセキュリティーに至るまで、そしてプロトコルに固有のさまざまな構成項目など、あらゆるものを制御するための大量のオプションがあります。

開発者の観点で見ると、オプションが大量にあることが cURL の最も魅力的な側面というわけではありません。ここからは、cURL ライブラリーを掘り下げていき、これらのファイル転送プロトコルをアプリケーションに追加する方法を見てみましょう。

ライブラリーとしての cURL

この 10 年間スクリプト言語を注視してきた人であれば、スクリプト言語の構造が明らかに変化してきたことに気付いているはずです。Python、Ruby、Perl、その他多くのスクリプト言語には、C や C++ に見られるようなソケット層が含まれているだけでなく、アプリケーション層のプロトコルの API も含まれています。これらのスクリプト言語には上位レベルの機能が組み込まれているため、例えば HTTP サーバーやクライアントを簡単に作成することができます。libcurl ライブラリーによって C や C++ などの言語と似た機能を追加することができますが、機能追加の方法はどの言語の場合も共通です。libcurl がサポートするすべての言語で libcurl の動作はおおよそ同じですが、それらの言語は大きく異なるため (C と Scheme を考えてみてください)、それらの言語が libcurl の動作を実現する方法も異なります。

libcurl ライブラリーはリスト 1リスト 2 に示す動作を API の形式でカプセル化しているため、この API を高級言語で使用することができます (この API を使用できる高級言語は現在 30 を超えています)。この記事では libcurl の使い方の例を 2 つ紹介します。最初の例は C による単純な HTTP クライアントであり (Web スパイダーの作成に適しています)、2 番目の例は Python による単純な HTTP クライアントです。

C ベースの HTTP クライアント

C の API には、libcurl の機能に関する API が 2 つ用意されています。easy インターフェースは同期型の単純な API です (つまり、あるリクエストで libcurl を呼び出すと、リクエストは libcurl の処理が完了するまで、あるいはエラーが発生するまで待ってから処理を続けます)。マルチ・インターフェースは libcurl を詳細に制御できるため、アプリケーションは複数の転送を同時に実行することができ、またいつどこで libcurl がデータを転送すればよいかを制御することができます。

この例では easy インターフェースを使います。この API でも (コールバックを使うことで) データの転送プロセスを多少制御することができますが、easy という名前のとおり容易に行うことができます。リスト 3 は C 言語による HTTP の例です。

リスト 3. libcurl の easy インターフェースを使った C による HTTP クライアント
#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

#define MAX_BUF	65536

char wr_buf[MAX_BUF+1];
int  wr_index;

/*
 * Write data callback function (called within the context of 
 * curl_easy_perform.
 */
size_t write_data( void *buffer, size_t size, size_t nmemb, void *userp )
{
  int segsize = size * nmemb;

  /* Check to see if this data exceeds the size of our buffer. If so, 
   * set the user-defined context value and return 0 to indicate a
   * problem to curl.
   */
  if ( wr_index + segsize > MAX_BUF ) {
    *(int *)userp = 1;
    return 0;
  }

  /* Copy the data from the curl buffer into our buffer */
  memcpy( (void *)&wr_buf[wr_index], buffer, (size_t)segsize );

  /* Update the write index */
  wr_index += segsize;

  /* Null terminate the buffer */
  wr_buf[wr_index] = 0;

  /* Return the number of bytes received, indicating to curl that all is okay */
  return segsize;
}


/*
 * Simple curl application to read the index.html file from a Web site.
 */
int main( void )
{
  CURL *curl;
  CURLcode ret;
  int  wr_error;

  wr_error = 0;
  wr_index = 0;

  /* First step, init curl */
  curl = curl_easy_init();
  if (!curl) {
    printf("couldn't init curl\n");
    return 0;
  }

  /* Tell curl the URL of the file we're going to retrieve */
  curl_easy_setopt( curl, CURLOPT_URL, "www.exampledomain.com" );

  /* Tell curl that we'll receive data to the function write_data, and
   * also provide it with a context pointer for our error return.
   */
  curl_easy_setopt( curl, CURLOPT_WRITEDATA, (void *)&wr_error );
  curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data );

  /* Allow curl to perform the action */
  ret = curl_easy_perform( curl );

  printf( "ret = %d (write_error = %d)\n", ret, wr_error );

  /* Emit the page if curl indicates that no errors occurred */
  if ( ret == 0 ) printf( "%s\n", wr_buf );

  curl_easy_cleanup( curl );

  return 0;
}

リストでは、まず先頭に、cURL のルート・ファイルなど、必要な include ファイルがあります。次に、転送のための変数をいくつか定義しています。最初の wr_buf は受信されるデータが書き込まれるバッファーを表します。wr_index はバッファーに対する現在の書き込みインデックスを表します。

下に下がると、main 関数では easy API を使ってセットアップを行っています。cURL の呼び出しはすべて、個別のリクエストに対する状態を管理するハンドルによって行われます。これは CURL ポインターの参照として定義されています。またこの例では、CURLcode という特別なリターン・コードを作成しています。どの libcurl 関数を使用するよりも前に、curl_easy_init を呼び出して CURL ハンドルを取得する必要があります。次に、いくつかの curl_easy_setopt 呼び出しがあることに注目してください。これらを呼び出すことによって、個別の操作に対するハンドルを構成します。これらを呼び出す場合には、ハンドル、コマンド、そしてオプションを指定します。この例ではまず、CURLOPT_URL を使用して取得対象の URL を指定しています。次に、CURL_WRITEDATA を使用してコンテキスト変数 (この場合には内部書き込みエラー変数) を指定しています。最後に、CURLOPT_WRITEFUNCTION を使用して、データが受信された時に呼び出す必要がある関数を指定しています。この API はこの関数を 1 回呼び出すことになるか、あるいはこの関数の実行を開始した後に読み取ったデータを引数として何度も呼び出すことになります。

転送を開始するためには curl_easy_perform を呼び出します。この関数の役割は、この関数が呼び出される前の構成に従って転送を実行することです。この関数を呼び出すと、転送が完了するかエラーが発生するまでこの関数からは戻りません。main で最後に行う処理は、リターン・ステータスを出力し、読み取ったページを出力し、そして最後に (ハンドルを使い終わったら) curl_easy_cleanup を使ってクリーンアップを行います。

今度は write_data 関数を見てみましょう。この関数は、特定の操作に対してデータが受信されると呼び出されるコールバック関数です。Web サイトからデータを読み取っている間に、write_data によってデータが書き込まれることに注意してください。このコールバック関数は引数として、(受信されたデータを含む) バッファー、メンバーの数とサイズ (この積はバッファーの中のデータの合計です)、そしてコンテキスト・ポインターを取ります。最初のタスクは、バッファー (wr_buf) にデータを書き込むための十分なスペースがあるかどうかを確認することです。十分なスペースがない場合には、コンテキスト・ポインターを設定してゼロを返し、問題があったことを知らせます。それ以外の場合には、cURL のバッファーからデータ書き込み用のバッファーにデータをコピーし、インデックスをインクリメントして次にデータを書き込む場所に設定します。またこの例では、後で printf を使って出力できるように文字列の終端の処理も行っています。最後に、操作対象となったバイト数を libcurl に返しています。バイト数を返すことによって、データが取り込まれたことを libcurl に伝え、libcurl はそのデータを破棄します。たったこれだけです。Web サイトからファイルを読み取ってメモリーに入れる方法としては比較的単純です。

Python ベースの HTTP クライアント

このセクションでは、C ベースの HTTP クライアントに似た例を Python で作成します。Python はオブジェクト指向の便利なスクリプト言語であり、本番ソフトウェアのプロトタイピングや構築に最適です。この例では、ある程度 Python を理解していることを前提にしますが、ほとんど Python の知識を使わないので、それほど深く理解している必要はありません。

Python で pycurl を使った簡単な HTTP クライアントをリスト 4 に示します。

リスト 4. Python で libcurl の pycurl インターフェースを使った簡単な HTTP クライアント
import sys
import pycurl

wr_buf = ''

def write_data( buf ):
	global wr_buf
	wr_buf += buf

def main():
	c = pycurl.Curl()
	c.setopt( pycurl.URL, 'http://www.exampledomain.com' )
	c.setopt( pycurl.WRITEFUNCTION, write_data )

	c.perform()

	c.close()

main()
sys.stdout.write(wr_buf)

この例は C のバージョンよりもはるかに単純です。まず、必要なモジュールをインポートします (標準的なシステム・モジュールの syspycurl モジュール)。次に、書き込みバッファー (wr_buf) を定義します。C プログラムの場合と同様、write_data 関数を定義しています。この関数が引数を 1 つ取ることに注意してください (HTTP サーバーから読み取られたデータ・バッファー)。ここでは単純に、このバッファーをグローバルな書き込みバッファーに連結しています。main 関数では最初に Curl ハンドルを作成し、次に setopt メソッドを使ってトランザクション用の URLWRITEFUNCTIONを定義しています。そして perform メソッドを呼び出して転送を開始し、最後にハンドルを閉じています。このリストの最後では、main 関数を呼び出し、書き込みバッファーを stdout に出力しています。この場合はエラー・コンテキストのポインターが必要ないことに注意してください。これは、Python のストリング連結を使っており、静的に大きさが決められたストリングを使わないためです。

さらに先に進むためには

libcurl が膨大な数のプロトコルと言語をサポートしていることを考えると、この記事は libcurl の表面的なことすら説明していないかも知れません。しかし、libcurl では HTTP のようなアプリケーション層のプロトコルを使用するアプリケーションの構築がいかに容易であるか、この記事から理解できたようであれば幸いです。libcurl の Web サイト (「参考文献」を参照) には、多数のサンプルと膨大な量の有益なドキュメントが用意されています。皆さんが次回、アプリケーション層のプロトコルを必要とする Web ブラウザーやスパイダー、その他のアプリケーションを作成する際には、libcurl を試してみてください。libcurl によって確実に開発時間を短縮することができ、コーディングを楽しめるはずです。


ダウンロード可能なリソース


関連トピック

  • cURL はクライアント・サイドのさまざまなプロトコルを実装したコマンドライン・ツールであり、ライブラリーです。cURL は 12 種類を超えるプロトコルをサポートしています (FTP、HTTP、Telnet、そしてそれらのセキュア版など)。また cURL はいくつかのプラットフォームで動作し (Linux、AIX、BSD、Solaris など)、そして 30 種類を超える言語をサポートしています。
  • PycURL は libcurl の API の上にある薄いレイヤーです。PycURL は薄いレイヤーであるため、非常に高速です。PycURL を使うと、libcurl ライブラリーを使う Python アプリケーションを作成することができます。
  • アプリケーションの柔軟性と言えば、アプリケーションにスクリプト機能を統合する方法を解説した記事として、「Guile によるスクリプティング」を読んでください。
  • developerWorks podcasts ではソフトウェア開発者のための興味深いインタビューや議論を聞くことができます。
  • developerWorks を Twitter でフォローしてください。
  • developerWorks の Open source ゾーンをご覧ください。オープンソース技術を使った開発や、IBM 製品でオープンソース技術を使用するためのハウ・ツー情報やツール、プロジェクトの更新情報など、豊富な情報が用意されています。
  • IBM 製品の試用版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2®、Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。

コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source
ArticleID=433895
ArticleTitle=cURL と libcurl を使ってインターネット経由でやりとりする
publish-date=09082009