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

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

前回、 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」などの文字が書かれてます。これは一体何を意味するのでしょうか。調べてみると次の意味であることが分かりました。

記号意味
SYNSynchronize
ACKAcknowledge
FINFinal

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

記号意味
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番号を見れば確認できます。

参考

TCP/IP - TCP 3ウェイハンドシェイク 3ウェイ・ハンドシェイク - Wikipedia

関連記事

最後までご覧いただきありがとうございます!

▼ 記事に関するご質問やお仕事のご相談は以下よりお願いいたします。
お問い合わせフォーム