socket_create
(PHP 4 >= 4.1.0, PHP 5, PHP 7, PHP 8)
socket_create — ソケット(通信時の終端)を作成する
説明
通信のエンドポイント(終端)と呼ばれることもある 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
このタイプでは、時系列的な、信頼性のある、 双方向の接続指向型の固定長データグラム転送が利用できます。 パケットを消費する側は、一つのパケット全部を一度の read コールで読み込む必要があります。 SOCK_RAW
このタイプでは、素のネットワークプロトコルを操作できます。 この特殊なソケットを使って、どのタイプのプロトコルでもユーザーの手で構築することができます。 よくある使い方として、(ping のような) ICMP リクエストの作成があります。 SOCK_RDM
このタイプでは、信頼に足る、非時系列的なデータグラム転送が利用できます。 ほとんどのオペレーティングシステムでは実装されていないでしょう。 protocol
-
protocol
は、ソケット上の通信で使われるdomain
で指定されたファミリーに属するプロトコルを指定します。 正しい値は、getprotobyname() を使うことで取得できます。利用したいプロトコルが、TCP または UDP の場合は、定数SOL_TCP
とSOL_UDP
を指定することもできます。一般的なプロトコルの一覧 名称 説明 icmp Internet Control Message Protocol は、主にゲートウェイやホストが、 データグラム通信におけるエラーを報告するのに使われます。 "ping" コマンド (最近のほとんどのオペーレーティングシステムに 搭載されています) が ICMP アプリケーションの一例です。 udp User Datagram Protocol は、非接続指向の、信頼性の高くない、 固定のレコード長を用いるプロトコルです。このような側面のおかげで、 UDP はプロトコルとして最小限のオーバーへッドしか要求しません。 tcp Transmission Control Protocol は、信頼性の高い、接続指向かつ ストリーム指向の全二重通信プロトコルです。TCP は、 すべてのパケットが、送信された順序で(時系列的に)受信されることを 保証します。もし、何らかの理由でパケットが通信中に失われた場合、 TCP では、送信先から通知があるまで、パケットが再送信されるように なっています。信頼性とパフォーマンス上の理由から、TCP の実装は、 下層にあるデータグラム通信レイヤーのオクテット幅を 適当な長さに決定します。このため、TCP アプリケーションは、 レコードの全部が一度に転送されない場合も考慮しなければなりません。
戻り値
socket_create() は、
成功時に Socket クラスのインスタンス、
失敗時に false
を返します。
実際のエラーコードは、socket_last_error() を
コールすることにより取得できます。このエラーコードをさらに
socket_strerror()に渡すことにより、
エラーの内容を文字列で取得することが可能です。
エラー / 例外
domain
や type
に
不正な値が与えられた場合、socket_create() は、これらを
それぞれ AF_INET
と SOCK_STREAM
であるとみなし、E_WARNING
メッセージを出します。
変更履歴
バージョン | 説明 |
---|---|
8.0.0 | 成功した場合に、この関数は Socket クラスのインスタンスを返すようになりました。 これより前のバージョンでは、リソースを返していました。 |
参考
- socket_accept() - ソケットへの接続を許可する
- socket_bind() - ソケットに名前をバインドする
- socket_connect() - ソケット上の接続を初期化する
- socket_listen() - ソケット上で接続待ち(listen)する
- socket_last_error() - ソケットの直近のエラーを返す
- socket_strerror() - ソケットエラーの内容を文字列として返す
User Contributed Notes 12 notes
Took me about 20 minutes to figure out the proper arguments to supply for a AF_UNIX socket. Anything else, and I would get a PHP warning about the 'type' not being supported. I hope this saves someone else time.
<?php
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
// code
?>
Note that if you create a socket with AF_UNIX, a file will be created in the filesystem. This file is not removed when you call socket_close - you should unlink the file after you close the socket.
It took some time to understand how one PHP process can communicate with another by means of unix udp sockets. Examples of 'server' and 'client' code are given below. Server is assumed to run before client starts.
'Server' code
<?php
if (!extension_loaded('sockets')) {
die('The sockets extension is not loaded.');
}
// create unix udp socket
$socket = socket_create(AF_UNIX, SOCK_DGRAM, 0);
if (!$socket)
die('Unable to create AF_UNIX socket');
// same socket will be used in recv_from and send_to
$server_side_sock = dirname(__FILE__)."/server.sock";
if (!socket_bind($socket, $server_side_sock))
die("Unable to bind to $server_side_sock");
while(1) // server never exits
{
// receive query
if (!socket_set_block($socket))
die('Unable to set blocking mode for socket');
$buf = '';
$from = '';
echo "Ready to receive...\n";
// will block to wait client query
$bytes_received = socket_recvfrom($socket, $buf, 65536, 0, $from);
if ($bytes_received == -1)
die('An error occured while receiving from the socket');
echo "Received $buf from $from\n";
$buf .= "->Response"; // process client query here
// send response
if (!socket_set_nonblock($socket))
die('Unable to set nonblocking mode for socket');
// client side socket filename is known from client request: $from
$len = strlen($buf);
$bytes_sent = socket_sendto($socket, $buf, $len, 0, $from);
if ($bytes_sent == -1)
die('An error occured while sending to the socket');
else if ($bytes_sent != $len)
die($bytes_sent . ' bytes have been sent instead of the ' . $len . ' bytes expected');
echo "Request processed\n";
}
?>
'Client' code
<?php
if (!extension_loaded('sockets')) {
die('The sockets extension is not loaded.');
}
// create unix udp socket
$socket = socket_create(AF_UNIX, SOCK_DGRAM, 0);
if (!$socket)
die('Unable to create AF_UNIX socket');
// same socket will be later used in recv_from
// no binding is required if you wish only send and never receive
$client_side_sock = dirname(__FILE__)."/client.sock";
if (!socket_bind($socket, $client_side_sock))
die("Unable to bind to $client_side_sock");
// use socket to send data
if (!socket_set_nonblock($socket))
die('Unable to set nonblocking mode for socket');
// server side socket filename is known apriori
$server_side_sock = dirname(__FILE__)."/server.sock";
$msg = "Message";
$len = strlen($msg);
// at this point 'server' process must be running and bound to receive from serv.sock
$bytes_sent = socket_sendto($socket, $msg, $len, 0, $server_side_sock);
if ($bytes_sent == -1)
die('An error occured while sending to the socket');
else if ($bytes_sent != $len)
die($bytes_sent . ' bytes have been sent instead of the ' . $len . ' bytes expected');
// use socket to receive data
if (!socket_set_block($socket))
die('Unable to set blocking mode for socket');
$buf = '';
$from = '';
// will block to wait server response
$bytes_received = socket_recvfrom($socket, $buf, 65536, 0, $from);
if ($bytes_received == -1)
die('An error occured while receiving from the socket');
echo "Received $buf from $from\n";
// close socket and delete own .sock file
socket_close($socket);
unlink($client_side_sock);
echo "Client exits\n";
?>
Here is a ping function for PHP without using exec/system/passthrough/etc... Very useful to use to just test if a host is online before attempting to connect to it. Timeout is in seconds.
<?PHP
function ping($host, $timeout = 1) {
/* ICMP ping packet with a pre-calculated checksum */
$package = "\x08\x00\x7d\x4b\x00\x00\x00\x00PingHost";
$socket = socket_create(AF_INET, SOCK_RAW, 1);
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $timeout, 'usec' => 0));
socket_connect($socket, $host, null);
$ts = microtime(true);
socket_send($socket, $package, strLen($package), 0);
if (socket_read($socket, 255))
$result = microtime(true) - $ts;
else $result = false;
socket_close($socket);
return $result;
}
?>
I've written the ping() function using socket_create() with SOCK_RAW.
(on Unix System, you need to have the root acces to execute this function)
<?php
/// start ping.inc.php ///
$g_icmp_error = "No Error";
// timeout in ms
function ping($host, $timeout)
{
$port = 0;
$datasize = 64;
global $g_icmp_error;
$g_icmp_error = "No Error";
$ident = array(ord('J'), ord('C'));
$seq = array(rand(0, 255), rand(0, 255));
$packet = '';
$packet .= chr(8); // type = 8 : request
$packet .= chr(0); // code = 0
$packet .= chr(0); // checksum init
$packet .= chr(0); // checksum init
$packet .= chr($ident[0]); // identifier
$packet .= chr($ident[1]); // identifier
$packet .= chr($seq[0]); // seq
$packet .= chr($seq[1]); // seq
for ($i = 0; $i < $datasize; $i++)
$packet .= chr(0);
$chk = icmpChecksum($packet);
$packet[2] = $chk[0]; // checksum init
$packet[3] = $chk[1]; // checksum init
$sock = socket_create(AF_INET, SOCK_RAW, getprotobyname('icmp'));
$time_start = microtime();
socket_sendto($sock, $packet, strlen($packet), 0, $host, $port);
$read = array($sock);
$write = NULL;
$except = NULL;
$select = socket_select($read, $write, $except, 0, $timeout * 1000);
if ($select === NULL)
{
$g_icmp_error = "Select Error";
socket_close($sock);
return -1;
}
elseif ($select === 0)
{
$g_icmp_error = "Timeout";
socket_close($sock);
return -1;
}
$recv = '';
$time_stop = microtime();
socket_recvfrom($sock, $recv, 65535, 0, $host, $port);
$recv = unpack('C*', $recv);
if ($recv[10] !== 1) // ICMP proto = 1
{
$g_icmp_error = "Not ICMP packet";
socket_close($sock);
return -1;
}
if ($recv[21] !== 0) // ICMP response = 0
{
$g_icmp_error = "Not ICMP response";
socket_close($sock);
return -1;
}
if ($ident[0] !== $recv[25] || $ident[1] !== $recv[26])
{
$g_icmp_error = "Bad identification number";
socket_close($sock);
return -1;
}
if ($seq[0] !== $recv[27] || $seq[1] !== $recv[28])
{
$g_icmp_error = "Bad sequence number";
socket_close($sock);
return -1;
}
$ms = ($time_stop - $time_start) * 1000;
if ($ms < 0)
{
$g_icmp_error = "Response too long";
$ms = -1;
}
socket_close($sock);
return $ms;
}
function icmpChecksum($data)
{
$bit = unpack('n*', $data);
$sum = array_sum($bit);
if (strlen($data) % 2) {
$temp = unpack('C*', $data[strlen($data) - 1]);
$sum += $temp[1];
}
$sum = ($sum >> 16) + ($sum & 0xffff);
$sum += ($sum >> 16);
return pack('n*', ~$sum);
}
function getLastIcmpError()
{
global $g_icmp_error;
return $g_icmp_error;
}
/// end ping.inc.php ///
?>
On UNIX systems php needs /etc/protocols for constants like SOL_UDP and SOL_TCP.
This file was missing on my embedded platform.
Please be aware that RAW sockets (as used for the ping example) are restricted to root accounts on *nix systems. Since web servers hardly ever run as root, they won't work on webpages.
On Windows based servers it should work regardless.
Okay I talked with Richard a little (via e-mail). We agree that getprotobyname() and using the constants should be the same in functionality and speed, the use of one or the other is merely coding style. Personally, we both think the constants are prettier :).
The eight different protocols are the ones implemented in PHP- not the total number in existance (RFC 1340 has 98).
All we disagree on is using 0- Richard says that "accordning to the official unix/bsd sockets 0 is more than fine." I think that since 0 is a reserved number according to RFC 1320, and when used usually refers to IP, not one of it's sub-protocols (TCP, UDP, etc.)