本文章使用PHP程式碼來測試TCP傳輸層的一個通訊過程
TCP/IP協議可以檢視本人釋出過的內容
TCP/IP協議
當然關於該協議的詳細內容可自行查閱<<TCP/IP詳解 卷1:協議>>
PHP的tcp/udp API內容是PHP手冊上的內容
socket API
測試的PHP版本
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/IP選項檔案
然後我們啟動服務
服務狀態檢視命令:netstat -ntlapc 可每隔一秒重新整理一次狀態
tcpdump工具:tcpdump -A -XX -i lo
客戶端我們使用telent工具連線測試 即可
測試圖如下
連線和關閉圖
如果是客戶端發起的關閉則狀態則是:
客戶端先傳送一個結束報文FIN包,此時處於FIN_WAIT1狀態,伺服器確認應答處於CLOSE_WAIT狀態
此時客戶端處於FIN_WAIT2狀態,當伺服器也發了一次FIN結束報文時,伺服器處於LAST_LOCK狀態,客戶端確認後處於TIME_WAIT狀態,伺服器則是關閉CLOSED了
狀態轉移圖
不管你用的是PHP提供的SOCKET API還是SWOOLE或是C寫的測試都一樣的!