2024 年 PHP 日本研討會

socket_connect

(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8)

socket_connect在通訊端上起始連線

說明

socket_connect(Socket $socket, 字串 $address, ?整數 $port = null): 布林值

使用 Socket 實例 socket 初始化與 address 的連線,該實例必須是由 socket_create() 建立的 Socket 實例。

參數

socket

socket_create() 建立的 Socket 實例。

address

address 參數可以是點分十進位表示法的 IPv4 地址(例如 127.0.0.1),如果 socketAF_INET;如果啟用了 IPv6 支援且 socketAF_INET6,則為有效的 IPv6 地址(例如 ::1);或者,如果 socket 家族是 AF_UNIX,則為 Unix 域 socket 的路徑名稱。

port

port 參數僅在連接到 AF_INETAF_INET6 socket 時使用,並且是必需的,它指定遠端主機上要建立連線的埠。

返回值

成功時返回 true,失敗時返回 false。錯誤碼可以使用 socket_last_error() 擷取。此程式碼可以傳遞給 socket_strerror() 以取得錯誤的文字說明。

注意:

如果 socket 是非阻塞的,則此函數返回 false 並顯示錯誤 Operation now in progress(操作正在進行中)。

更新日誌

版本 說明
8.0.0 socket 現在是一個 Socket 實例;以前,它是一個 資源
8.0.0 port 現在可以為 null。

參見

新增註釋

使用者貢獻的註釋 11 則註釋

w at ff dot st
21 年前
connect 的 man 頁面
EINPROGRESS
此 socket 為非阻塞式,連線無法立即完成。可以透過 select(2) 或 poll(2) 選擇 socket 的寫入狀態來檢查連線是否完成。在 select 指示可寫入後,使用 getsockopt(2) 讀取 SOL_SOCKET 層級的 SO_ERROR 選項,以確定連線是否成功完成 (SO_ERROR 為零) 或失敗 (SO_ERROR 為其中一個常見的錯誤碼,說明失敗原因)。

使用 socket_getoption($socket,SOL_SOCKET,SO_ERROR)。如果取得的值是 115,表示正在連線中。如果取得的值不是 115 也不是 0,則表示發生錯誤(使用 socket_strerror() 查看錯誤訊息)。

然而,我不知道這在 Windows 下如何運作,也許它根本無法運作。它應該可以在 Linux 下運作(man 頁面有說明)。
tacapi at canela dot com
7 年前
提醒一下各位:確保傳遞給 ping 和 socket 函式的 IP 格式正確。

例如:192.168.0.18 -> OK
192.168.0.018 -> 會導致「未知的主機」錯誤

我之前一直收到 11004 錯誤,直到我發現問題所在才解決。

(或許對某些人有用:請確認傳遞給 ping 和 socket 函式的 IP 參數是格式正確的 IP 位址)
greg at mtechsolutions dot ca
21 年前
如果您使用非阻塞模式,請確保在連線後才啟用它,否則您會收到以下訊息:

PHP 警告:socket_connect() 無法連線 [115]:操作正在進行中,位於 file.php 的第 123 行

而且 socket_connect() 會返回 false(即使它會連線)。
jerrywilborn at gmail dot com
15 年前
這將會印出真正的「telnet」伺服器(路由器、交換器、主機等)的 banner 資訊。

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '127.0.0.1', 23);

while (TRUE) {
$r = array($socket);
$c = socket_select($r, $w = NULL, $e = NULL, 5);

foreach ($r as $read_socket) {
if ($r = negotiate($read_socket)) {
var_dump($r);
exit;
}
}
}

function negotiate ($socket) {
socket_recv($socket, $buffer, 1024, 0);

for ($chr = 0; $chr < strlen($buffer); $chr++) {
if ($buffer[$chr] == chr(255)) {

$send = (isset($send) ? $send . $buffer[$chr] : $buffer[$chr]);

$chr++;
if (in_array($buffer[$chr], array(chr(251), chr(252)))) $send .= chr(254);
if (in_array($buffer[$chr], array(chr(253), chr(254)))) $send .= chr(252);

$chr++;
$send .= $buffer[$chr];
} else {
break;
}
}

if (isset($send)) socket_send($socket, $send, strlen($send), 0);
if ($chr - 1 < strlen($buffer)) return substr($buffer, $chr);

}
Cedar Myers
13 年前
似乎可以透過在呼叫 socket_connect() 之前設定 SO_SNDTIMEO 選項來指定逾時值。

<?php
socket_set_option
($socket, SOL_SOCKET, SO_SNDTIMEO, array('sec' => $seconds, 'usec' => $milliseconds));
socket_connect($socket, $address, $port)//...
?>
maganap
16 年前
嗨!

對於 TCP 連線:socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
如果您在 socket_connect() 中遇到 socket_strerror() = "Permission denied" 的問題,您可能遇到了 SELinux 設定問題。

檢查 SELinux 是否已啟用
# /usr/sbin/sestatus -v
如果是,您可以輸入以下指令
# setsebool httpd_can_network_connect=1

就這樣... 我讀到您必須重新開機,但我沒有這樣做,它仍然可以正常運作。更多資訊,您可以查看
http://arkiv.netbsd.se/?ml=squirrelmail-users&a=2005-11&t=1523021
vshih at yahoo
15 年前
rbarnes 的提示很有幫助,但我發現我需要在 while 迴圈中加入 SOCKET_EISCONN 的檢查

