iPhoneからC言語を使ってTCP通信してみよう

前回、UDP通信を実装してみたところ、意外にも簡単にプログラミングできることに驚き感動しました。この勢いでTCP通信も試してみたいと思いましたが、いざ調べてみるとUDPよりもだいぶ複雑そうです。まずは、プログラミングの前にそもそもTCPがどういった通信なのか調べてみることにしました。

TCP通信の流れ

TCPでググってみると、この形の図がたくさん出てきます。


画像の拡大

まずは大きく分けて考えてみます。すると、次の3つの流れ(シーケンス)に分けられるようです。

  1. コネクションの確立
  2. データ送信
  3. コネクションの解放

実際のデータを送るのは2番の仕事ですが、その前後で1と3のコネクション処理が出てきます。

これは人間のコミュニケーションに似ているところがあると思いました。つまり、「挨拶から始まり、さよならの挨拶で終わる」のです。

そしてこの約束事をプロトコルと呼びます。プロトコルを決めないと、人間も機械も会話が成り立たないですよね。

さて、UDPは「User Datagram Protocol」の略でした。Datagramは、届くかどうかは保証されない意味でした。それに対して、TCPは「Transmission Control Protocol」の略です。Transmissionは送信の意味です。つまり、送信状態を管理(コントロール)できるプロトコルであることがわかります。

ところで図をもう一度みてみます。矢印の流れの中に「SYN」や「ACK」などの文字が書かれています。これは一体何を意味するのでしょうか。調べてみると次の意味であることが分かりました。

記号 意味
SYN Synchronize
ACK Acknowledge
FIN Final

それでも、いまいちピンときません。そこで、次ように読み替えるとわかりやすくなりました。

記号 意味
SYN 送ってもいいかな?
ACK おっけー
FIN 終わるねー

実際のTCPパケットの構造は次の図のようになります。UDPパケットよりは複雑になっていることが分かります。それでも、ポイントを抑えればきっと私でも理解できるはずと思い、ここで挫けずに進めていきたいと思います。


画像の拡大

パケットをよく見てみると、さきほどの、ACKやSYNフラグが存在します。つまりACKやSYNパケットであるかどうかの判断は、この部分を変えれば良さそうです。もっと言うと、フラグとは1ビット単位の0か1かの信号状態の意味です。

送信元ポートや送信先ポートはUDPでも出てきましたので大丈夫でしょう。気になるのはシーケンス番号とシーケンスACK番号(確認応答番号)の部分です。どうもこの部分がTCP通信で鍵を握っているようです。

さて、大雑把ですがTCP通信の雰囲気がつかめてきました。次では実際の通信のやり取りを詳しく見ていきます。そこで最初の「コネクションの確立」つまり「3ウェイハンドシェイク」のやり取りを詳しく見ていくことにします。

3ウェイハンドシェイク


画像の拡大

TCP通信を行うにあたって、まず、最初に3ウェイハンドシェイクのコネクション確立を行う必要があります。TCPパケットで意識する部分はたったの4つで良いので、その他はいったん忘れてしまいましょう。


画像の拡大

また、次のルールがあることを先に覚えておくと理解しやすいと思います。

シーケンスACK番号のルール

  • 「相手から受信したシーケンス番号」+「データサイズの値」
  • ただし、3ウェイハンドシェイクは「相手から受信したシーケンス番号」に「1」を加算した値となる

とくに今回は3ウェイハンドシェイクの話ですので、「相手から受信したシーケンス番号」に「1」を加算した値となるの部分を意識すれば良いことになります。

それでは3ウェイハンドシェイクのやり取りを見ていきます。

最初に、クライアントがサーバーに対してSYNパケットを送ります。最初に述べた通り、SYNパケットといってもSYNフラグを1、ACKフラグを0にしただけです。シーケンス番号ですが、最初の送信ではランダムな値をとるようです。なので、ここでは仮に1000としました。


画像の拡大

次に、 SYNパケットを受け取ったサーバーは、クライアントへSYN ACKパケットを返します。この時のシーケンスACK番号は、さきほどのルールに従います。つまり、受信したシーケンス番号の1000に1を加えた1001の値になるのです。

シーケンス番号はですが、サーバー側でも最初の送信はランダム値をとるようです。そこで、ここでは仮に9000としました。


画像の拡大

最後に、クライアント側を見ていきます。サーバーからSYN、ACKパケットを受けとったクライアントは、接続開始のACKパケットを送り返すことになります。シーケンス番号は相手から受け取ったシーケンスACK番号になる決まりがあります。つまりここでは、1001の値となることが分かります。また、シーケンスACK番号は先ほどと同様に、送られてきたシーケンス番号9000に1を足して9001となります。


画像の拡大

以上のように、このまま続いていけばシーケンス番号とACK番号が加算されながら入れ替わっていくことが分かります。相手の状態をパケットに埋め込むことで、通信の状態をお互いに把握できることが分かりました。つまり、どこまで送信受信できているかは、このシーケンス番号とシーケンスACK番号を見れば確認できます。

参考

Amazonでお得に購入するなら、Amazonギフト券がオススメ!

\Amazonギフトがお得/

コンビニ・ATM・ネットバンキングで¥5,000以上チャージすると、プライム会員は最大2.5%ポイント、通常会員は最大2%ポイントがもらえます!
Amazonギフト券

\この記事をシェアする/