強いて言えば、以下の場合には必要となるでしょう。
もっとも、このレベルになるとUNIX自体の知識も必要条件に なってくると思います。あ、ファイアーウォールは Windows NT で動くやつの方が多いかな?ということなので、そこまでは いらないという方は適当に読み飛ばしてください。
TCPとUDPの、ユーザ側から見た基本的な違いを以下に示します。
UDP | TCP | |
---|---|---|
接続形態 | 1:1 および 1:n どちらも可能 | 1:1 のみ |
アプリケーションの特定方法 | UDPポート番号 | TCPポート番号 |
送受信の単位 | パケット(*1) | ストリーム(*2) |
宛先までの到達保証 | なし | あり |
送信エラー時の動作 | パケット破棄 | 自動的に再送 |
事前のアプリケーション同士の接続動作 (コネクションの確立) | 不要(*3) | 必要(*4) |
処理の重さ | 軽い | 重い |
TCPの方がUDPより高級なプロトコルであることは明白ですね。 これらのことを実現するために、TCPでは以下のことが必要となります。
では、1つずつ説明していきましょう。 必要に応じてTCPパケット を参照してください。
まず大前提として、アプリケーションはTCPで接続をオープンする際に、 自分が能動的(active)に接続しにいくのか、受動的(passive)に相手側 からの接続を待つのかを前もって決定しておく必要があります。 前者をクライアント・アプリケーション(以下クライアント)、 後者をサーバ・アプリケーション(以下サーバ)と呼びます。
接続確立時のシーケンスは以下のようになります。 これを3ウェイ・ハンドシェークと呼んでいます。 便宜上、クライアント←サーバの転送を下り、その逆を上りと称します。 また、SYN, ACK ビットとはTCPパケット の14バイト目(下6ビット)にあるフラグの中の各ビットのことです。
サーバ | クライ アント | 説明 | |
---|---|---|---|
← | SYN | 上りコネクションの確立を試みる。 | |
ACK+SYN | → | SYNに対するACKを立てる (上りコネクションの確立を承認)とともに、 自らもSYNビットを立てることにより、下りコネクションの確立を試みる。 | |
← | ACK | 下りコネクションの確立を承認する。 |
これを見てもわかるように、上りと下りで論理的には別々の コネクションを確立するような感じになります。つまり、 TCPは全2重(同時に双方向の通信ができる)通信を行うということです。
前の節ですでに登場しましたが、TCPでは肯定応答をするために ACK ビットを使用します。コネクションの確立のところでは、 ACKはSYN(接続要求)に対する肯定応答として使用されました。 いったん接続が確立されると(3ウェイ・ハンドシェークで使われた 3パケットの次のパケット以降)、ACKは相手からの実電文に対する 肯定応答という意味になります。TCPでユニークなところは、 ACKパケットを単独で返すだけではなく、自分が送信する電文のTCPヘッダに、 「前回のそちらからの電文を正常に受信しました」という意味のACKビット を立てるということです。つまり、肯定応答と電文の送信を1パケットで 行うことができるということです。
TCPでは電文を送信するたびに、送信側はシーケンス番号 (TCPパケット・ヘッダの5バイト目) を増やしてゆき、ACK は相手側から受信した「シーケンス番号」番目の バイトまでは正常に受信したことを相手側に知らせます。 また、電文に区切りがないので(論理的には)以下のようなことができます。
送信側 | 受信側 | 説明 | ||||
---|---|---|---|---|---|---|
送信 データ | 送信 バイト数 | シーケンス 番号 | ACK ビット | ACK 番号 | ||
パケット1 | 800 | 0 | → | |||
パケット2 | 1000 | 800 | → | |||
← | ACK | 800 | パケット1受信OK | |||
パケット3 | 500 | 1800 | → | |||
← | ACK | 1600 | パケット2の途中まで (全部で1600バイト分)受信OK | |||
← | ACK | 2300 | パケット2の後半から パケット3まで受信OK |
★読者の方から、記述がやや不正確であるとのご指摘を受けましたので、 その方のご指摘をそのままの形で追加しておきます。ご指摘いただいた方、 どうもありがとうございました。
TCPにおけるACK番号とは、 「ACKを発行した側が次に相手から受け取りを期待するデータのシーケンス番号」 です。例えば、100バイトのデータをシーケンス番号0番から送信するとすると、 このセグメントで消費されるシーケンス番号は0から99番ですよね。 そうすると受信側は、次に送られてくるセグメントに期待するシーケンス番号 である100番をACK番号として返すわけです。したがってACK番号は、相手側から 受信した(シーケンス番号-1)番目のバイトまでは正常に受信したことを相手側に 知らせます。
上記のシーケンスで見たように、受信側では、ストリームのうち 何バイト目までを受信したかを意識してACK番号をセットしてACKを返し、 データが途中で抜けた場合はそれ以降のACKを返しません。これにより、 アプリケーションから見ると到着順序が保証されます (データ抜けが発生しない)。
送信側ではパケットを送出するたびにタイマーを発生させ、 一定時間にACKが返らないと、データの再送を行います。また、 (途中のルータによる再送などで)重複したパケットが到着する場合も ありますが、この場合はパケット・ヘッダのシーケンス番号がすべて 同一になるので、受信側はシーケンス番号(+データ長)を見て、 自分がすでにACKを返した分のデータ・ストリームは無視(破棄)します。
フロー制御とは、受信側が受信データの処理が追いつかなくなったとき、送信側に対してのデータ転送の中止/再開を指示することです。このしくみがないと、データ抜けなどを起こしてしまいます。
シリアル通信など使われるフロー制御としては、XON/XOFFという文字を使って行うソフトウェア・フロー制御と、モデムの信号線をオンオフすることによって行うハードウェア・フロー制御というものがあります。
TCPレベルで行われるフロー制御は、TCPヘッダ内のウィンドウというフィールドで行われます。ウィンドウは、受信側のバッファサイズの残りバイト数と思えばいいです。これが0で送られたということは、受信側のバッファにまだ処理していない(上位に渡していない)データが溜まりすぎて空きがなくなってしまったことを示します。ウィンドウが0の間は、送信側は(ACKを返すなどのために)TCPパケット自体は送信しますが、実データ部は送りません。しかし、送信側データはTCP/IPスタック上にあるバッファに溜められていますので、アプリケーションの送信動作がすぐに停止してしまうということはありません。