iPhoneからC言語を使ってTCP通信してみよう
前回、 iPhoneからC言語を使ってTCP通信してみよう でUDP通信を実装してみたところ、意外にも簡単にプログラミングできることに驚き感動しました。この勢いでTCP通信も試してみたいと思いましたが、いざ調べてみるとUDPよりもだいぶ複雑そうです。まずは、プログラミングの前にそもそもTCPがどういった通信なのか調べてみることにしました。
TCP通信の流れ
TCPでググってみると、この形の図がたくさん出てきます。
まずは大きく分けて考えてみます。すると、次の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番号を見れば確認できます。
参考
TCP/IP - TCP 3ウェイハンドシェイク 3ウェイ・ハンドシェイク - Wikipedia