目次
FIN を発行する前に、プログラムは close() または shutdown() を実行する必要がありますか? " >FIN を発行する前に、プログラムは close() または shutdown() を実行する必要がありますか?
アクティブなパーティはクローズ後に受信したデータをどのように処理しますか?" >アクティブなパーティはクローズ後に受信したデータをどのように処理しますか?
怎么知道对端socket执行了close还是shutdown" >怎么知道对端socket执行了close还是shutdown
受動的な側が 3 回目に手を振らなかったらどうなるか" >受動的な側が 3 回目に手を振らなかったらどうなるか
TCP三次挥手" >TCP三次挥手
送信したいデータがある場合、3回手を振るだけで送信できないのでしょうか?" >送信したいデータがある場合、3回手を振るだけで送信できないのでしょうか?
IP ポート を持つプロセスです。 " >通常の状況では、TCP 接続の両端は異なる IP ポート を持つプロセスです。
一个socket能建立连接?" >一个socket能建立连接?
一端发出第一次握手后,如果又收到了第一次握手的SYN包,TCP连接状态会怎么变化?" >一端发出第一次握手后,如果又收到了第一次握手的SYN包,TCP连接状态会怎么变化?
一端发出第二次握手后,如果又收到第二次握手的SYN+ACK包,TCP连接状态会怎么变化?" >一端发出第二次握手后,如果又收到第二次握手的SYN+ACK包,TCP连接状态会怎么变化?
一端第一次挥手后,又收到第一次挥手的包,TCP连接状态会怎么变化?" >一端第一次挥手后,又收到第一次挥手的包,TCP连接状态会怎么变化?
代码复现自连接" >代码复现自连接
自连接的解决方案" >自连接的解决方案
四次握手" >四次握手
复现TCP同时打开" >复现TCP同时打开
概要" >概要
ついに" >ついに
ホームページ よくある問題 また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?

Jul 24, 2023 pm 05:18 PM
tcp

TCP が 接続指向で信頼性の高い、バイト ストリーム ベースのトランスポート層通信プロトコルであることは誰もが知っています。

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?
TCPとは

そして、ここで言及されている「接続指向」は、それです。は、接続を確立し、接続を使用し、接続を解放する必要があることを意味します。

接続の確立とは、よく知られた TCP スリーウェイ ハンドシェイクを指します。

そして、コネクションを利用して、送信1回、確認1回の形でデータ送信が行われます。

「接続を解放する」もあります。これは、一般的な TCP 4 つのウェーブ ジェスチャ です。

TCP 4 つの波 誰もがよく知っているはずですが、3 つの波 を見たことはありますか?そして 2 回手を振りました ? ######見たことありますか? 4ウェイハンドシェイク

についてはどうですか?

今日のトピックは単なる好奇心やトリビアに関するものではありません。

4 つの波から始めて、実践的な知識を取得しましょう。


TCP ウェーブは 4 回

TCP の 4 つのウェーブについて簡単に説明します。
また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?
TCP ウェーブ 4 回

通常の状況では。データ送信が完了している限り、クライアントとサーバーの両方が 4 つのウェーブをアクティブに開始できます

図のように、4 つのウェーブがクライアントによって開始されたと仮定すると、それは アクティブ パーティ です。サーバーはクライアントの Wave リクエストを受動的に受信します。これは パッシブ パーティー と呼ばれます。

クライアントとサーバーは、最初は ESTABLISHED 状態にあります。

初めて手を振る: 通常の状況では、アクティブなパーティが close() または shutdown() メソッドを実行すると、 ## が送信されます。#FIN メッセージ が表示され、「データを送信しません 」と表示されます。

第 2 波 : アクティブなパーティから FIN メッセージを受信した後、パッシブなパーティはすぐに ACK で応答します。これは、「私は」を意味します。 FIN を受け取りましたが、今後データを送信しないことも承知しています。」

上記の内容は、

アクティブな当事者がデータを送信しなくなったことです。ただし、この時点で 受動的なパーティ にまだ送信するデータがある場合は、送信を続けます。なお、2波目から3波目まではパッシブ側からアクティブ側にデータを送信できますが、アクティブ側が正常に受信できるかどうかは後述しますので不明です。

