Socket 函数
在线手册:中文  英文

socket_create

(PHP 4 >= 4.1.0, PHP 5)

socket_create创建一个套接字(通讯节点)

说明

resource socket_create ( int $domain , int $type , int $protocol )

创建并返回一个套接字,也称作一个通讯节点。一个典型的网络连接由 2 个套接字构成,一个运行在客户端,另一个运行在服务器端。

参数

domain

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 请求(例如 ping)。
SOCK_RDM 提供一个可靠的数据层,但不保证到达顺序。一般的操作系统都未实现此功能。
protocol

protocol 参数,是设置指定 domain 套接字下的具体协议。这个值可以使用 getprotobyname() 函数进行读取。如果所需的协议是 TCP 或 UDP,可以直接使用常量 SOL_TCPSOL_UDP

常见协议
名称 描述
icmp Internet Control Message Protocol 主要用于网关和主机报告错误的数据通信。例如“ping”命令(在目前大部分的操作系统中)就是使用 ICMP 协议实现的。
udp User Datagram Protocol 是一个无连接的、不可靠的、具有固定最大长度的报文协议。由于这些特性,UDP 协议拥有最小的协议开销。
tcp Transmission Control Protocol 是一个可靠的、基于连接的、面向数据流的全双工协议。TCP 能够保障所有的数据包是按照其发送顺序而接收的。如果任意数据包在通讯时丢失,TCP 将自动重发数据包直到目标主机应答已接收。因为可靠性和性能的原因,TCP 在数据传输层使用 8bit 字节边界。因此,TCP 应用程序必须允许传送部分报文的可能。

返回值

socket_create() 正确时返回一个套接字,失败时返回 FALSE。要读取错误代码,可以调用 socket_last_error()。这个错误代码可以通过 socket_strerror() 读取文字的错误说明。

更新日志

版本 说明
5.0.0 增加 AF_INET6 支持。

错误/异常

如果使用一个无效的 domaintypesocket_create() 会使用 AF_INETSOCK_STREAM 替代无效参数,同时会发出 E_WARNING 警告信息。

参见


Socket 函数
在线手册:中文  英文

用户评论:

ab1965 at yandex dot ru (2012-05-04 12:43:48)

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_UNIXSOCK_DGRAM0);
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$buf655360$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$len0$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_UNIXSOCK_DGRAM0);
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$len0$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$buf655360$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";
?>

geoff at spacevs dot com (2010-11-20 01:51:33)

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_INETSOCK_RAW1);
                
socket_set_option($socketSOL_SOCKETSO_RCVTIMEO, array('sec' => $timeout'usec' => 0));
                
socket_connect($socket$hostnull);

                
$ts microtime(true);
                
socket_send($socket$packagestrLen($package), 0);
                if (
socket_read($socket255))
                        
$result microtime(true) - $ts;
                else    
$result false;
                
socket_close($socket);

                return 
$result;
        }
?>

rhollencamp at gmail dot com (2009-09-08 21:29:42)

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.

took at sd-gp dot de (2009-04-28 16:05:34)

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($frame1500);

/**
 * 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_INETSOCK_DGRAMSOL_UDP);
    
socket_sendto($socket$bufstrlen($buf), 0$host$port);
    
socket_close($socket);
    
usleep($delay*1000);
}
?>

skelly at keenio (2008-06-16 10:56:05)

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/

shaun at verticalevolution dot com (2008-05-01 12:03:34)

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.

alexander dot krause at ed-solutions dot de (2008-03-20 00:57:12)

On UNIX systems php needs /etc/protocols for constants like SOL_UDP and SOL_TCP.
This file was missing on my embedded platform.

Jean Charles MAMMANA (2008-01-31 04:54:16)

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(0255), rand(0255));

     
$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_INETSOCK_RAW,  getprotobyname('icmp'));
        
$time_start microtime();
    
socket_sendto($sock$packetstrlen($packet), 0$host$port);
    

    
$read   = array($sock);
        
$write  NULL;
        
$except NULL;

        
$select socket_select($read$write$except0$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$recv655350$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 ///
?>

Kyle Wilson (2008-01-22 17:02:18)

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')));
?>

matth ingersoll (2007-05-03 22:45:01)

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_INETSOCK_RAW1))
        {
                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_INETSOCK_RAW1))
        {
                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($socket08000))
                        {
                                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");
}
?>

ionic at ionic dot de (2007-01-21 03:39:18)

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. :)

jens at surefoot dot com (2006-08-08 05:46:19)

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.

(2006-01-06 14:36:13)

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_INETSOCK_RAW1);
    
    
/* set socket receive timeout to 1 second */
    
socket_set_option($socketSOL_SOCKETSO_RCVTIMEO, array("sec" => 1"usec" => 0));
    
    
/* connect to socket */
    
socket_connect($socket$hostnull);
    
    
/* record start time */
    
list($start_usec$start_sec) = explode(" "microtime());
    
$start_time = ((float) $start_usec + (float) $start_sec);
    
    
socket_send($socket$packagestrlen($package), 0);
    
    if(@
socket_read($socket255)) {
        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);
}

?>

kyle gibson (2005-10-28 17:48:10)

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_UNIXSOCK_STREAM0);
// code
?>

david at eder dot us (2005-01-25 11:42:29)

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_INETSOCK_DGRAMSOL_UDP);
    
socket_connect($socket$dest$port);
    
socket_getsockname($socket$addr$port);
    
socket_close($socket);
    return 
$addr;
  }
?>

david at eder dot us (2004-06-08 08:07:52)

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_INETSOCK_DGRAMSOL_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$packetstrlen($packet), 0x100$host69);

    
$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$buffer5160$host$port);

      
// add the block number from the data packet to the ack packet
      
$packet chr(0) . chr(4) . substr($buffer22);
      
// send ack
      
socket_sendto($socket$packetstrlen($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($buffer4);
    }
    while(
strlen($buffer) == 516);  // the first non-full packet is the last.
    
return $ret;
  }
?>

evan at coeus hyphen group dot com (2002-02-14 18:33:44)

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.)

email at NOSPAMrichard-samar dot de (2002-02-13 13:10:21)

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

易百教程