大きな工場の面接で毎回聞かれるTCPハンドシェイク、面接官の答えには唖然とするが、一度読めば理解できる。

TCP スリーウェイ ハンドシェイクで、クライアントが 2 回目のハンドシェイクで受信した ACK 確認番号が予期したものと異なります。どうなりますか? RST メッセージを直接破棄するか、返す必要がありますか?
どのような状況で不正な ack (2 回目のハンドシェイクでの ack) が受信されるのでしょうか?
質問への回答
大騒ぎせずに、質問に直接答えて、RST メッセージを返してください。プロセスは次のとおりです。

写真

歴史的なつながりを避けるための 3 ウェイ ハンドシェイク

クライアントが接続を確立するために複数の SYN メッセージを連続的に送信し、ネットワークが混雑すると、クライアントは誤った ACK を受信する可能性があります。具体的なプロセスは次のとおりです。

クライアントは最初に SYN (seq = 90) メッセージを送信しましたが、ネットワークによってブロックされ、サーバーはそれを受信しませんでした。その後、クライアントは SYN (seq = 100) メッセージを再送信しました。これは再送信ではないことに注意してください。 SYN ですが、再送信です。 送信された SYN のシリアル番号は同じです。
「古い SYN メッセージ」は「最新の SYN」メッセージよりも早くサーバーに到着したため、サーバーはこの時点で SYN + ACK メッセージをクライアントに返します。このメッセージの確認番号は 91 (90+1) です。 。
クライアントがそれを受信した後、受信すると予想される確認番号は 90+1 ではなく 100+1 である必要があるため、RST メッセージが返されます。
サーバーは RST メッセージを受信すると、接続を終了します。
最新の SYN がサーバーに到着すると、通常、クライアントとサーバーは 3 ウェイ ハンドシェイクを完了できます。
上記の「古い SYN メッセージ」は履歴接続と呼ばれます。TCP が接続の確立に 3 ウェイ ハンドシェイクを使用する主な理由は、「履歴接続」による接続の初期化を防ぐためです。

RFC 793 から、TCP 接続が 3 ウェイ ハンドシェイクを使用する主な理由もわかります。

スリーウェイ ハンドシェイクの主な理由は、古い重複した接続開始によって混乱が生じるのを防ぐことです。

簡単に言うと、スリーウェイ ハンドシェイクの最大の理由は、古い接続初期化の繰り返しによる混乱を防ぐことです。RFC によって指定された、過去の接続を防止する 3 ウェイ ハンドシェイクのケース図は次のとおりです。

写真

RFC 793

双方向ハンドシェイク接続の場合、過去の接続はブロックできないのに、なぜ TCP 双方向ハンドシェイクで過去の接続を阻止できないのでしょうか?

最初に結論を直接述べておきます。主な理由は、2 つのハンドシェイクの場合、「パッシブ イニシエーター」には履歴接続を防止する「アクティブ イニシエーター」の中間状態が存在しないため、「パッシブ イニシエーター」が履歴接続を確立する可能性があるためです。接続するとリソースが無駄になります。

考えてみてください。2 回のハンドシェイクの場合、「パッシブ イニシエータ」は SYN メッセージを受信した後に ESTABLISHED 状態に入ります。これは、この時点で相手にデータを送信できることを意味しますが、「アクティブ イニシエータ」はまだ ESTABLISHED 状態にあります。 ESTABLISHED 状態にならない場合、これが履歴接続であると仮定して、アクティブ イニシエータは、この接続が履歴接続であると判断し、切断するための RST メッセージを返し、「パッシブ」イニシエータは ESTABLISHED 状態になるため、データを送信できますが、これが履歴接続であることは認識されず、RST メッセージを受信した後にのみ切断されます。

写真

双方向ハンドシェイクが過去の接続をブロックできない

上記のシナリオでは、「パッシブ イニシエータ」が「アクティブ イニシエータ」にデータを送信する前に履歴接続をブロックしなかったため、「パッシブ イニシエータ」が履歴接続を確立して無駄にデータを送信し、適切に無駄になっていることがわかります。 「パッシブイニシエーター」のリソース。

したがって、この現象を解決するには、リソースを無駄にしないように、「パッシブ イニシエーター」がデータを送信する前、つまり接続が確立される前に履歴接続をブロックすることが最善であり、この機能を実現するには次の時間がかかります。 3回握手をする。

ソースコード分析
RSTに戻ると言ったらRSTを返すのでしょうか? もちろんそうではありません。私が述べた結論を証明するにはソース コードを使用する必要があります。

ソースコード解析が必要と聞くと落胆する学生もいるかもしれません。

実際、今日の問題を分析するために、if else を理解していれば、コードのロジックを中国語で表現することもできるので、単に私のテキストを読むことも可能です。

今回は、SYN_SENT 状態で間違った確認番号を含む syn+ack メッセージを処理する方法を分析することに焦点を当てます。

SYN_SENT 状態のクライアントは、サーバーから syn+ack メッセージを受信した後、最終的に tcp_rcv_state_process を呼び出し、TCP 状態に従って対応する処理を実行します。ここでは SYN_SENT 状態のみに焦点を当てます。

// net/ipv4/tcp_ipv4.c
int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb)
{  ...  int queued = 0;   ...  switch (sk->sk_state) {  case TCP_CLOSE:   ...  case TCP_LISTEN:   ...  case TCP_SYN_SENT:     ....   queued = tcp_rcv_synsent_state_process(sk, skb, th);   if (キューに入っている >= 0)    キューに入っている状態を返します。     ...  }

  

  

  












次に、tcp_rcv_synsent_state_process 関数が呼び出され続けることがわかります。

static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
      const struct tcphdr *th)
{  ....

 if (th->ack) {   /* rfc793:    * "状態が SYN-SENT の場合、    * まず ACK ビットをチェックします    * ACK ビットがセットされている場合    * SEG.ACK =< ISS、または SEG.ACK > SND の場合.NXT、    * リセットを送信します (RST ビットが設定されていない限り、* 設定されている場合は    * セグメントを削除して戻ります)"    */     // ack の承認号は不確実です   if (!after(TCP_SKB_CB(skb)->ack_seq, tp ->snd_una) ||       after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt))       //回 RST 报文    goto replace_and_undo;












  ...
}


上記の関数から、SYN_SENT 状態で、間違った確認番号を含む syn+ack メッセージを受信した場合、クライアントは RST メッセージを返すことがわかります。

概要
TCP スリーウェイ ハンドシェイクで、クライアントが 2 回目のハンドシェイクで受信した ACK 確認番号が予期したものではありません。どうなりますか? RST メッセージを直接破棄するか、返す必要がありますか?

RST メッセージを返します。

どのような状況で不正な ack (2 回目のハンドシェイクでの ack) が受信されるのでしょうか?

クライアントが複数の SYN パケットを送信し、ネットワークが混雑している場合、「古い SYN パケット」が「新しい SYN パケット」よりも先にサーバーに到着し、サーバーは受信した「古い SYN パケット」に従う「SYN メッセージ」を送信します。 syn+ack メッセージを返信しますが、このメッセージの確認応答番号はクライアントが受信すると予想しているものではないため、クライアントは RST メッセージを返します。

おすすめ

転載: blog.csdn.net/weixin_42450130/article/details/130024661