...
$error = socket_last_error();

if ($error == SOCKET_EISCONN) {
$connected = true;
break;
}
...

至少在 Mac OS X 10.5 上是如此。
rbarnes at fake dot com
16 年前
以下是一個非阻塞連線的範例,它應該比 Seymour 下面發佈的範例執行速度快得多

<?php
function msConnectSocket($remote, $port, $timeout = 30) {
# this works whether $remote is a hostname or IP
$ip = "";
if( !
preg_match('/^\d+\.\d+\.\d+\.\d+$/', $remote) ) {
$ip = gethostbyname($remote);
if (
$ip == $remote) {
$this->errstr = "Error Connecting Socket: Unknown host";
return
NULL;
}
} else
$ip = $remote;

if (!(
$this->_SOCK = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) {
$this->errstr = "Error Creating Socket: ".socket_strerror(socket_last_error());
return
NULL;
}

socket_set_nonblock($this->_SOCK);

$error = NULL;
$attempts = 0;
$timeout *= 1000; // adjust because we sleeping in 1 millisecond increments
$connected;
while (!(
$connected = @socket_connect($this->_SOCK, $remote, $port+0)) && $attempts++ < $timeout) {
$error = socket_last_error();
if (
$error != SOCKET_EINPROGRESS && $error != SOCKET_EALREADY) {
$this->errstr = "Error Connecting Socket: ".socket_strerror($error);
socket_close($this->_SOCK);
return
NULL;
}
usleep(1000);
}

if (!
$connected) {
$this->errstr = "Error Connecting Socket: Connect Timed Out After $timeout seconds. ".socket_strerror(socket_last_error());
socket_close($this->_SOCK);
return
NULL;
}

socket_set_block($this->_SOCK);

return
1;
}
?>
peter at videoripper dot org
15 年前
這會提供一個簡單的連接埠檢查器。

請注意,在正式環境的機器上,您可能需要更改錯誤報告級別,
因為不成功的連線會在日誌中產生「由於目標電腦主動拒絕連線,因此無法建立連線」的錯誤。


在 Windows 下,請確保您在 php.ini 中啟用了 php_sockets.dll 擴充功能。

<?php
$address
=$_SERVER['REMOTE_ADDR'];

if (isset(
$_REQUEST['port']) and
(!
strlen($_REQUEST['port'])==0))
$port=$_REQUEST['port'];
else
unset(
$port);

if (isset(
$port) and
(
$socket=socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) and
(
socket_connect($socket, $address, $port)))
{
$text="連線至 IP $address,連接埠 $port 成功";
socket_close($socket);
}
else
$text="無法連線<pre>".socket_strerror(socket_last_error())."</pre>";

echo
"<html><head></head><body>".
$text.
"</body></html>";
?>

問候,

Peter.
thewanderer
13 年前
請注意,從 PHP 5.3 開始,無法將 IPv6 多播傳送到鏈路本地地址,因為 socket_connect() 只是 connect() 的精簡版本,並不支援傳遞 sin6_scope_id 參數 — 例如,在將封包傳送到 ff02::1 (所有節點) 時,需要範圍 ID。
一開始我以為我需要使用 SO_BINDTODEVICE 選項 (PHP 中未定義的常數 - 使用數值 25) 將通訊端繫結到裝置,但這沒有任何區別,只是需要 root 權限卻沒有產生任何可用的結果。
此外,如果您認為只是因為 socket_sendto() 傳回正的位元組數,就表示您正在將多播封包傳送到鏈路本地地址,那您可能錯了 — 僅僅傳回成功並不表示封包有透過任何鏈路傳送。在我的測試案例中,我傳送封包到 ff02::1,沒有偵測到任何錯誤,但 Wireshark 卻沒有顯示任何封包。它們最終消失無蹤。
這與本地接收通訊端的處理無關,因此 UDP 監聽器應該仍然可以照常使用 IPv6/UDP。不過,您可能需要使用 C 語言來實作多播發送器。
seymour@itsyourdomain
21 年前
以下是使用 socket 函數實作逾時的方法。

此範例適用於阻塞式通訊端,但稍作修改後即可適用於阻塞式和非阻塞式通訊端。在非阻塞模式下,第一次呼叫 connect 會傳回 115 EINPROGRESS,如果連線尚未失敗或成功,後續呼叫則會傳回 114 EALREADY。一旦連線成功,就會傳回通訊端資源。

<?
$host = "127.0.0.1";
$port = "80";
$timeout = 15; //逾時時間,單位為秒

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)
or die("無法建立通訊端\n");

socket_set_nonblock($socket)
or die("無法將通訊端設定為非阻塞模式\n");

$time = time();
while (!@socket_connect($socket, $host, $port))
{
$err = socket_last_error($socket);
if ($err == 115 || $err == 114)
{
if ((time() - $time) >= $timeout)
{
socket_close($socket);
die("連線逾時\n");
}
sleep(1);
continue;
}
die(socket_strerror($err) . "\n");
}

socket_set_block($this->socket)
or die("無法將通訊端設定為阻塞模式\n");
?>
To Top