第 3 の波 : 受動的な側は第 2 の波を感知した後、一連の仕上げ作業を行い、最後に close() を呼び出します。そのとき、FIN-ACK の第 3 波が発行されます。

第 4 ウェーブ : アクティブなパーティは、受信されたことを意味する ACK で応答します。

最初のウェーブと 3 番目のウェーブは両方とも、アプリケーション内でアクティブにトリガーされます (close() メソッドの呼び出しなど)。これは、記述するときに注意する必要があることです。コード、場所。

2 番目と 4 番目のウェーブはカーネル プロトコル スタックによって自動的に行われますが、コードを書くときにこの場所に触れることができないため、あまり気にする必要はありません。

さらに、アクティブかパッシブかに関係なく、各当事者は FINACK を送信します。 FINACK も受け取りました。 この点については後述しますのでご注意ください。

FIN を発行する前に、プログラムは close() または shutdown() を実行する必要がありますか?

不確実です。通常、FIN は、socketclose() または shutdown() メソッドを実行することによって発行されます。しかし実際には、アプリケーションが終了する限り、active が終了するか passive が終了するかに関係なく (何らかの不可解な理由で killed されます)、問題 FIN

FINは「データを送信しなくなりました」という意味なので、shutdown()は読み込みがクローズされた場合には相手にFINを送信しませんが、書き込みがクローズされた場合にはFINが送信されます。


#マシン上に FIN-WAIT-2 状態が多すぎる場合、なぜ

上記によると4 つの波 図から、

FIN-WAIT-2アクティブ パーティ のステータスであることがわかります。

この状態のプログラムは、

FIN第 3 波を待っています。 3 番目のウェーブは、受動的側がコード内で close() を実行して発行する必要があります。

したがって、マシン上に多数の

FIN-WAIT-2 状態がある場合、一般的に、別のマシン上にも多数の CLOSE_WAIT が存在します。 CLOSE_WAIT が多数発生するマシンが、接続を閉じるために close() を呼び出すことに消極的な理由を確認する必要があります。

したがって、マシン上に

FIN-WAIT-2 状態が多すぎる場合、通常はピアが close() メソッドを実行していないことが原因です。第3波を起こす。

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?FIN-WAIT-2 が非常に多い理由


アクティブなパーティはクローズ後に受信したデータをどのように処理しますか?

以前に書いた記事「コードの実行が成功したら、データは送信されますか?」 》では、ソース コードの観点から、通常の状況では 、プログラムがアクティブに close();

  • を実行する場合に言及されています。現在の接続に対応する socket受信バッファ にデータがある場合、RST が送信されます。

  • 送信バッファ にデータがある場合は、送信が完了するまで待機してから最初のウェーブ FIN を送信します。

ご存知のとおり、TCP は

全二重通信 であり、データを送信しながらデータを受信できることを意味します。

Close() は、 メッセージの送受信機能を 同時に閉じる必要があることを意味します。

つまり、

理論上はですが、第2波と第3波の間では、パッシブ側がアクティブ側にデータを送信できます。

ただし、アクティブ パーティの 4 つのウェーブが

close() によってトリガーされた場合、アクティブ パーティはこのメッセージを受信しません。また、#RST と応答します。この接続を直接終了します。

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?close() は TCP を 4 回ウェーブさせるトリガーになります


ハンドの第 2 波と第 3 波の間でデータを転送することはできませんか? #########いいえ。前述したように、Close()

メッセージの送受信機能を 同時に閉じることを意味します。 メッセージの送信のみを無効にし 、メッセージの受信機能を無効にしない

ことができれば、引き続きメッセージを受信できます。この

half-close 関数は、shutdown() メソッドを呼び出すことで実現できます。

int shutdown(int sock, int howto);
ログイン後にコピー
Howto は切断方法です。次の値が利用可能です:

