TCP 的連線建立與關閉狀態及資料傳輸通訊過程【含有 PHP 測試實驗程式碼】

勺顛顛發表於2019-06-19

本文章使用PHP程式碼來測試TCP傳輸層的一個通訊過程
TCP/IP協議可以檢視本人釋出過的內容
TCP/IP協議
當然關於該協議的詳細內容可自行查閱<<TCP/IP詳解 卷1:協議>>
PHP的tcp/udp API內容是PHP手冊上的內容
socket API
測試的PHP版本
TCP的連線建立與關閉狀態及資料傳輸通訊過程
PHP程式碼 【就是手冊的例子了】

 <?php
/**
 * Created by PhpStorm.
 * User: 1655664358@qq.com
 * Date: 2019/6/19
 * Time: 9:55
 */
error_reporting(E_ALL);

/* Allow the script to hang around waiting for connections. */
set_time_limit(0);

/* Turn on implicit output flushing so we see what we're getting
 * as it comes in. */
ob_implicit_flush();

$address = '0.0.0.0';  //要監聽的ip地址
$port = 54321;//要監聽的埠 
//建立一個socket AF_INET是一個協議族 (一般都有AF_INET,AF_INET6,AF_UNIX c語言提供也是,只不過是地址族),而PHP叫網路協議  
//C語言提供的協議族是PF_INET,PF_INET6,PF_UNIX  
//SOCK_STREAM sock的服務型別  這裡是位元組流的服務型別用於TCP
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}

//socket選項  ,選項一般在socket建立後設定 用於設定TCP的連線屬性
//選項幾乎和c差不多一樣
//一般來說這些選項我們可以通過修改系統核心來調整 
if (!socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1)) {
    echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL;
}

if (!socket_set_option($sock, SOL_SOCKET, SO_REUSEPORT, 1)) {
    echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL;
}

//繫結【有的叫命名socket】
if (socket_bind($sock, $address, $port) === false) {
    echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}

//監聽 此時伺服器進入  LISTEN狀態
if (socket_listen($sock, 5) === false) {
    echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}

do {
//接受客戶端連線sock  從系統核心接受佇列裡取  如果取出則雙方進入了ESTABLISHED狀態
    if (($msgsock = socket_accept($sock)) === false) {
        echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
        break;
    }
    /* Send instructions. */
    $msg = "\nWelcome to the PHP Test Server. \n" .
        "To quit, type 'quit'. To shut down the server type 'shutdown'.\n";
    socket_write($msgsock, $msg, strlen($msg));

    do {
    //讀客戶端資料【從TCP接收接受緩衝區取】
        if (false === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
            echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)) . "\n";
            break 2;
        }
        if (!$buf = trim($buf)) {
            continue;
        }
        if ($buf == 'quit') {
            break;
        }
        if ($buf == 'shutdown') {
        //傳送FIN結束報文,正常情況伺服器會進入CLOSED狀態
            socket_close($msgsock);
            break 2;
        }
        $talkback = "PHP: You said '$buf'.\n";
        socket_write($msgsock, $talkback, strlen($talkback));
        echo "$buf\n";
    } while (true);
    socket_close($msgsock);
} while (true);

socket_close($sock);

socket服務型別
TCP的連線建立與關閉狀態及資料傳輸通訊過程
TCP/IP選項檔案
TCP的連線建立與關閉狀態及資料傳輸通訊過程

然後我們啟動服務
服務狀態檢視命令:netstat -ntlapc 可每隔一秒重新整理一次狀態
tcpdump工具:tcpdump -A -XX -i lo
客戶端我們使用telent工具連線測試 即可
測試圖如下
TCP的連線建立與關閉狀態及資料傳輸通訊過程

TCP的連線建立與關閉狀態及資料傳輸通訊過程

TCP 的連線建立與關閉狀態及資料傳輸通訊過程

TCP 的連線建立與關閉狀態及資料傳輸通訊過程

連線和關閉圖
TCP 的連線建立與關閉狀態及資料傳輸通訊過程

如果是客戶端發起的關閉則狀態則是:
客戶端先傳送一個結束報文FIN包,此時處於FIN_WAIT1狀態,伺服器確認應答處於CLOSE_WAIT狀態
此時客戶端處於FIN_WAIT2狀態,當伺服器也發了一次FIN結束報文時,伺服器處於LAST_LOCK狀態,客戶端確認後處於TIME_WAIT狀態,伺服器則是關閉CLOSED了
狀態轉移圖
TCP 的連線建立與關閉狀態及資料傳輸通訊過程

不管你用的是PHP提供的SOCKET API還是SWOOLE或是C寫的測試都一樣的!

相關文章