<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Yoichi Kawasaki&#039;s Web &#187; tcpip</title>
	<atom:link href="http://yk55.com/blog/tags/tcpip/feed/" rel="self" type="application/rss+xml" />
	<link>http://yk55.com/blog</link>
	<description>my publicly accessible private memorandums</description>
	<lastBuildDate>Mon, 14 May 2012 01:31:02 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
	<!-- google_ad_section_end --><!-- google_ad_section_start(weight=ignore) -->	<item>
		<title>OpenSSH -SOCKSプロキシ経由でSSH接続</title>
		<link>http://yk55.com/blog/2010/10/11/openssh_ssh_over_socks_prox/</link>
		<comments>http://yk55.com/blog/2010/10/11/openssh_ssh_over_socks_prox/#comments</comments>
		<pubDate>Mon, 11 Oct 2010 02:15:14 +0000</pubDate>
		<dc:creator>yoichi</dc:creator>
				<category><![CDATA[Environment Setup]]></category>
		<category><![CDATA[dynamicforward]]></category>
		<category><![CDATA[netcat]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[socks]]></category>
		<category><![CDATA[ssh]]></category>
		<category><![CDATA[tcpip]]></category>

		<guid isPermaLink="false">http://yk55.com/blog/?p=411</guid>
		<description><![CDATA[外部からのssh接続を受け付けていないＬＡＮ内のサーバに外部からログインするために踏み台サーバを経由してＬＡＮ内サーバにログインするというよくある話です。今回試したのはSSHをSOCKSプロキシとして利用して、そのSOCKSプロキシ経由して目的のLAN内サーバに一発ログインする方法です。 SOCKS(RFC1928) とはさまざまなアプリケーションが間にファイアーウォールを挟んでいても安全に快適にやり取りができるようにすることを目的として作られたプロトコルのことで、SOCKSプロキシはSOCKSプロトコルを受け取りファイアウォール内外との接続を可能にします。 これに関してはIPAのコンテンツに図付きの解りやすい説明があります。参考までに > SOCKS ちなみにSOCKSプロキシの利点はSOCKS対応のアプリケーションであればSOCKSプロキシ経由でLAN内部のサーバにアクセスが可能なことで、例えばSOCKS対応ブラウザであればSOCKSプロキシ経由でLAN内のコンテンツが閲覧できます。自分はこのSOCKSプロキシ経由でのLAN内ドキュメントのブラウジングは多用してます。 SOCKSプロキシの作成 まずはSOCKSプロキシの立ち上げです。OpenSSHのダイナミックポートフォワード機能を使います。 ダイナミックポートフォワードはsshをSOCKSプロキシとして振舞うことを可能にします。sshでアクセス先ホストと DynamicFoward(-D)でポートを指定することでlocalhostにSOCKSプロキシが立ち上がり指定のTCPポート(SOCKSプロキシサーバは基本的は1080ですが、割り当て可能なポートであればどのポートでもOK)をlocalhost側からログイン先ホストのSSHサーバに転送することができるようになります。もちろん経路は暗号化され、現状のサポートプロトコルはSOCKS4とSOCKS5です。 例えばJumpサーバにDynamicFoward(-D)1080でログインすると、Jumpサーバにポート1080を転送するSOCKSプロキシが localhostに立ち上がり、そのlocalhost:1080に対してSOCKS4またはSOCKS5プロトコルで接続することでJumpサーバを経由して通信を行うことができます。 localhost ポート1080のJumpサーバへのダイナミックフォワードは次のように-Dオプションで行います。 ServerA$ ssh -2 -D 1080 -l &#60;Account&#62; &#60;JumpServer&#62; 毎回-Dオプション指定が面倒な場合は次のようにconfg(ssh_config)にDynamicForwardの記述しておきましょう。 $ cat ~/.ssh/config Host JumpServer &#160; &#160;User &#160; &#160; &#160; &#160;&#60;Account&#62; &#160; &#160;HostName &#160;JumpServeer &#160; &#160;Protocol 2 &#160; &#160;ForwardAgent &#160;yes &#160; &#160;DynamicForward 1080 SOCKSプロキシを使ったSSH接続 次に上で事前に作成したSOCKSプロキシを使ってLAN内のサーバにSSH接続をします。 netcatでSOCKSプロキシを経由してlocalhostから目的のＬＡＮ内サーバ（ServerB）間にnetcat tunnelを作成します。ServerBにはそのnetcat tunnelを通じて接続します。 ServerA$ ssh -2 [...]]]></description>
			<content:encoded><![CDATA[<p>外部からのssh接続を受け付けていないＬＡＮ内のサーバに外部からログインするために踏み台サーバを経由してＬＡＮ内サーバにログインするというよくある話です。今回試したのはSSHをSOCKSプロキシとして利用して、そのSOCKSプロキシ経由して目的のLAN内サーバに一発ログインする方法です。</p>
<p>SOCKS(<a href="http://www.ietf.org/rfc/rfc1928.txt">RFC1928</a>) とはさまざまなアプリケーションが間にファイアーウォールを挟んでいても安全に快適にやり取りができるようにすることを目的として作られたプロトコルのことで、SOCKSプロキシはSOCKSプロトコルを受け取りファイアウォール内外との接続を可能にします。 これに関してはIPAのコンテンツに図付きの解りやすい説明があります。参考までに > <a href="http://www.ipa.go.jp/security/awareness/administrator/remote/capter7/8.html">SOCKS</a></p>
<p>ちなみにSOCKSプロキシの利点はSOCKS対応のアプリケーションであればSOCKSプロキシ経由でLAN内部のサーバにアクセスが可能なことで、例えばSOCKS対応ブラウザであればSOCKSプロキシ経由でLAN内のコンテンツが閲覧できます。自分はこのSOCKSプロキシ経由でのLAN内ドキュメントのブラウジングは多用してます。</p>
<p><h2><strong>SOCKSプロキシの作成</strong></h2>
<p>まずはSOCKSプロキシの立ち上げです。OpenSSHのダイナミックポートフォワード機能を使います。</p>
<p>ダイナミックポートフォワードはsshをSOCKSプロキシとして振舞うことを可能にします。sshでアクセス先ホストと <a href="http://www.openbsd.org/cgi-bin/man.cgi?query=ssh_config">DynamicFoward(-D)</a>でポートを指定することでlocalhostにSOCKSプロキシが立ち上がり指定のTCPポート(SOCKSプロキシサーバは基本的は1080ですが、割り当て可能なポートであればどのポートでもOK)をlocalhost側からログイン先ホストのSSHサーバに転送することができるようになります。もちろん経路は暗号化され、現状のサポートプロトコルはSOCKS4とSOCKS5です。<br />
例えばJumpサーバにDynamicFoward(-D)1080でログインすると、Jumpサーバにポート1080を転送するSOCKSプロキシが localhostに立ち上がり、そのlocalhost:1080に対してSOCKS4またはSOCKS5プロトコルで接続することでJumpサーバを経由して通信を行うことができます。</p>
<p>localhost ポート1080のJumpサーバへのダイナミックフォワードは次のように-Dオプションで行います。</p>
<div class="codecolorer-container c mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ServerA$ ssh <span style="color: #339933;">-</span><span style="color: #0000dd;">2</span> <span style="color: #339933;">-</span>D <span style="color: #0000dd;">1080</span> <span style="color: #339933;">-</span>l <span style="color: #339933;">&lt;</span>Account<span style="color: #339933;">&gt;</span> <span style="color: #339933;">&lt;</span>JumpServer<span style="color: #339933;">&gt;</span></div></div>
<p>毎回-Dオプション指定が面倒な場合は次のようにconfg(<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=sshd_config&#038;sektion=5&#038;arch=&#038;apropos=0&#038;manpath=OpenBSD+Current">ssh_config</a>)にDynamicForwardの記述しておきましょう。</p>
<div class="codecolorer-container c mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ cat ~<span style="color: #339933;">/</span>.<span style="color: #202020;">ssh</span><span style="color: #339933;">/</span>config<br />
<br />
Host JumpServer<br />
&nbsp; &nbsp;User &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #339933;">&lt;</span>Account<span style="color: #339933;">&gt;</span><br />
&nbsp; &nbsp;HostName &nbsp;JumpServeer<br />
&nbsp; &nbsp;Protocol <span style="color: #0000dd;">2</span><br />
&nbsp; &nbsp;ForwardAgent &nbsp;yes<br />
&nbsp; &nbsp;DynamicForward <span style="color: #0000dd;">1080</span></div></div>
<p><h2><strong>SOCKSプロキシを使ったSSH接続</strong></h2>
<p>次に上で事前に作成したSOCKSプロキシを使ってLAN内のサーバにSSH接続をします。 <a href="http://www.openbsd.org/cgi-bin/man.cgi?query=nc">netcat</a>でSOCKSプロキシを経由してlocalhostから目的のＬＡＮ内サーバ（ServerB）間にnetcat tunnelを作成します。ServerBにはそのnetcat tunnelを通じて接続します。</p>
<div class="codecolorer-container c mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ServerA$ ssh <span style="color: #339933;">-</span><span style="color: #0000dd;">2</span> <span style="color: #339933;">-</span>l ＜Account＞ <span style="color: #339933;">-</span>o <span style="color: #ff0000;">'ProxyCommand nc -x localhost:1080 %h %p'</span> <span style="color: #339933;">&lt;</span>ServerB<span style="color: #339933;">&gt;</span></div></div>
<p>netcat のプロキシ指定は-xオプションで行います。 ここでは事前に作成したSOCKSプロキシ(localhost:1080)を指定。 netcat tunnelの作成コマンドはおなじみProxyCommandに記述します。こちらも毎回長いオプション入力を避けるために config（<a href="http://www.openbsd.org/cgi-bin/man.cgi?query=sshd_config&#038;sektion=5&#038;arch=&#038;apropos=0&#038;manpath=OpenBSD+Current">ssh_config</a>）設定をしましょう。</p>
<div class="codecolorer-container c mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ cat ~<span style="color: #339933;">/</span>.<span style="color: #202020;">ssh</span><span style="color: #339933;">/</span>config<br />
<br />
Host ServerB<br />
&nbsp; &nbsp;User &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #339933;">&lt;</span>Account<span style="color: #339933;">&gt;</span><br />
&nbsp; &nbsp;Protocol <span style="color: #0000dd;">2</span><br />
&nbsp; &nbsp;ForwardAgent &nbsp;yes<br />
&nbsp; &nbsp;ProxyCommand nc <span style="color: #339933;">-</span>x localhost<span style="color: #339933;">:</span><span style="color: #0000dd;">1080</span> <span style="color: #339933;">-</span>w1 <span style="color: #339933;">%</span>h <span style="color: #339933;">%</span>p</div></div>
<p><u>注意点</u><br />
netcatにはGNU本家版とそれ以外にいくつか<a href="http://ja.wikipedia.org/wiki/Netcat#.E6.B4.BE.E7.94.9F.E3.83.84.E3.83.BC.E3.83.AB">派生</a>がありますが -x オプションの利用可能なnetcatをインストールしてください。<a href="http://nc110.sourceforge.net/">オリジナルのnetcat</a>や<a href="http://netcat.sourceforge.net/">GNU netcat</a>には-xオプションはありません。ここで使用しているnetcatはIPv6に対応している<a href="http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/nc/">OpenBSD netcat</a>です。</p>
<p><h2><strong>SOCKSプロキシを経由しないでSSH接続</strong></h2>
<p>別解としてProxyCommandでJumpからBのnetcat tunnelを作成してServerＡからServerＢにログインする方法があります。詳しくは「<a href="http://dsas.blog.klab.org/archives/50765770.html">DSAS開発者の部屋:OpenSSH クライアントの proxy &#8212; 踏み台サーバを経由しての ssh</a>」を参考にしてもらうとしてここでは次のようにログインすることができます。</p>
<div class="codecolorer-container c mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ServerA$ ssh <span style="color: #339933;">-</span><span style="color: #0000dd;">2</span> <span style="color: #339933;">-</span>o <span style="color: #ff0000;">'ProxyCommand ssh &nbsp;Jump nc -w1 %h %p'</span> ServerB</div></div>
<p>ProxyCommand でプロキシ設定を行うため事前に別コンソールで何かを用意する必要がなく間違いなく楽。それと比べて事前にSOCKSプロキシを立てる必要がある SOCKSプロキシ経由のログインは面倒です。ではどうしてこんな設定を選んだのか？ 理由は単純で、当初は楽な方法で行く予定でいたものの、結局Jumpサーバがnetcatが使えない(ncはおろかsshコマンド以外ほとんど何も使えない)環境だったからです。そこでいくつか調べて行き着いたのがこのSOCKSプロキシ経由でのSSHログインだったというわけです。まさに苦肉の策。でも回り道した分少しだけOpenSSHに詳しくなりました。</p>
<p>おわり。</p>
<iframe src="http://www.facebook.com/plugins/like.php?href=http://yk55.com/blog/2010/10/11/openssh_ssh_over_socks_prox/&amp;layout=button_count&amp;show_faces=1&amp;width=450&amp;action=like&amp;colorscheme=light&amp;font=arial" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:25px"></iframe>]]></content:encoded>
			<wfw:commentRss>http://yk55.com/blog/2010/10/11/openssh_ssh_over_socks_prox/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>cURL MultiインターフェースでHTTP Pipeliningリクエストの送信</title>
		<link>http://yk55.com/blog/2010/04/20/curl_multi_http_pipelining_reques/</link>
		<comments>http://yk55.com/blog/2010/04/20/curl_multi_http_pipelining_reques/#comments</comments>
		<pubDate>Tue, 20 Apr 2010 14:54:54 +0000</pubDate>
		<dc:creator>yoichi</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[cplusplus]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[debian]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[keep-alive]]></category>
		<category><![CDATA[network]]></category>
		<category><![CDATA[pipelining]]></category>
		<category><![CDATA[tcpdump]]></category>
		<category><![CDATA[tcpip]]></category>

		<guid isPermaLink="false">http://yk55.com/blog/?p=303</guid>
		<description><![CDATA[cURLのC APIマニュアルを読んでいたらCURLMOPT_PIPELININGというおもしろそうなオプションを見つけた。これはlibcurl 7.16.0 より加わった並列実行用Multiインターフェースのオプションで、設定することでHTTP Pipeliningなリクエストが送信できるようになる。HTTP Pipeliningとは個々のレスポンスを待つことなく複数のリクエストを投げることを意味するHTTP/1.1よりサポートされた通信パフォーマンス向上のためのテクニックである。 通常N個のリクエストを処理する際はN個ソケットがオープンされOPEN → REQUEST → RESPONSE → CLOSEなサイクルがN回行われる。Multiインターフェースによる並列処理の場合はOPEN → REQUEST → RESPONSE → CLOSEが並列に行われる。 またこれがkeep-aliveな接続であればOPEN → (REQUEST → RESPONSE) x N → CLOSEのようにクローズされるまで１ソケットが再利用される。 そしてHTTP Pipeliningはkeep-aliveな接続で使うテクニックであり、これが有効な場合は1ソケットオープン後にOPEN → (REQUEST x N) → (RESPONSE x N) → CLOSEのようにリクエストN個をレスポンスを待つことなく1ソケットに書き込むことができる。 まとめて送信される分パケット効率がアップし全体的なネットワークを流れるパケットの数を減らすことができ、 さらにまとめてリクエスト送信するので高レイテンシーなネットワークにおいては速度面で効果的といえる。 see also 「Mozilla HTTP/1.1 パイプライン化 FAQ」。 (1) NOT keep-alive, single &#160; &#160; &#160; [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://curl.haxx.se/">cURL</a>の<a href="http://curl.haxx.se/libcurl/">C APIマニュアル</a>を読んでいたら<a href="http://curl.haxx.se/libcurl/c/curl_multi_setopt.html">CURLMOPT_PIPELINING</a>というおもしろそうなオプションを見つけた。これはlibcurl 7.16.0 より加わった並列実行用Multiインターフェースのオプションで、設定することで<a href="http://en.wikipedia.org/wiki/HTTP_pipelining">HTTP Pipelining</a>なリクエストが送信できるようになる。HTTP Pipeliningとは個々のレスポンスを待つことなく複数のリクエストを投げることを意味する<a href="http://www.ietf.org/rfc/rfc2616.txt">HTTP/1.1</a>よりサポートされた通信パフォーマンス向上のためのテクニックである。</p>
<p>通常N個のリクエストを処理する際はN個ソケットがオープンされOPEN → REQUEST → RESPONSE → CLOSEなサイクルがN回行われる。Multiインターフェースによる並列処理の場合はOPEN → REQUEST → RESPONSE → CLOSEが並列に行われる。 またこれがkeep-aliveな接続であればOPEN → (REQUEST → RESPONSE) x N → CLOSEのようにクローズされるまで１ソケットが再利用される。 そしてHTTP Pipeliningはkeep-aliveな接続で使うテクニックであり、これが有効な場合は1ソケットオープン後にOPEN → (REQUEST x N) → (RESPONSE x N) → CLOSEのようにリクエストN個をレスポンスを待つことなく1ソケットに書き込むことができる。 まとめて送信される分パケット効率がアップし全体的なネットワークを流れるパケットの数を減らすことができ、 さらにまとめてリクエスト送信するので高レイテンシーなネットワークにおいては速度面で効果的といえる。 see also 「<a href="https://developer.mozilla.org/ja/HTTP_Pipelining_FAQ">Mozilla HTTP/1.1 パイプライン化 FAQ</a>」。</p>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">(1) NOT keep-alive, single &nbsp; &nbsp; &nbsp; &nbsp; (OPEN → REQUEST → RESPONSE → CLOSE) x N<br />
(2) NOT keep-alive, multi &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(OPEN → REQUEST → RESPONSE → CLOSE) をN並列で行う<br />
(3) keep-alive &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; OPEN → (REQUEST → RESPONSE) x N → CLOSE<br />
(4) keep-alive, Pipelining &nbsp; &nbsp; &nbsp; &nbsp; OPEN → (REQUEST x N) → (RESPONSE x N) → CLOSE</div></div>
<p>というわけでいつものようにテストプログラムを作ってMultiインターフェースによる並列処理をHTTP Pipeliningありとなしで実行してみる。<br />
(環境 libcurl-7.19.5、httpd-2.2.2 on Debian-5.0.1 )</p>
<p><h2><strong>テストツールとそのコンパイル</strong></h2>
<p>cURL CAPIのMultiインターフェースを使って複数URLからfetchしてくるツールを作成した。</p>
<p><a href="http://github.com/yokawasa/any/blob/master/libcurl/multi_fetch.cpp">http://github.com/yokawasa/any/blob/master/libcurl/multi_fetch.cpp</a></p>
<p>標準的なcurl Multiインターフェースを使ったコードであるがポイントは次の部分。各リクエスト用のCURLハンドルでソケットの送信待ち時間を最小限にする<a href="http://curl.haxx.se/libcurl/c/curl_easy_setopt.html">CURLOPT_TCP_NODELAY</a>オプション（詳しくは「<a href="http://www.ibm.com/developerworks/jp/linux/library/l-hisock/index.html">Linuxにおけるソケット機能の向上</a>」を参照ください）と挙動把握のためにlibcurlのINFO情報を出力する<a href="http://curl.haxx.se/libcurl/c/curl_easy_setopt.html">CURLOPT_VERBOSE</a>オプションを有効にしている。またHTTP Pipeliningモード指定のときにMulti用ハンドルで<a href="http://curl.haxx.se/libcurl/c/curl_multi_setopt.html">CURLMOPT_PIPELINING</a>オプションを有効にしている。　以下該当箇所のコード断片。</p>
<div class="codecolorer-container c mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="c codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
<span style="color: #b1b100;">for</span><span style="color: #009900;">&#40;</span>vector<span style="color: #339933;">&lt;</span>string<span style="color: #339933;">&gt;::</span><span style="color: #202020;">iterator</span> it<span style="color: #339933;">=</span>urls.<span style="color: #202020;">begin</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; it<span style="color: #339933;">!=</span>urls.<span style="color: #202020;">end</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span> <span style="color: #339933;">++</span>it<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span><br />
&nbsp; &nbsp; curl_easy_setopt<span style="color: #009900;">&#40;</span>c_handles<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> CURLOPT_URL<span style="color: #339933;">,</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>it<span style="color: #009900;">&#41;</span>.<span style="color: #202020;">c_str</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; curl_easy_setopt<span style="color: #009900;">&#40;</span>c_handles<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> CURLOPT_TCP_NODELAY<span style="color: #339933;">,</span> <span style="color: #0000dd;">1L</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; curl_easy_setopt<span style="color: #009900;">&#40;</span>c_handles<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> CURLOPT_VERBOSE<span style="color: #339933;">,</span> <span style="color: #0000dd;">1L</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; curl_multi_add_handle<span style="color: #009900;">&#40;</span>m_handle<span style="color: #339933;">,</span> c_handles<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
&nbsp; &nbsp; i<span style="color: #339933;">++;</span><br />
<span style="color: #009900;">&#125;</span><br />
<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>pipelining<span style="color: #009900;">&#41;</span><br />
&nbsp; &nbsp; curl_multi_setopt<span style="color: #009900;">&#40;</span>m_handle<span style="color: #339933;">,</span> CURLMOPT_PIPELINING<span style="color: #339933;">,</span> <span style="color: #0000dd;">1L</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span><br />
...</div></div>
<p>上記URLのソースコードを取得して次のようにコンパイルを行う。</p>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">g++ multi_fetch.cpp -o multi_fetch -I/usr/include -L/usr/lib -lcurl</div></div>
<p>使い方は次のように複数URLの書かれたファイルを指定する。-pオプションを加えてやることでHTTP Pipeliningモードでリクエスト送信を行う。 これはオプショナル。</p>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Usage: ./multi_fetch &lt;options&gt;<br />
Options: -f file &nbsp;URL list file<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;-p &nbsp; &nbsp; &nbsp; HTTP pipelining mode (optional)<br />
&nbsp;<br />
例) urls.txtに書かれた複数URLからHTTP Pipeliningモードでfetch<br />
$ ./multi_fetch -f urls.txt -p</div></div>
<p><h2><strong>NON HTTP Pipelining リクエスト送信</strong></h2>
<p>テスト用にホストfooとWebサーバ（apache）のあるホストbarを用意する。まずはHTTP Pipeliningオプションをはずした通常のmultiインターフェースによる並列実行を行う。リクエスト用CURLハンドルでCURLOPT_VERBOSEオプションが有効になっているので実行結果にlibcurlのINFO 情報も一緒に出力される。出力内容を全てここに貼り付けるには量が多すぎるので内容を簡略化してHTTP接続部分と各リクエストとそのレスポンスの最初の一行だけに絞る。</p>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ cat urls.txt<br />
http://bar.yk55.com/test1.html<br />
http://bar.yk55.com/test2.html<br />
http://bar.yk55.com/test3.html<br />
http://bar.yk55.com/test4.html<br />
<br />
$ ./multi_fetch -f urls.txt &nbsp;2&gt;&amp;1 |tee /tmp/nonpipelined.out</div></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[出力結果]<br />
$ cat /tmp/nonpipelined.out<br />
<br />
* About to connect() to bar.yk55.com port 80 (#0) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* About to connect() to bar.yk55.com port 80 (#1)<br />
* About to connect() to bar.yk55.com port 80 (#2)<br />
* About to connect() to bar.yk55.com port 80 (#3)<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#0)<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#1)<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#2)<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#3)<br />
&gt; GET /test1.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&gt; GET /test2.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&gt; GET /test3.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&gt; GET /test4.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* Connection #1 to host bar.yk55.com left intact &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* Connection #2 to host bar.yk55.com left intact &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* Connection #3 to host bar.yk55.com left intact &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <br />
* Connection #0 to host bar.yk55.com left intact</div></div>
<p>これを見ると並列にConnection #[0-3]の4つのソケットがオープンしてそれぞれにリクエスト、レスポンスが書き出され終了していることが分かる。 「(2)NOT keep-alive, multi」のパターンになっているといえる。想定どおり。</p>
<p><h2><strong>HTTP Pipeliningリクエスト送信</strong></h2>
<p>次に前テストと同様にホストfooからホストbar(Webサーバ)の4ファイル対してにHTTP Pipeliningなリクエストを送信してみる。</p>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ cat urls.txt<br />
http://bar.yk55.com/test1.html<br />
http://bar.yk55.com/test2.html<br />
http://bar.yk55.com/test3.html<br />
http://bar.yk55.com/test4.html<br />
&nbsp;<br />
$ ./multi_fetch -f urls.txt -p　 2&gt;&amp;1 |tee /tmp/pipelined.out</div></div>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">[出力結果]<br />
$ cat /tmp/pipelined.out<br />
<br />
* About to connect() to bar.yk55.com port 80 (#0)<br />
* Re-using existing connection! (#0) with host bar.yk55.com<br />
* Re-using existing connection! (#0) with host bar.yk55.com<br />
* Re-using existing connection! (#0) with host bar.yk55.com<br />
* Connected to bar.yk55.com (192.168.1.5) port 80 (#0) <br />
&gt; GET /test1.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&gt; GET /test2.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&gt; GET /test3.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&gt; GET /test4.html HTTP/1.1 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
&lt; HTTP/1.1 200 OK &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
* Connection #0 to host bar.yk55.com left intact</div></div>
<p>出力結果から、1つのソケットオープン(Connection #0)後に他のリクエスト達はConnectoin#0を 再利用しているのが分かる。 またtest1.htmlをGETするためのリクエスト送信後に「HTTP/1.1 200 OK&#8230;.」とレスポンスを受け、次にtest2.html～test4.html GETの3リクエストがレスポンスを待つことなく連続で送られ、その後それらのレスポンスを受けている。 「(4) keep-alive, Pipelining」のパターンになると予想していたのだが１つ目のリクエスト（REQUEST-a）だけがPipeliningではない。 試しにリクエスト数を増やしてみても同じ、１つ目のリクエストがPipeliningではなく、2番目以降のリクエストはPipeliningになっている。 今一理由がわからない。 ひょっとして実際の処理とは別にログ出力に問題があるのではないかと思い<a href="http://www.linux.or.jp/JM/html/tcpdump/man1/tcpdump.1.html">tcpdump</a>で実際のTCPパケットのやりとり確認してみる。</p>
<div class="codecolorer-container text mac-classic" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">$ sudo tcpdump -lX -s 1024 -i eth0 port 80 |tee /tmp/tcpdump.txt<br />
$ cat /tmp/tcpdump.txt<br />
<br />
*** SYN: Socketオープン<br />
01:27:14.155974 IP foo.43242 &gt; bar.http: S 3856091341:3856091341(0) win 5840 &lt;mss 1460,sackOK,timestamp 31004352 0,nop,wscale 6&gt;<br />
01:27:14.158135 IP bar.http &gt; foo.43242: S 2366776094:2366776094(0) ack 3856091342 win 5792 &lt;mss 1460,sackOK,timestamp 224898337 31004352,nop,wscale 2&gt;<br />
01:27:14.158197 IP foo.43242 &gt; bar.http: . ack 1 win 92 &lt;nop,nop,timestamp 31004352 224898337&gt;<br />
<br />
*** PUSH: 1stリクエスト<br />
01:27:14.156012 IP foo.43242 &gt; bar.http: P 1:63(62) ack 1 win 92 &lt;nop,nop,timestamp 31004352 224898337&gt;<br />
01:27:14.156080 IP bar.http &gt; foo.43242: . ack 63 win 1448 &lt;nop,nop,timestamp 224898337 31004352&gt;<br />
<br />
*** PUSH: 1stレスポンス<br />
01:27:14.158768 IP bar.http &gt; foo.43242: P 1:198(197) ack 63 win 1448 &lt;nop,nop,timestamp 224898340 31004352&gt;<br />
01:27:14.158930 IP foo.43242 &gt; bar.http: . ack 198 win 108 &lt;nop,nop,timestamp 31004353 224898340&gt;<br />
<br />
*** PUSH: 2nd, 3rd, 4thリクエスト<br />
01:27:14.159183 IP foo.43242 &gt; bar.http: P 63:125(62) ack 198 win 108 &lt;nop,nop,timestamp 31004353 224898340&gt;<br />
01:27:14.159277 IP foo.43242 &gt; bar.http: P 125:187(62) ack 198 win 108 &lt;nop,nop,timestamp 31004353 224898340&gt;<br />
01:27:14.159361 IP foo.43242 &gt; bar.http: P 187:249(62) ack 198 win 108 &lt;nop,nop,timestamp 31004353 224898340&gt;<br />
<br />
*** PUSH: &nbsp;2nd, 3rd, 4thまとめてレスポンス<br />
01:27:14.163514 IP bar.http &gt; foo.43242: P 198:789(591) ack 249 win 1448 &lt;nop,nop,timestamp 224898344 31004353&gt;<br />
<br />
*** FIN: &nbsp;Socket クローズ<br />
01:27:14.164128 IP foo.43242 &gt; bar.http: F 249:249(0) ack 789 win 127 &lt;nop,nop,timestamp 31004354 224898344&gt;<br />
01:27:14.164634 IP bar.http &gt; foo.43242: F 789:789(0) ack 250 win 1448 &lt;nop,nop,timestamp 224898345 31004354&gt;<br />
01:27:14.164780 IP foo.43242 &gt; bar.http: . ack 790 win 127 &lt;nop,nop,timestamp 31004354 224898345&gt;</div></div>
<p>tcpdumpの結果も変わらず同じ。 ログを見る限りオープン処理（SYNフラグの出力部分、いわゆる3way handshake）が行われた後、１つ目のリクエスト直後にレスポンスを受け、その後2番目以降のリクエストはレスポンスを待つことなくソケット書き込み、つまりPipeliningリクエスト送受信が行われている。　どうして１つ目だけがダメで、２つ目以降からうまくいくのだろう？？ (T . T)</p>
<p>細かくlibcurlのコードを追えばよいのだろうがこれ以上調査する気持ちにならないので取り敢えずここで終えておく。 続きはいつか。</p>
<p>おわり。</p>
<iframe src="http://www.facebook.com/plugins/like.php?href=http://yk55.com/blog/2010/04/20/curl_multi_http_pipelining_reques/&amp;layout=button_count&amp;show_faces=1&amp;width=450&amp;action=like&amp;colorscheme=light&amp;font=arial" scrolling="no" frameborder="0" style="border:none; overflow:hidden; width:450px; height:25px"></iframe>]]></content:encoded>
			<wfw:commentRss>http://yk55.com/blog/2010/04/20/curl_multi_http_pipelining_reques/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	<!-- google_ad_section_end --></channel>
</rss>

