本文轉載於我的個人部落格,原文地址 www.codeover.cn/php-socket/ 歡迎來訪
socket介紹
實現網路程式之間的通訊,幾乎所有應用程式都是採用 socket,socket 是應用層與 TCP/IP 協議族通訊的中間抽象層,它是一組介面。在設計模式中,socket 其實是一個門面模式,它把複雜的 TCP/IP 協議族隱藏在 socket 介面後面,對使用者來說,一組簡單的介面就是全部,讓 socket 去組織資料,以符合指定的協議
socket 的英文原意是 「孔」或「插座」,通常也被稱作「套接字」,用於描述 IP 地址和埠,是一個通訊鏈的控制程式碼,可以用來實現不同虛擬機器或不同計算機之間的通訊。
socket 連結的三個過程
- 服務端監聽:IP+埠號
- 客戶端請求:發出向服務端的 IP 以及埠的連線請求
- 連結確認:服務端套接字監聽到或者說接收到客戶端套接字連線請求,他就會建立一個新的程式,把服務端的套接字描述發給客戶端,以響應客戶端的請求,一旦客戶端確認了此描述,連線就建立好了。兒服務端的套接字繼續處於監聽狀態,繼續接受其他客戶端套接字的連線請求。
php實現socket
如果需要在 php 中使用 socket,則需要在編譯 php 是新增 --enable-sockets
配置項來啟用,可使用 php -m|grep sockets
命令檢查啟用情況,具體編譯過程可參考 這篇文章
快速體驗
服務端與客戶端簡略程式碼如下,執行後服務端會阻塞等待客戶端連線,客戶端會在控制檯要求輸入內容,輸入後資訊會在服務端列印,同時客戶端顯示轉為大寫的內容,此示例服務端與客戶端執行在一臺伺服器:
服務端監聽
<?php
// 建立套接字
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 設定 ip 被釋放後立即可使用
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, true);
// 繫結ip與埠
socket_bind($socket, 0, 8888);
// 開始監聽
socket_listen($socket);
while (true) {
// 接收內容
$conn_sock = socket_accept($socket);
socket_getpeername($conn_sock, $ip, $port);
// echo '請求ip: ' . $ip . PHP_EOL . '埠: ' . $port;
while (true) {
// 獲取訊息內容
$msg = socket_read($conn_sock, 10240);
// TODO 處理業務邏輯
// 將資訊轉為大寫並原樣返回客戶端
socket_write($conn_sock, strtoupper($msg));
echo $msg;
}
}
客戶端連線
<?php
// 建立套接字
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
// 連線服務端
socket_connect($socket, '127.0.0.1', 8888);
while (true) {
// 讓控制檯輸入內容
fwrite(STDOUT, '請輸入內容:');
$in = fgets(STDIN);
// 向服務端傳送內容
socket_write($socket, $in);
// 讀取服務端傳送的訊息
$msg = socket_read($socket, 10240);
echo $msg;
}
語法解釋
socket_create
socket_create(int $domain,int $type, int $protocol): resource|false
建立並返回一個套接字資源,通常也稱作一個通訊節點。一個典型的 socket 由至少 2 個套接字組成,其中一個執行在客戶端,一個執行在服務端。
引數:
domain
指定當前套接字使用什麼協議,可用協議如下:Domain 描述 AF_INET IPv4 網路協議,TCP 與 UDP 都可使用此協議 AF_INET6 IPv6 網路協議,TCP 與 UDP 都可使用此協議 AF_UNIX 本地通訊協議,具有高效能與低成本的 IPC type
使用者指定當前套接字使用的型別type 描述 SOCK_STREAM 可順序化的、可靠的、全雙工的、基於連結的位元組流,支援資料傳送流量控制機制。TCP 協議基於這種流式套接字。 SOCK_DGRAM 資料包文的支援(無連線、不可靠、固定最大長度)UDP 協議基於這種報文套接字 SOCK_SEQPACKET 可順序化的、可靠的、全雙工的、面向連線的、固定最大長度的資料通訊,資料端通過接收每一個資料段來讀取整個資料包 SOCK_RAW 讀取原始的網路協議,這種特殊的套接字可用於手工構建任意型別的協議,一般使用這個套接字來實現 ICMP 請求 SOCK_RDM 可靠的資料層,但不保證到達順序,一般的作業系統都未實現此功能 protocol
設定指定 domain 套接字下的具體協議,如果所需協議是 TCP 或者 UDP,可以直接使用常量SOL_TCP
或SOL_UDP
,這個引數的具體值可通過getprotobyname()
函式獲取
返回值
socket_create()
正確時返回一個套接字資源,失敗時返回 false
。可以呼叫 socket_last_error()
獲取錯誤碼,錯誤碼可以通過 socket_strerror(int $err_no)
轉換為文字的錯誤說明。
socket_bind
socket_bind(resource $socket, string $address [, int $port]): bool
繫結一個地址與埠到套接字
引數:
socket
使用socket_create()
建立的套接字資源address
如果套接字是
AF_INET
族,那麼address
必須是一個四點法的 IP 地址,例如127.0.0.1
、0.0.0.0
如果套接字是
AF_UNIX
族,那麼address
是 Unix 套接字一部分(例如/tmp/my.sock
)port
(可選)該引數僅用於使用
AF_INET
族時,指定當前套接字監聽的埠號
返回值:
繫結成功返回 true
,失敗時則返回 false
,同 socket_create
,在繫結失敗時可以呼叫 socket_last_error()
獲取錯誤碼,錯誤碼可以通過 socket_strerror(int $err_no)
轉換為文字的錯誤說明。
socket_listen
socket_listen(resource $socket [, int $backlog]): bool
在使用 socket_create()
建立套接字並使用 socket_bind()
將其繫結到名稱之後,可能會告訴它偵聽套接字上的傳入連線。該函式僅適用於 SOCK_STREAM
或 SOCK_SEQPACKET
型別的套接字。
引數:
socket
使用socket_create()
建立的套接字資源backlog
最大數量的積壓傳入連線將排隊等待處理,如果連線請求到達時佇列已滿,則客戶端可能會收到指示為ECONNREFUSED
的錯誤。或者,如果底層協議支援重傳,則可能會忽略該請求,以便重試可能會成功。
返回值:
繫結成功返回 true
,失敗時則返回 false
,可以呼叫 socket_last_error()
獲取錯誤碼,錯誤碼可以通過 socket_strerror(int $err_no)
轉換為文字的錯誤說明。
socket_accept
socket_accept(resource $socket): resource|false
當有新的客戶端連線時,返回一個新的 socket 資源以用於與客戶端通訊,如有多個連線排隊,則返回第一個連線,相反如果沒有待處理的連線,該函式會預設阻塞當前程式,直至新的客戶端連線、斷開
引數:
socket
使用socket_create()
建立的套接字資源
返回值:
成功時返回一個新的套接字資源,錯誤時返回 false
,可以呼叫 socket_last_error()
獲取錯誤碼,錯誤碼可以通過 socket_strerror(int $err_no)
轉換為文字的錯誤說明。
socket_connect
socket_connect(resource $socket, string $address [, int $port = null]): bool
使用套接字例項發起到 address
的連線
引數:
socket
該引數必須是由socket_create()
建立的socket
例項address
如果套接字是
AF_INET
族,那麼address
必須是一個四點法的 IP 地址,例如127.0.0.1
如果支援 IPv6 並且套接字是AF_INET6
,那麼address
也可以是一個有效的 IPv6 地址(例如::1
)如果套接字是
AF_UNIX
族,那麼address
是 Unix 套接字一部分(例如/tmp/my.sock
)
返回值:
成功時返回 true
, 或者在失敗時返回 false
socket_write
socket_write(resource $socket, string $data [, int $length = null]): int|false
傳輸資料至指定套接字
引數:
socket
使用socket_create()
或socket_accept()
建立的套接字資源data
要傳送的內容length
(可選)可以指定傳送套接字的替代位元組長度。如果這個長度大於實際傳送內容的長度,它將被靜默地截斷為實際傳送內容的長度。
返回值:
成功時返回成功傳送的位元組數,或者在失敗時返回 false
,可以呼叫 socket_last_error()
與 socket_strerror(int $err_no)
獲取具體錯誤資訊
socket_read
socket_read(resource $socket, int $length, int $mode = PHP_BINARY_READ): string|false
從套接字資源內讀取資料
引數:
socket
使用socket_create()
或socket_accept()
建立的套接字資源(服務端為socket_accept()
客戶端為socket_create()
)length
指定最大能夠讀取的位元組數。否則您可以使用\r
、\n
、\0
結束讀取(根據mode
引數設定)mode
(可選)PHP_BINARY_READ
(預設)- 使用系統的recv()
函式。二進位制安全地讀取資料。PHP_NORMAL_READ
- 讀取到\n
、\r
時停止。
返回值:
socket_read()
返回一個字串,表示接收到的資料。如果發生了錯誤(包括遠端主機關閉了連線),則返回 false
,可以呼叫 socket_last_error()
與 socket_strerror(int $err_no)
獲取具體錯誤資訊
socket_close
socket_close(resource $socket): void
關閉並銷燬一個套接字資源
引數:
socket
使用socket_create()
或socket_accept()
建立的套接字資源
返回值:
無
本作品採用《CC 協議》,轉載必須註明作者和本文連結