SHUT_RD: 読み取りを終了します。このとき、アプリケーション層はデータの受信を試行する必要がなく、受信バッファがカーネルプロトコルスタック内のデータを受信したとしても、そのデータは破棄されます。

  • SHUT_WR: 書き込みを終了します。送信バッファ内に未送信のデータが残っている場合、そのデータはターゲットホストに配信されます。

  • SHUT_RDWR: 読み取りと書き込みを閉じます。 close()

    と同等。
  • シャットダウンによる TCP 4 ウェーブ
また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?

怎么知道对端socket执行了close还是shutdown

不管主动关闭方调用的是close()还是shutdown(),对于被动方来说,收到的就只有一个FIN

被动关闭方就懵了,"我怎么知道对方让不让我继续发数据?"

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?

其实,大可不必纠结,该发就发。

第二次挥手和第三次挥手之间,如果被动关闭方想发数据,那么在代码层面上,就是执行了 send() 方法。

int send( SOCKET s,const char* buf,int len,int flags);
ログイン後にコピー

send() 会把数据拷贝到本机的发送缓冲区。如果发送缓冲区没出问题,都能拷贝进去,所以正常情况下,send()一般都会返回成功。

![tcp_sendmsg 逻辑](https://cdn.jsdelivr.net/gh/xiaobaiTech/image/tcp_sendmsg 逻辑.png)

然后被动方内核协议栈会把数据发给主动关闭方。

  • 如果上一次主动关闭方调用的是shutdown(socket_fd, SHUT_WR)。那此时,主动关闭方不再发送消息,但能接收被动方的消息,一切如常,皆大欢喜。

  • 最後の active クロージング パーティーが close() を呼び出した場合。 アクティブ パーティ は、パッシブ パーティ からデータを受信した後、直接 破棄 し、RST で応答します。

2 番目の状況について。

パッシブ側カーネルプロトコルスタックは、RSTを受信した後に接続を閉じます。しかし、カーネル接続は閉じられており、アプリケーション層は (通知されない限り) 知りません。

この時点で、受動側アプリケーション層次の操作は読み取りまたは書き込みにすぎません。

  • これが読み取られると、RST のエラーが返されます。これは、ピア による一般的な 接続のリセットです。

  • これが記述されると、プログラムは SIGPIPE 信号を生成します。アプリケーション層のコードは信号をキャプチャして処理できます。処理されない場合、プロセスは終了します。デフォルトでは予期せず終了します。


要約すると、パッシブ終了メソッド recv()EOF## を返す場合、 #、アクティブなパーティが close() または shutdown(fd, SHUT_WR) を通じて最初のウェーブを開始したことを意味します。

この時点で受動側が を 2 回実行した場合 send()

  • 初回 send() は通常、正常に返されます。

  • 2 回目 send()。アクティブなパーティが shutdown(fd, SHUT_WR) を介して最初のウェーブを開始した場合、この時点では send() は引き続き成功します。アクティブなパーティが close() を通じて最初のウェーブを開始すると、この時点で SIGPIPE シグナルが生成され、プロセスはデフォルトで終了し、異常終了します。異常終了したくない場合は、必ずこのシグナルをキャッチして処理してください。


受動的な側が 3 回目に手を振らなかったらどうなるか

3 回目の手を振るclose() の呼び出しなど、受動的なパーティ によってアクティブにトリガーされます。

コードエラーまたはその他の理由により、受動的側は第 3 ウェーブを実行しません。

現時点では、アクティブなパーティは、最初に手を振るときに close() を使用するか shutdown(fd, SHUT_WR) を使用するかによって異なる動作をします。時間、パフォーマンス。

  • 如果是 shutdown(fd, SHUT_WR) ,说明主动方其实只关闭了写,但还可以读,此时会一直处于 FIN-WAIT-2, 死等被动方的第三次挥手。

  • 如果是 close(), 说明主动方读写都关闭了,这时候会处于 FIN-WAIT-2一段时间,这个时间由 net.ipv4.tcp_fin_timeout 控制,一般是 60s,这个值正好跟2MSL一样 。超过这段时间之后,状态不会变成 `TIME-WAIT`,而是直接变成`CLOSED`。

# cat /proc/sys/net/ipv4/tcp_fin_timeout
60
ログイン後にコピー

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?
一直不发第三次挥手的情况


TCP三次挥手

四次挥手聊完了,那有没有可能出现三次挥手?

是可能的。

我们知道,TCP四次挥手里,第二次和第三次挥手之间,是有可能有数据传输的。第三次挥手的目的是为了告诉主动方,"被动方没有数据要发了"。

所以,在第一次挥手之后,如果被动方没有数据要发给主动方。第二和第三次挥手是有可能合并传输的。这样就出现了三次挥手。

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?
TCP三次挥手


送信したいデータがある場合、3回手を振るだけで送信できないのでしょうか?

上記は、送信するデータがない状況です。 send. 2回目の場合、3回目のウェーブの間に送信するデータがあるので、3回振るのは無理でしょうか? ######### ではありません ###。 TCP には 遅延確認 と呼ばれる機能もあります。これは単純に次のように理解できます。

受信側は、データを受信した直後に ACK 確認パケットで応答する必要はありません。

これに基づいて、受信者は確認をマージできるため、データ パケットが送信されるたびに ACK 確認パケットを受信できるわけではありません。

そして、このマージ確認を4つのウェーブに配置し、第2ウェーブ、第3ウェーブ、それらの間のデータ送信をマージしてまとめて送信することができます。つまり3つの波がありました。 #TCP ウェーブ 3 回遅延確認

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?TCP ウェーブ 2 回
4 つのウェーブで前述したように、クロージング時に、両当事者

が FIN を送信し、ACK
を受信しました。

通常の状況では、TCP 接続の両端は異なる IP ポート を持つプロセスです。

しかし、IP ポート が TCP 接続の両端で同じである場合、接続を閉じるときに同じことが起こります。

一方の端は FIN を送信します。 ACK

を受信しましたが、たまたま両端が実際には 同じソケットでした。

TCP は 2 回ウェーブしますそして、両端で同じ IP ポート

を使用するこの種の接続は、
TCP セルフと呼ばれます-繋がり ###。 また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?

是的,你没看错,我也没打错别字。同一个socket确实可以自己连自己,形成一个连接。


一个socket能建立连接?

上面提到了,同一个客户端socket,自己对自己发起连接请求。是可以成功建立连接的。这样的连接,叫TCP自连接

下面我们尝试下复现。

注意我是在以下系统进行的实验。在mac上多半无法复现。

#  cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ログイン後にコピー

通过nc命令可以很简单的创建一个TCP自连接

# nc -p 6666 127.0.0.1 6666
ログイン後にコピー

上面的 -p 可以指定源端口号。也就是指定了一个端口号为6666的客户端去连接 127.0.0.1:6666

# netstat -nt | grep 6666
tcp        0      0 127.0.0.1:6666          127.0.0.1:6666          ESTABLISHED
ログイン後にコピー
ログイン後にコピー

整个过程中,都没有服务端参与。可以抓个包看下。

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?
image-20210810093309117

可以看到,相同的socket,自己连自己的时候,握手是三次的。挥手是两次的。

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?
TCP自连接

上面这张图里,左右都是同一个客户端,把它画成两个是为了方便大家理解状态的迁移。

我们可以拿自连接的握手状态对比下正常情况下的TCP三次握手。

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?
正常情况下的TCP三次握手

看了自连接的状态图,再看看下面几个问题。


一端发出第一次握手后,如果又收到了第一次握手的SYN包,TCP连接状态会怎么变化?

第一次握手过后,连接状态就变成了SYN_SENT状态。如果此时又收到了第一次握手的SYN包,那么连接状态就会从SYN_SENT状态变成SYN_RCVD

// net/ipv4/tcp_input.c
static int tcp_rcv_synsent_state_process()
{
    // SYN_SENT状态下,收到SYN包
    if (th->syn) {
        // 状态置为 SYN_RCVD
        tcp_set_state(sk, TCP_SYN_RECV);
    }
}
ログイン後にコピー


一端发出第二次握手后,如果又收到第二次握手的SYN+ACK包,TCP连接状态会怎么变化?

第二握手过后,连接状态就变为SYN_RCVD了,此时如果再收到第二次握手的SYN+ACK包。连接状态会变为ESTABLISHED

// net/ipv4/tcp_input.c
int tcp_rcv_state_process()
{
    // 前面省略很多逻辑,能走到这就认为肯定有ACK
    if (true) {
        // 判断下这个ack是否合法
        int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH | FLAG_UPDATE_TS_RECENT) > 0;
        switch (sk->sk_state) {
        case TCP_SYN_RECV:
            if (acceptable) {
        // 状态从 SYN_RCVD 转为 ESTABLISHED
                tcp_set_state(sk, TCP_ESTABLISHED);
            } 
        }
    }
}
ログイン後にコピー


