此節使用 PHP 原生 Socket 演示完成 http 請求的原理
套接字怎麼理解
一個典型的網路連線由 2 個套接字構成,一個執行在客戶端,另一個執行在伺服器端。
“套接字”取名叫做 socket,翻譯:“插座”。在網路程式設計中叫做用於網路資料傳輸的“軟體裝置”。
那麼通俗理解一點,“套接字” 就是一個檔案資源 file
。
以一家拉麵館做比喻
流程概要:假設場景是食城,一家拉麵店。透過 create
建立一個服務端套接字,初始化套接字有三個引數,是ipv4還是ipv6、面向連線還是面向訊息、tcp 還是 udp ,也就是描述這個套接字是幹嘛用,描述我是一家拉麵館。接下來透過 bind
埠號,也就是聯絡地址,食城第123號能找到我。透過 listen
開始監聽,準備好了開始營業(這時店裡沒人接待,顧客排隊等候)。呼叫 accept
函式等待客戶端連線(此處阻塞,直到有客戶端連線產生一個客戶端檔案描述符 fd ),也就是等待客人,直到來了一個顧客,此時店員拿起一張紙片問顧客問吃點啥,隨後將紙片轉給後廚。後廚 read
紙片上的菜品,然後準備食物 響應
,然後 write
給顧客。顧客走了,卡片也就沒用了扔掉了(fd回收),socket_close($client)
結束該顧客的服務。晚上10點,拉麵店關門,socket_close($server)
。
PHP 程式碼
建立一個套接字,$server
表示的是服務端的套接字。描述我是拉麵館。
$server = socket_create(
AF_INET,
SOCK_STREAM,
SOL_TCP);
繫結埠號,0.0.0.0
服務所有人,當然也可以是專供
socket_bind($socket, '0.0.0.0', 80);
開始監聽,5
排隊中等待接待的顧客人數,地方小門口只能站5個人(資源層面)。
socket_listen($socket, 5);
與一個客戶端建立連線,接待了張三
$client = socket_accept($server);
讀取客戶端訊號,張三:⌈來大碗拉麵加個蛋⌉,服務員拿紙記錄下來
$buf = socket_read($client, 1024);
處理訊號,後廚閱讀紙片,製片內容如下
$content = 'hello! i am server!';
$http_resonse = "HTTP/1.1 200 OK\r\n"; $http_resonse .= "Content-Type: text/html;charset=UTF-8\r\n"; $http_resonse .= "Connection: keep-alive\r\n"; $http_resonse .= "Server: php socket server\r\n"; $http_resonse .= "Content-length: ".strlen($content)."\r\n\r\n"; $http_resonse .= $content;
向客戶端寫入訊號,上菜品
socket_write($client,$http_resonse);
關閉客戶端,資源回收。系統建立的資源描述符是有限的(因為每個資源描述符都要開啟輸入輸出緩衝區,如果不及時回收資源,記憶體就會被佔滿)
socket_close($client);
關閉服務端,店面關門
socket_close($server);
處理訊號
上邊讀取到客戶端的訊號buf,如果我請求的地址是這樣:
列印buf:
buf是一長串字串,用正則來匹配該字串:
$preg = '#GET (.*) HTTP/1.1#iU';
preg_match($preg, $buf, $arr);
$request = $arr[1];
匹配的 (.*)
是:
這樣我們就拿到了請求的內容。
響應
如果是載入靜態頁面:
獲取的檔名是
那麼 $response
是:
$filePath = __DIR__ . '/html' . $path;
$content = file_get_contents($filePath);
如果是 php
檔案:
獲取的檔名是:
那麼 $response
是:
$filePath = __DIR__ . '/php' . $path;
ob_start();
include $filePath; //這裡執行了一段php邏輯程式碼
$content = ob_get_contents();
ob_clean();
本作品採用《CC 協議》,轉載必須註明作者和本文連結