目录
今天在文档里面看到一个组socket_addrinfo新函数,研究了一下,顺便看看怎么用同时记一记。
socket_addrinfo_lookup 和 socket_addrinfo_explain
IP地址查询
如果只是用来地址查询,那么类似的函数有gethostbyname或gethostbynamel,区别是后面2个函数只能获取ipv4地址。
而默认情况下,socket_addrinfo_lookup会获取全部ip地址,包括ipv6和ipv4。(读取地址时需要配合socket_addrinfo_explain函数)
var_dump(gethostbyname('cloudfare.com'));
var_dump(gethostbynamel('cloudfare.com'));
$addrinfo_arr = socket_addrinfo_lookup('cloudfare.com');
if(!empty($addrinfo_arr)){
foreach ($addrinfo_arr as $addrinfo){
$addrinfo_explain = socket_addrinfo_explain($addrinfo);
var_dump($addrinfo_explain);
}
}
只查询IPV6
只需要在第三个参数hints中指定其中的类型。
$addrinfo_arr = socket_addrinfo_lookup('cloudfare.com',null,[
'ai_family' => AF_INET6,
]);
hints可配置键名
socket_addrinfo_lookup(): Argument #3 ($hints) must only contain array keys “ai_flags”, “ai_socktype”, “ai_protocol”, or “ai_family”
其中的 ai_flags 没搞明白,另外的 ai_family,ai_socktype,ai_protocol,可以对应socket_create中的三个参数domain ,type,protocol。如果定义了类型,socket_addrinfo_lookup返回的数组中地址实例内对应键值会与之对应。
ai_family
对于参数 socket_create domain, 可用 AF_INET、AF_INET6、AF_UNIX
ai_socktype
对于参数 socket_create type , 可用 SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET、SOCK_RAW、SOCK_RDM
ai_protocol
对于参数 socket_create protocol, 可用 getprotobyname()获取。
获取指定类型地址实例
以ipv6-icmp为例,注意返回数组中的对应类型
$addrinfo_arr = socket_addrinfo_lookup('cloudfare.com',null,array(
'ai_family' => AF_INET6,
'ai_socktype' => SOCK_RAW,
'ai_protocol' => getprotobyname('ipv6-icmp'),
));
if(!empty($addrinfo_arr)){
foreach ($addrinfo_arr as $addrinfo){
$addrinfo_explain = socket_addrinfo_explain($addrinfo);
var_dump($addrinfo_explain);
}
}
socket_addrinfo_connect
只有一个参数,由 socket_addrinfo_lookup()创建的对象,返回的是 Socket 实例 (同socket_create 返回)。
效果类似 socket_create + socket_connect。下面以icmp为例子做对比。
icmp为例子做对比
$addrinfo_arr = socket_addrinfo_lookup('cloudfare.com',null,array(
'ai_family' => AF_INET,
'ai_socktype' => SOCK_RAW,
'ai_protocol' => getprotobyname('icmp'),
));
if(!empty($addrinfo_arr)){
foreach ($addrinfo_arr as $addrinfo){
$addrinfo_explain = socket_addrinfo_explain($addrinfo);
if($sock = socket_addrinfo_connect($addrinfo)){
ping($sock,$addrinfo_explain['ai_addr']['sin_addr'],'socket_addrinfo_connect');
}
}
}
$host = gethostbyname('cloudfare.com');
$sock = socket_create(AF_INET,SOCK_RAW,getprotobyname('icmp'));
if(socket_connect($sock,$host,0)){
ping($sock,$host,'socket_connect');
}
function ping ($sock,$host,$type){
$ts = microtime(true);
$package = "\x08\x00\x7d\x4b\x00\x00\x00\x00PingHost";
socket_send($sock, $package, strLen($package), 0);
if (socket_read($sock, 255))
$result = microtime(true) - $ts;
else $result = false;
socket_close($sock);
echo $type.' host='.($host).' rrt='.$result.PHP_EOL;
}
socket_addrinfo_bind
效果类似 socket_create + socket_bind。这里还需认识socket_addrinfo_lookup 的第二个参数,service
socket_addrinfo_lookup二个参数service
默认为null,返回的地址实例中ai_addr中的端口为0。
可以设置为数字或者字符串,比如http,端口会被设置成80。
code
$addrinfo_arr = socket_addrinfo_lookup('localhost','http',array(
'ai_family' => AF_INET,
'ai_socktype' => SOCK_STREAM,
'ai_protocol' => getprotobyname('tcp'),
));
if(!empty($addrinfo_arr)){
foreach ($addrinfo_arr as $addrinfo){
$sock = socket_addrinfo_bind($addrinfo);
socket_listen($sock,1);
$clnt_sock = socket_accept($sock);
$st = "hello world ^_^";
socket_write($clnt_sock, $st,strlen($st));
socket_close($clnt_sock);
socket_close($sock);
break;
}
}
$sock = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
socket_bind($sock, '127.0.0.1',80);
socket_listen($sock,1);
$clnt_sock = socket_accept($sock);
$st = "hello world ^_^";
socket_write($clnt_sock, $st,strlen($st));
socket_close($clnt_sock);
socket_close($sock);
$addrinfo_arr = socket_addrinfo_lookup('localhost','http',array(
'ai_family' => AF_INET,
'ai_socktype' => SOCK_STREAM,
'ai_protocol' => getprotobyname('tcp'),
));
if(!empty($addrinfo_arr)){
foreach ($addrinfo_arr as $addrinfo){
if($sock = socket_addrinfo_connect($addrinfo)){
$ret= socket_read($sock, 1024);
print "from simple-tcp-server:".$ret.PHP_EOL;
socket_close($sock);
exit;
}
}
}
$clnt_sock = socket_create(AF_INET, SOCK_STREAM, getprotobyname('tcp'));
socket_connect($clnt_sock, '127.0.0.1', 5000);
$ret= socket_read($clnt_sock, 1024);
print "from simple-tcp-server:".$ret.PHP_EOL;
socket_close($clnt_sock);
参考
function.socket-addrinfo-lookup.php
https://github.com/php/php-src/search?q=socket_addrinfo_lookup