一端第一次挥手后,又收到第一次挥手的包,TCP连接状态会怎么变化?

第一次挥手过后,一端状态就会变成 FIN-WAIT-1。正常情况下,是要等待第二次挥手的ACK。但实际上却等来了 一个第一次挥手的 FIN包, 这时候连接状态就会变为CLOSING

// net/
static void tcp_fin(struct sock *sk)
{
    switch (sk->sk_state) {
    case TCP_FIN_WAIT1:
        tcp_send_ack(sk);
    // FIN-WAIT-1状态下,收到了FIN,转为 CLOSING
        tcp_set_state(sk, TCP_CLOSING);
        break;
    }
}
ログイン後にコピー

这可以说是隐藏剧情了。

CLOSING 很少见,除了出现在自连接关闭外,一般还会出现在TCP两端同时关闭连接的情况下。

处于CLOSING状态下时,只要再收到一个ACK,就能进入 TIME-WAIT 状态,然后等个2MSL,连接就彻底断开了。这跟正常的四次挥手还是有些差别的。大家可以滑到文章开头的TCP四次挥手再对比下。


代码复现自连接

可能大家会产生怀疑,这是不是nc这个软件本身的bug

那我们可以尝试下用strace看看它内部都做了啥。

# strace nc -p 6666 127.0.0.1 6666
// ...
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK)    = 0
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(6666), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(6666), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now in progress)
// ...
ログイン後にコピー

