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.
socket_create
(PHP 4 >= 4.1.0, PHP 5)
socket_create — ソケット(通信時の終端)を作成する
説明
通信のエンドポイント(終端)と呼ばれることもあるソケットのリソースを作成し、 返します。典型的なネットワーク接続は、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 や traceroute などで行われているような) 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() は、 成功時にソケットリソース、失敗時に FALSE を返します。 実際のエラーコードは、socket_last_error() を コールすることにより取得できます。このエラーコードをさらに socket_strerror()に渡すことにより、 エラーの内容を文字列で取得することが可能です。
変更履歴
| バージョン | 説明 |
|---|---|
| 5.0.0 | 定数 AF_INET6 が追加されました。 |
エラー / 例外
domain や type に 不正な値が与えられた場合、socket_create() は、これらを それぞれ AF_INET と SOCK_STREAM であるとみなし、E_WARNING メッセージを出します。
参考
- socket_accept() - ソケットへの接続を許可する
- socket_bind() - ソケットに名前をバインドする
- socket_connect() - ソケット上の接続を初期化する
- socket_listen() - ソケット上で接続待ち(listen)する
- socket_last_error() - ソケットの直近のエラーを返す
- socket_strerror() - ソケットエラーの内容を文字列として返す
socket_create
09-Sep-2009 01:29
29-Apr-2009 08:05
A simple example how to send a raw udp packet
<?php
$frame = array(
array(1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1),
array(1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1),
array(1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1),
array(1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1),
array(1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1),
array(1,1,0,1,1,0,1,1,0,1,1,0,1,1,0,1,1,1),
array(1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1),
array(1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1)
);
send_frame($frame, 1500);
/**
* Sends 18x8 MCUF-UDP packet to target host.
*
* see also:
* wiki.blinkenarea.org/index.php/MicroControllerUnitFrame
*
* @param array $frame 18x8 '0' or '1'
* @param int $delay delay in msec
* @param string $host target host
* @param int $port target port (udp)
*/
function send_frame($frame, $delay, $host="192.168.0.23", $port=2323) {
$header = "\x23\x54\x26\x66\x00\x08\x00\x12\x00\x01\x00\xff";
$buf = $header;
for ($i=0;$i<8;$i++) {
for ($j=0;$j<18;$j++) {
if ($frame[$i][$j]) {
$buf.="\xff";
} else {
$buf.="\x00";
}
}
}
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_sendto($socket, $buf, strlen($buf), 0, $host, $port);
socket_close($socket);
usleep($delay*1000);
}
?>
17-Jun-2008 02:56
It took some investigation to understand the bit about raw sockets requiring root access and raw sockets being required to perform a seemingly simple "ping" on a Linux server. My source of confusion was that the regular command line "ping" utility did not require me to be root to exec it, so what was the difference? I found the answer starting with a forum posting to look up `man 7 raw` for raw sockets info which reveals:
"Only processes with an effective user ID of 0 or the CAP_NET_RAW capability are allowed to open raw sockets."
So root is not the only way; the ping utility normally works because it has suid permission. There is some interesting source code however that allows you to selectively assign the CAP_NET_RAW capability without the need for suid. Use at your own risk, this is provided for informational purposes only:
http://www.olafdietsche.de/linux/capability/
02-May-2008 04:03
After weeks of trying to find a PHP network client that supports TFTP and Telnet I finally break down and wrote a client library. You can find it here: http://www.verticalevolution.com/blog/index.php?/pages/PHP-Client.html
This isn't like other telnet libraries that just exec the OS's telnet, it makes and negotiates its own options. It creates the TFTP packets to send data back and forth.
20-Mar-2008 05:57
On UNIX systems php needs /etc/protocols for constants like SOL_UDP and SOL_TCP.
This file was missing on my embedded platform.
31-Jan-2008 09:54
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 ///
?>
23-Jan-2008 10:02
to ionic and david...
I'm just taking a stab at this, but wouldn't some combination of gethostbynamel and php_uname('n') provide you with the world-facing IP address without making a system call or using expensive socket operations? Don't get me wrong, socket operations have their place, but if you just need the server IP from a cli script, socket operations seem expensive. :)
<?php
print_r(gethostbynamel(php_uname('n')));
?>
04-May-2007 02:45
When running these types of sockets, typically you need to be root or else they fail (not the case with stream sockets: http://us.php.net/manual/en/function.stream-socket-server.php)
This is an example for Linux/Unix type systems to use sockets without being root. (tested on Debian and CentOS)
<?php
$user = "daemon";
$script_name = "uid"; //the name of this script
/////////////////////////////////////////////
//try creating a socket as a user other than root
echo "\n__________________________________________\n";
echo "Trying to start a socket as user $user\n";
$uid_name = posix_getpwnam($user);
$uid_name = $uid_name['uid'];
if(posix_seteuid($uid_name))
{
echo "SUCCESS: You are now $user!\n";
if($socket = @socket_create(AF_INET, SOCK_RAW, 1))
{
echo "SUCCESS: You are NOT root and created a socket! This should not happen!\n";
} else {
echo "ERROR: socket_create() failed because you're not root!\n";
}
$show_process = shell_exec("ps aux | grep -v grep | grep $script_name");
echo "Current process stats::-->\t $show_process";
} else {
exit("ERROR: seteuid($uid_name) failed!\n");
}
/////////////////////////////////////////////
//no try creating a socket as root
echo "\n__________________________________________\n";
echo "Trying to start a socket as user 'root'\n";
if(posix_seteuid(0))
{
echo "SUCCESS: You are now root!\n";
$show_process = shell_exec("ps aux | grep -v grep | grep $script_name");
echo "Current process stats::-->\t $show_process";
if($socket = @socket_create(AF_INET, SOCK_RAW, 1))
{
echo "SUCCESS: You created a socket as root and now should seteuid() to another user\n";
/////////////////////////////////////////
//now modify the socket as another user
echo "\n__________________________________________\n";
echo "Switching to user $user\n";
if(posix_seteuid($uid_name))
{
echo "SUCCESS: You are now $user!\n";
if(socket_bind($socket, 0, 8000))
{
echo "SUCCESS: socket_bind() worked as $user!\n";
} else {
echo "ERROR: Must be root to user socket_bind()\n";
}
$show_process = shell_exec("ps aux | grep -v grep | grep $script_name");
echo "Current process stats::-->\t $show_process";
socket_close($socket); //hard to error check but it does close as this user
echo "SUCCESS: You closed the socket as user $user!\n";
} else {
echo "ERROR: seteuid($uid_name) failed while socket was open!\n";
}
} else {
echo "ERROR: Socket failed for some reason!\n";
}
} else {
exit("ERROR: Changing to root failed!\n");
}
?>
21-Jan-2007 08:39
To david _at* eder #do; us:
Dependent on your system, you could, at least on LINUX, use exec with "/sbin/ifconfig eth0 | awk '/inet /{ print substr($2,9) }'" (also working for non-root users), that will work a little bit faster as your PHP function.
Though, we should keep in mind that users with safe_mode enabled are more or less forced to use the socket thing. :)
08-Aug-2006 09:46
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.
Here's a ping function that uses sockets instead of exec(). Note: I was unable to get socket_create() to work without running from CLI as root. I've already calculated the package's checksum to simplify the code (the message is 'ping' but it doesn't actually matter).
<?php
function ping($host) {
$package = "\x08\x00\x19\x2f\x00\x00\x00\x00\x70\x69\x6e\x67";
/* create the socket, the last '1' denotes ICMP */
$socket = socket_create(AF_INET, SOCK_RAW, 1);
/* set socket receive timeout to 1 second */
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 1, "usec" => 0));
/* connect to socket */
socket_connect($socket, $host, null);
/* record start time */
list($start_usec, $start_sec) = explode(" ", microtime());
$start_time = ((float) $start_usec + (float) $start_sec);
socket_send($socket, $package, strlen($package), 0);
if(@socket_read($socket, 255)) {
list($end_usec, $end_sec) = explode(" ", microtime());
$end_time = ((float) $end_usec + (float) $end_sec);
$total_time = $end_time - $start_time;
return $total_time;
} else {
return false;
}
socket_close($socket);
}
?>
29-Oct-2005 09:48
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
?>
26-Jan-2005 04:42
Sometimes when you are running CLI, you need to know your own ip address.
<?php
$addr = my_ip();
echo "my ip address is $addr\n";
function my_ip($dest='64.0.0.0', $port=80)
{
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
socket_connect($socket, $dest, $port);
socket_getsockname($socket, $addr, $port);
socket_close($socket);
return $addr;
}
?>
09-Jun-2004 12:07
Seems there aren't any examples of UDP clients out there. This is a tftp client. I hope this makes someone's life easier.
<?php
function tftp_fetch($host, $filename)
{
$socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
// create the request packet
$packet = chr(0) . chr(1) . $filename . chr(0) . 'octet' . chr(0);
// UDP is connectionless, so we just send on it.
socket_sendto($socket, $packet, strlen($packet), 0x100, $host, 69);
$buffer = '';
$port = '';
$ret = '';
do
{
// $buffer and $port both come back with information for the ack
// 516 = 4 bytes for the header + 512 bytes of data
socket_recvfrom($socket, $buffer, 516, 0, $host, $port);
// add the block number from the data packet to the ack packet
$packet = chr(0) . chr(4) . substr($buffer, 2, 2);
// send ack
socket_sendto($socket, $packet, strlen($packet), 0, $host, $port);
// append the data to the return variable
// for large files this function should take a file handle as an arg
$ret .= substr($buffer, 4);
}
while(strlen($buffer) == 516); // the first non-full packet is the last.
return $ret;
}
?>
15-Feb-2002 11:33
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.)
14-Feb-2002 06:10
Actually, you don't need to use
getprotobyname("tcp") but instead can use
the constants: SOL_TCP and SOL_UDP.
Here an extract of the source from
ext/sockets which should make this clear.
if ((pe = getprotobyname("tcp"))) {
REGISTER_LONG_CONSTANT("SOL_TCP", pe->p_proto,
CONST_CS | CONST_PERSISTENT);
}
Normally the third parameter can be set to 0. In the
original BSD Socket implementation the third parameter
(there are 8 different types, here only two) should be
IPPROTO_TCP or IPPROTO_UPD (or one of the 6 others ones).
These two parameters are though not warpped in PHP as
constants and therefore not available.
Please use SOL_TCP and SOL_UDP. e.g.:
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
Please be aware of the fact that UDP
and TCP can only be used with AF_INET which is: "Adress Family Internet". With UNIX Domain sockets TCP/UDP would make no sense!
best regards
-Richard-Moh Samar