无非就是以创建了一个客户端socket句柄,然后对这个句柄执行 bind, 绑定它的端口号是6666,然后再向 127.0.0.1:6666发起connect方法。

我们可以尝试用C语言去复现一遍。

下面的代码,只用于复现问题。直接跳过也完全不影响阅读。

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>


int main()
{
    int lfd, cfd;
    struct sockaddr_in serv_addr, clie_addr;
    socklen_t clie_addr_len;
    char buf[BUFSIZ];
    int n = 0, i = 0, ret = 0 ;
    printf("This is a client \n");

    /*Step 1: 创建客户端端socket描述符cfd*/    
    cfd = socket(AF_INET, SOCK_STREAM, 0);
    if(cfd == -1)
    {
        perror("socket error");
        exit(1);
    }

    int flag=1,len=sizeof(int);
    if( setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &flag, len) == -1)
    {
        perror("setsockopt");
        exit(1);
    } 


    bzero(&clie_addr, sizeof(clie_addr));
    clie_addr.sin_family = AF_INET;
    clie_addr.sin_port = htons(6666);
    inet_pton(AF_INET,"127.0.0.1", &clie_addr.sin_addr.s_addr);

    /*Step 2: 客户端使用bind绑定客户端的IP和端口*/  
    ret = bind(cfd, (struct sockaddr* )&clie_addr, sizeof(clie_addr));
    if(ret != 0)
    {
        perror("bind error");
        exit(2);
    }

    /*Step 3: connect链接服务器端的IP和端口号*/    
    bzero(&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(6666);
    inet_pton(AF_INET,"127.0.0.1", &serv_addr.sin_addr.s_addr);
    ret = connect(cfd,(struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if(ret != 0)
    {
        perror("connect error");
        exit(3);
    }

    /*Step 4: 向服务器端写数据*/
    while(1)
    {
        fgets(buf, sizeof(buf), stdin);
        write(cfd, buf, strlen(buf));
        n = read(cfd, buf, sizeof(buf));
        write(STDOUT_FILENO, buf, n);//写到屏幕上
    }
    /*Step 5: 关闭socket描述符*/
    close(cfd);
    return 0;
}
ログイン後にコピー

保存为 client.c 文件,然后执行下面命令,会发现连接成功。

# gcc client.c -o client && ./client
This is a client
ログイン後にコピー
# netstat -nt | grep 6666
tcp        0      0 127.0.0.1:6666          127.0.0.1:6666          ESTABLISHED
ログイン後にコピー
ログイン後にコピー

说明,这不是nc的bug。事实上,这也是内核允许的一种情况。


自连接的解决方案

自连接一般不太常见,但遇到了也不难解决。

解决方案比较简单,只要能保证客户端和服务端的端口不一致就行。

事实上,我们写代码的时候一般不会去指定客户端的端口,系统会随机给客户端分配某个范围内的端口。而这个范围,可以通过下面的命令进行查询

# cat /proc/sys/net/ipv4/ip_local_port_range
32768   60999
ログイン後にコピー

也就是只要我们的服务器端口不在32768-60999这个范围内,比如设置为8888。就可以规避掉这个问题。

另外一个解决方案,可以参考golang标准网络库的实现,在连接建立完成之后判断下IP和端口是否一致,如果遇到自连接,则断开重试。

func dialTCP(net string, laddr, raddr *TCPAddr, deadline time.Time) (*TCPConn, error) {
    // 如果是自连接,这里会重试
    for i := 0; i < 2 && (laddr == nil || laddr.Port == 0) && (selfConnect(fd, err) || spuriousENOTAVAIL(err)); i++ {
        if err == nil {
            fd.Close()
        }
        fd, err = internetSocket(net, laddr, raddr, deadline, syscall.SOCK_STREAM, 0, "dial", sockaddrToTCP)
    }
    // ...
}

func selfConnect(fd *netFD, err error) bool {
    // 判断是否端口、IP一致
    return l.Port == r.Port && l.IP.Equal(r.IP)
}
ログイン後にコピー


四次握手

前面提到的TCP自连接是一个客户端自己连自己的场景。那不同客户端之间是否可以互联?

答案是可以的,有一种情况叫TCP同时打开

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?
TCP同时打开

大家可以对比下,TCP同时打开在握手时的状态变化,跟TCP自连接是非常的像。

比如SYN_SENT状态下,又收到了一个SYN,其实就相当于自连接里,在发出了第一次握手后,又收到了第一次握手的请求。结果都是变成 SYN_RCVD

SYN_RCVD 状态下收到了 SYN+ACK,就相当于自连接里,在发出第二次握手后,又收到第二次握手的请求,结果都是变成 ESTABLISHED他们的源码其实都是同一块逻辑。


复现TCP同时打开

分别在两个控制台下,分别执行下面两行命令。

while true; do nc -p 2224 127.0.0.1 2223 -v;done

while true; do nc -p 2223 127.0.0.1 2224 -v;done
ログイン後にコピー

上面两个命令的含义也比较简单,两个客户端互相请求连接对方的端口号,如果失败了则不停重试。

执行后看到的现象是,一开始会疯狂失败,重试。一段时间后,连接建立完成。

# netstat -an | grep  2223
Proto Recv-Q Send-Q Local Address           Foreign Address         State 
tcp        0      0 127.0.0.1:2224          127.0.0.1:2223          ESTABLISHED
tcp        0      0 127.0.0.1:2223          127.0.0.1:2224          ESTABLISHED
ログイン後にコピー

期间抓包获得下面的结果。

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?

可以看到,这里面建立连接用了四次交互。因此可以说这是通过"四次握手"建立的连接。

而且更重要的是,这里面只涉及两个客户端,没有服务端

看到这里,不知道大家有没有跟我一样,被刷新了一波认知,对socket有了重新的认识。

在以前的观念里,建立连接,必须要有一个客户端和一个服务端,并且服务端还要执行一个listen()和一个accept()。而实际上,这些都不是必须的。

次に、面接官が listen() なしで TCP は接続を確立できますか?」 と質問したら、誰もが答え方を知っているはずです。

しかし、問題が再び発生します。クライアントが 2 つしかなく、listen() がありません。なぜ TCP 接続が確立できるのでしょうか?

ご興味がございましたら、将来的にこの穴を再度埋める機会がございます。


概要

  • プログラムの有無に関係なく、4 回手を振ります close() がアクティブに実行されるか、プロセスが強制終了されると、最初のウェーブ FIN パケットが送信される可能性があります。マシン上の FIN-WAIT-2 状態が多すぎる場合は、通常、ピアが close() メソッドを実行して 3 番目のウェーブを発行していないことが原因です。

  • Close() は、 同時にメッセージを送受信する機能を無効にします。 shutdown() メッセージの送受信を個別にシャットダウンできます。

  • 第2波と第3波が複合する可能性はあります。したがって、4 つの波は
  • 3 つの波

    になりました。

  • 同じソケットが自分自身に接続すると、
  • TCP セルフコネクション

    が発生し、セルフコネクションのウェーブは 2 回ウェーブします。

  • listen を行わなくても、2 つのクライアント間で接続を確立できます。この状況は TCP が同時に開くと呼ばれ、4 ウェイ ハンドシェイク によって引き起こされます。


ついに

本日、2回手を振るか#に関係なく言及されました。 ##自己接続、または TCP が同時にオープン など。

一見すると、毎日のレンガ移動には役に立たないかもしれませんが、実際には本当に役に立ちません。

そして面接では聞かれない可能性が高いです。

結局のところ、ほとんどの面接官は、フェンネルという単語が何通りに書かれるかなど気にしません。

この記事の主な目的は、

socket を別の角度から皆さんに再理解していただくことです。 TCP 自体は接続でき、サーバーなしでも 2 つのクライアントさえ接続できることがわかりました。

これは本当に、本当に予想外です。

以上がまた近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

win10でtcp/ipプロトコルをリセットするにはどうすればよいですか? Windows 10でTCP/IPプロトコルスタックをリセットする方法 win10でtcp/ipプロトコルをリセットするにはどうすればよいですか? Windows 10でTCP/IPプロトコルスタックをリセットする方法 Mar 16, 2024 am 11:07 AM

win10でtcp/ipプロトコルをリセットするにはどうすればよいですか?実際、その方法は非常に簡単で、ユーザーがコマンド プロンプトに直接入力し、ctrl Shift Enter キーの組み合わせを押して操作を実行するか、reset コマンドを直接実行して設定することができます。 Windows 10 で TCP/IP プロトコル スタックをリセットする方法をユーザーに注意深く紹介します。 Windows 10 で tcp/ip プロトコル スタックをリセットする方法 1。 管理者権限 1. ショートカット キー win R を使用してファイル名を指定して実行ウィンドウを直接開き、「cmd」と入力し、ctrl Shift Enter キーの組み合わせを押し続けます。 2. または、スタート メニューでコマンド プロンプトを直接検索し、右クリックします。

TCPを使用してPythonでクライアントとサーバー間の会話を実装する方法 TCPを使用してPythonでクライアントとサーバー間の会話を実装する方法 May 17, 2023 pm 03:40 PM

TCP クライアント TCP プロトコルを使用して継続的な対話を実現するクライアントのサンプル コード: importsocket#クライアント設定 HOST='localhost'PORT=12345#TCP ソケットを作成し、サーバーに接続 client_socket=socket.socket(socket.AF_INET,socket . SOCK_STREAM)client_socket.connect((HOST,PORT))whileTrue:#ユーザー入力を取得 message=input("送信するメッセージを入力してください:&

Java API開発におけるTCP通信にNetty4を使用する Java API開発におけるTCP通信にNetty4を使用する Jun 17, 2023 pm 11:18 PM

TCP は、コンピュータ ネットワーク通信プロトコルの一種であり、コネクション型の伝送プロトコルです。 Javaアプリケーション開発において、TCP通信はクライアントとサーバー間のデータ送信、音声や映像のリアルタイム送信など、さまざまなシーンで広く利用されています。 Netty4 は、サーバーとクライアント間のデータ交換プロセスを最適化し、効率性と信頼性を高めることができる、高性能、拡張性の高いネットワーク プログラミング フレームワークです。 TCP 通信に Netty4 を使用する具体的な実装手順は次のとおりです。

また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの? また近いうちにお会いしましょう! TCP が 2 回振られるのを見たことがありますか? 4回の握手はどうなるの? Jul 24, 2023 pm 05:18 PM

ここで言う「コネクション指向」とは、コネクションを確立し、コネクションを利用し、コネクションを解放する必要があることを意味します。接続の確立とは、よく知られている TCP スリーウェイ ハンドシェイクを指します。接続を使用する場合、データは 1 回の送信と 1 回の確認の形式で送信されます。また、一般的な TCP 4 波である接続の解放もあります。

Linux SIGPIPE 信号 Linux SIGPIPE 信号 Feb 19, 2024 pm 04:00 PM

以下では、説明の便宜上、TCPの通信当事者のうち、通信当事者をA、Bに置き換えて説明する。 TCP プロトコルによれば、A が接続を閉じた後も B がデータを送信し続けると、B は A の RST 応答を受信します。 B がデータの送信を続ける場合、システムは SIGPIPE 信号を送信して、接続が切断されたことを通知し、送信を停止します。 SIGPIPE シグナルに対するシステムのデフォルトの処理動作は、プロセス B を終了させることです。 SIGPIPE 信号に対するオペレーティング システムのデフォルトの処理動作は非常に不親切です。これを分析してみましょう。 TCP 通信は全二重チャネルであり、2 つの単信チャネルに相当し、接続の各端が 1 つのチャネルを担当します。反対側が「閉じる」場合、2 つのチャネル全体を閉じることが意図されていますが、ローカル エンドは FIN パケットのみを受信します。 TCP プロトコルの規定に従って、

Java で単一の TCP 接続を使用して複数のファイルを送信するにはどうすればよいですか? Java で単一の TCP 接続を使用して複数のファイルを送信するにはどうすればよいですか? Apr 27, 2023 am 08:49 AM

1 つの TCP 接続を使用して複数のファイルを送信することについてこのブログが書かれているのはなぜですか?最近関連の本を読んでいるのですが、単に Socket を使ってプログラミングするのは問題ありませんが、これはいくつかの基本的な概念を確立するだけです。本当の問題に対してはまだ何もできません。ファイルを転送する必要がある場合、データ (バイナリ データ) を送信しただけのように見えますが、ファイルに関する一部の情報 (ファイル拡張子) が失われていることがわかります。そして、毎回 1 つのファイルを送信するために 1 つのソケットしか使用できないため、連続してファイルを送信する方法はありません (ファイルの送信を完了するにはストリームを閉じることに依存しているため、実際にはファイルの長さがわかりません。したがって、1 つのソケット接続がファイルを表す場合にのみファイルを送信できます)。

TCPとIPの違いは何ですか TCPとIPの違いは何ですか Sep 04, 2023 pm 02:19 PM

TCP と IP はインターネットにおける 2 つの異なるプロトコルです: 1. TCP はトランスポート層プロトコル、IP はネットワーク層プロトコル; 2. TCP はデータ パケットのセグメント化、並べ替え、確認、再送信などの機能を提供します。プロトコルはデータ パケットの送信元アドレスと宛先アドレスを提供する役割を果たします; 3. TCP は接続指向のプロトコルですが、IP プロトコルはコネクションレスです; 4. TCP はフロー制御と輻輳制御も提供します。

インタビュアーは、「TCP 接続は HTTP リクエストをいくつ送信できますか?」と尋ねました。 インタビュアーは、「TCP 接続は HTTP リクエストをいくつ送信できますか?」と尋ねました。 Feb 22, 2023 pm 12:00 PM

かつて、面接でよくある質問で、「ブラウザに URL が入力されてからページが表示されるまでの過程で何が起こっていますか?」という質問がありました。予習をした学生ならほとんど答えられると思いますが、「受信したHTMLに数十個の画像タグが含まれている場合、どのような方法で、どのような順序で、どれだけの接続が確立され、どのようなプロトコルでダウンロードされるのか」と続けて尋ねると、これらの画像はどうでしょうか?