|
|
CXIII. ソケット関数
ソケット拡張モジュールは、一般的なBSDソケットに基づくソケット通信
に関する低レベルインターフェースを実装し、クライアントだけでなく、
ソケットサーバーとして動作させることが可能となります。
より一般的なクライアントサイドのソケットインターフェースについては、
stream_socket_client(),
stream_socket_server(),
fsockopen() および
pfsockopen()を参照下さい。
ここで説明するソケット関数を使用する場合、多くの関数は、C言語に同
じ名前の関数が存在しますが、しばしば定義が異なっていることに注意して下さい。
混乱を避けるには、説明をよく読んで下さい。
このようにソケットプログラミングと異なっている点はあります
が、それでも有用な多くのUNIX man ページを参照することができます。
Web上にC言語のソケットプログラミングのチュートリアル情報が存在し、
その多くは、若干の修正により、PHPにおけるソケットプログラミングに
適当することが可能です。
が、手始めと
して適しているでしょう。
警告 | このモジュールは、
実験的なものです。これは、これらの関数の動作、関
数名は、このドキュメントに書かれて事項と同様に告知なく将来的なPHPのリ
リースで変更される可能性があります。注意を喚起するとともに、このモジュー
ルは使用者のリスクで使用して下さい。 |
これらの関数は、標準モジュールの一部として利用可能であり、常に使用できます。
ここに既述されたソケット関数はPHP拡張モジュールの一部であり、コン
パイル時にconfigureにオプション--enable-socketsを指定することにより使用
可能となります。
この拡張モジュールは設定ディレクティブを全く定義しません。 この拡張モジュールはリソース型を全く定義しません。
これらの定数は、この拡張モジュールで定義されており、
この拡張モジュールがPHP内部にコンパイルされているか実行時に動的にロー
ドされるかのどちらかの場合のみ使用可能です。
ソケット拡張モジュールは、強力なBSDソケットへの有用なインターフェ
イスを提供するために作成されました。
関数は、Win32およびUNIXの実装において等しく動作するように注意が払
われています。ソケット関数の多くは特定の条件で失敗し、エラーを記
述するE_WARNINGメッセージを出力します。
これは、時々開発者が望まない時に発生することがあります。例えば、
関数 socket_read() は突然
E_WARNING メッセージを出力する可能性がありま
す。これは、予測しない接続断が発生したためです。
@演算子により警告出力を抑制し、
socket_last_error()関数によりアプリケーション
内でエラーコードを取得することが一般に行われています。
エラーを記述する文字列を取得するためにこのエラーコードを指定して
socket_strerror()関数をコールすることが可能で
す。詳細は、この関数の説明を参照して下さい。
注意:
E_WARNINGメッセージは、ソケット拡張モジュー
ルにより英語で生成されますが、取得されるエラーメッセージは、カレ
ントのロケール(LC_MESSAGES)に依存します。
Warning - socket_bind() unable to bind address [98]: Die Adresse wird bereits verwendet
|
例 1. ソケットの例: 簡易TCP/IPサーバー
この例は、簡単な応答サーバーです。変数address
とportを設定と実行環境に合うように変更して下
さい。このサーバーに次のようなコマンドで接続することが可能です。
: telnet 192.168.1.53 10000 (ただし、アドレス
とポートは設定に合わせます)入力したものは、サーバー側の出力とな
り、エコーバックされます。接続を閉じるには、'quit'を入力します。
#!/usr/local/bin/php -q
<?php
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$address = '192.168.1.53';
$port = 10000;
if (($sock = socket_create(AF_INET, SOCK_STREAM, 0)) < 0) {
echo "socket_create() failed: reason: " . socket_strerror ($sock) . "\n";
}
if (($ret = socket_bind($sock, $address, $port)) < 0) {
echo "socket_bind() failed: reason: " . socket_strerror ($ret) . "\n";
}
if (($ret = socket_listen($sock, 5)) < 0) {
echo "socket_listen() failed: reason: " . socket_strerror($ret) . "\n";
}
do {
if (($msgsock = socket_accept($sock)) < 0) {
echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
break;
}
$msg = "\nWelcome to the PHP Test Server. \n" .
"To quit, type 'quit'. To shut down the server type 'shutdown'.\n";
socket_write($msgsock, $msg, strlen($msg));
do {
if (FALSE === ($buf = socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
echo "socket_read() failed: reason: " . socket_strerror($ret) . "\n";
break 2;
}
if (!$buf = trim($buf)) {
continue;
}
if ($buf == 'quit') {
break;
}
if ($buf == 'shutdown') {
socket_close($msgsock);
break 2;
}
$talkback = "PHP: You said '$buf'.\n";
socket_write($msgsock, $talkback, strlen ($talkback));
echo "$buf\n";
} while (true);
socket_close($msgsock);
} while (true);
socket_close($sock);
?>
|
|
例 2. ソケットの例: 簡易 TCP/IP クライアント
この例は、簡単な一回限りのHTTPクライアントです。ここでは、あるペー
ジに接続し、HEADリクエストを送信し、応答を出力た後、終了します。
<?php
error_reporting(E_ALL);
echo "<h2>TCP/IP Connection</h2>\n";
$service_port = getservbyname('www', 'tcp');
$address = gethostbyname('www.example.com');
$socket = socket_create(AF_INET, SOCK_STREAM, 0);
if ($socket < 0) {
echo "socket_create() failed: reason: " . socket_strerror($socket) . "\n";
} else {
echo "OK.\n";
}
echo "Attempting to connect to '$address' on port '$service_port'...";
$result = socket_connect($socket, $address, $service_port);
if ($result < 0) {
echo "socket_connect() failed.\nReason: ($result) " . socket_strerror($result) . "\n";
} else {
echo "OK.\n";
}
$in = "HEAD / HTTP/1.0\r\n";
$in .= "Host: www.example.com\r\n";
$in .= "Connection: Close\r\n\r\n";
$out = '';
echo "Sending HTTP HEAD request...";
socket_write($socket, $in, strlen ($in));
echo "OK.\n";
echo "Reading response:\n\n";
while ($out = socket_read($socket, 2048)) {
echo $out;
}
echo "Closing socket...";
socket_close($socket);
echo "OK.\n\n";
?>
|
|
m dot hoppe at hyperspeed dot de
03-Sep-2004 01:36
Very good step-by-step tutorial for a network daemon is here:
[next]
[before] /psecom,id,484,nodeid,114.html
Sorry, I was forced to split this link. :-(
aidan at php dot net
18-Aug-2004 12:08
hexdump() is a fantastic function for "dumping" packets or binary output from servers. See the below link for more information.
Toppi at kacke dot de
18-Jun-2004 02:06
Here another socketclass which can handle the most importand things.
Here a little Chatserver based on this class.
Maybe its helpful :)
Toppi
KnoB
11-Jun-2004 12:35
A little example that shows how to implement a simple multi-client iterative server (without forking). Launch the server and connect multiple telnet clients on it. It's a basic chat program.
#!/usr/bin/php -q
<?php
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$address = '127.0.0.1';
$port = 8888;
function handle_client($allclient, $socket, $buf, $bytes) {
foreach($allclient as $client) {
socket_write($client, "$socket wrote: $buf");
}
}
if (($master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) < 0) {
echo "socket_create() failed: reason: " . socket_strerror($master) . "\n";
}
socket_set_option($master, SOL_SOCKET,SO_REUSEADDR, 1);
if (($ret = socket_bind($master, $address, $port)) < 0) {
echo "socket_bind() failed: reason: " . socket_strerror($ret) . "\n";
}
if (($ret = socket_listen($master, 5)) < 0) {
echo "socket_listen() failed: reason: " . socket_strerror($ret) . "\n";
}
$read_sockets = array($master);
while (true) {
$changed_sockets = $read_sockets;
$num_changed_sockets = socket_select($changed_sockets, $write = NULL, $except = NULL, NULL);
foreach($changed_sockets as $socket) {
if ($socket == $master) {
if (($client = socket_accept($master)) < 0) {
echo "socket_accept() failed: reason: " . socket_strerror($msgsock) . "\n";
continue;
} else {
array_push($read_sockets, $client);
}
} else {
$bytes = socket_recv($socket, $buffer, 2048, 0);
if ($bytes == 0) {
$index = array_search($socket, $read_sockets);
unset($read_sockets[$index]);
socket_close($socket);
} else {
$allclients = $read_sockets;
array_shift($allclients); handle_client($allclients, $socket, $buffer, $bytes);
}
}
}
}
?>
noSanity
18-May-2004 02:40
I have searched long and hard for a ping script that does NOT use EXEC() or SYSTEM(). So far, I have found nothing, so I decided to write my own, which was a task to say the least.
First off, I would like to thank Khaless for their checksum function, converting it from C looked like a task in itself.
Here is the class I wrote
<?php
class Net_Ping
{
var $icmp_socket;
var $request;
var $request_len;
var $reply;
var $errstr;
var $time;
var $timer_start_time;
function Net_Ping()
{
$this->icmp_socket = socket_create(AF_INET, SOCK_RAW, 1);
socket_set_block($this->icmp_socket);
}
function ip_checksum($data)
{
for($i=0;$i<strlen($data);$i += 2)
{
if($data[$i+1]) $bits = unpack('n*',$data[$i].$data[$i+1]);
else $bits = unpack('C*',$data[$i]);
$sum += $bits[1];
}
while ($sum>>16) $sum = ($sum & 0xffff) + ($sum >> 16);
$checksum = pack('n1',~$sum);
return $checksum;
}
function start_time()
{
$this->timer_start_time = microtime();
}
function get_time($acc=2)
{
$start_time = explode (" ", $this->timer_start_time);
$start_time = $start_time[1] + $start_time[0];
$end_time = explode (" ", microtime());
$end_time = $end_time[1] + $end_time[0];
return number_format ($end_time - $start_time, $acc);
}
function Build_Packet()
{
$data = "abcdefghijklmnopqrstuvwabcdefghi"; $type = "\x08"; $code = "\x00"; $chksm = "\x00\x00"; $id = "\x00\x00"; $sqn = "\x00\x00"; $chksm = $this->ip_checksum($type.$code.$chksm.$id.$sqn.$data);
$this->request = $type.$code.$chksm.$id.$sqn.$data;
$this->request_len = strlen($this->request);
}
function Ping($dst_addr,$timeout=5,$percision=3)
{
if ((int)$timeout <= 0) $timeout=5;
if ((int)$percision <= 0) $percision=3;
socket_set_option($this->icmp_socket,
SOL_SOCKET, SO_RCVTIMEO, array(
"sec"=>$timeout, "usec"=>0 )
);
if ($dst_addr)
{
if (@socket_connect($this->icmp_socket, $dst_addr, NULL))
{
} else {
$this->errstr = "Cannot connect to $dst_addr";
return FALSE;
}
$this->Build_Packet();
$this->start_time();
socket_write($this->icmp_socket, $this->request, $this->request_len);
if (@socket_recv($this->icmp_socket, &$this->reply, 256, 0))
{
$this->time = $this->get_time($percision);
return $this->time;
} else {
$this->errstr = "Timed out";
return FALSE;
}
} else {
$this->errstr = "Destination address not specified";
return FALSE;
}
}
}
$ping = new Net_Ping;
$ping->ping("www.google.ca");
if ($ping->time)
echo "Time: ".$ping->time;
else
echo $ping->errstr;
?>
Hope this saves some troubles.
noSanity
Khaless [at] bigpond [dot] com
19-Jan-2004 05:55
I spent a while trying to use SOCK_RAW to send ICMP request packets so i could ping. This however lead me to need the internet checksum written as a php function, which was a little hard because of the way PHP handles variable types. Anyway, to save others the effort heres what i came up with, this returns Checksum for $data
<?PHP
function inetChecksum($data)
{
for($i=0;$i<strlen($data);$i += 2)
{
if($data[$i+1]) $bits = unpack('n*',$data[$i].$data[$i+1]);
else $bits = unpack('C*',$data[$i]);
$sum += $bits[1];
}
while ($sum>>16) $sum = ($sum & 0xffff) + ($sum >> 16);
$checksum = pack('n1',~$sum);
return $checksum;
}
?>
And with this i was able to construct a correct PING Request.
murzik [at] pisem dot net
06-Oct-2003 11:32
>The function, that send the WakeOnLan (WOL, Magic packet) signal:
<?php
flush();
function WakeOnLan($addr, $mac)
{
$addr_byte = explode(':', $mac);
$hw_addr = '';
for ($a=0; $a < 6; $a++) $hw_addr .= chr(hexdec($addr_byte[$a]));
$msg = chr(255).chr(255).chr(255).chr(255).chr(255).chr(255);
for ($a = 1; $a <= 16; $a++) $msg .= $hw_addr;
$s = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if ($s == false)
{
echo "Error creating socket!\n";
echo "Error code is '".socket_last_error($s)."' - " . socket_strerror(socket_last_error($s));
}
else
{
$opt_ret = socket_set_option($s, 1, 6, TRUE);
if($opt_ret < 0)
{
echo "setsockopt() failed, error: " . strerror($opt_ret) . "\n";
}
$e = socket_sendto($s, $msg, strlen($msg), 0, $addr, 2050);
socket_close($s);
echo "Magic Packet sent (".$e.") to ".$addr.", MAC=".$mac;
}
}
WakeOnLan('192.168.1.255', '00:05:1C:10:04:05');
?>
>TO "judeman at yahoo dot com":
I haven't found setsockopt() function ip PHP, I've found an equialent: socket_set_option().
bart at mediawave dot nl
06-Jan-2003 11:42
Here is a good article on some of the differences between HTTP 1.0 and HTTP 1.1.
talmage at usi-rpg dot com
04-Jan-2003 08:02
I have spent the past two days ripping out hair trying to figure out how to prevent zombie processes w/the examples above and I just happend to find this in the manual for another lanuage, felt it neccassry to port it here.
--begin copy--
van[at]webfreshener[dot]com
11-Oct-2002 02:53
Forking your PHP daemon will cause it to zombie on exit.
...or so I've seen on:
FreeBSD (PHP4.2.x)
Debian (PHP4.3.0-dev)
Darwin (PHP4.3.0-dev)
This was tested with the example code above and other scripts created for evaluation.
Seems adding <b>--enable-sigchild</b> to your configure will get rid of the problem.
Hope that saves some hair tearing :]
--end copy--
Thanks [email protected] !!!!
saryon at unfix dot org
09-Jul-2002 04:42
I found this EXTREMELY useful link on the zend php
mailing list:
It's about being able to use multiple connections
in a php socket server, WITHOUT having
to use those threads everyone seems to be
so very fond of.
works very well :)
(ps: i didn't make it, so....don't say thanks to me ;),
thank him)
daniel[at]lorch.cc
22-Feb-2002 05:32
"Beej's Guide to Network Programming" is an absolutely excellent and easy to understand tutorial to socket programming. It was written for C developers, but as the socket functions in PHP are (almost) analoguous, this should not be a problem.
davem at olsusa dot com
18-Feb-2002 02:27
Below is a simple forked daemon I wrote in PHP. I haven't seen one yet anywhere else, so I thought some people might be wondering how to do it. Execute
with php -q <file>
<?PHP
function sig_handler($signo) {
switch($signo) {
case SIGTERM:
exit;
break;
case SIGHUP:
break;
case SIGUSR1:
print "Caught SIGUSR1...\n";
break;
case SIGCHLD:
while( pcntl_waitpid(-1,$status,WNOHANG)>0 ) {
}
break;
case SIGINT:
exit;
default:
break;
}
}
function interact($sock) {
}
function become_daemon() {
$child = pcntl_fork();
if($child) {
exit; }
posix_setsid(); chdir("/");
umask(0); return posix_getpid();
}
function open_pid_file($file) {
if(file_exists($file)) {
$fp = fopen($file,"r");
$pid = fgets($fp,1024);
fclose($fp);
if(posix_kill($pid,0)) {
print "Server already running with PID: $pid\n";
exit;
}
print "Removing PID file for defunct server process $pid\n";
if(!unlink($file)) {
print "Cannot unlink PID file $file\n";
exit;
}
}
if($fp = fopen($file,"w")) {
return $fp;
} else {
print "Unable to open PID file $file for writing...\n";
exit;
}
}
function change_identity($uid,$gid) {
global $pid_file;
if(!posix_setgid($gid)) {
print "Unable to setgid to $gid!\n";
unlink($pid_file);
exit;
}
if(!posix_setuid($uid)) {
print "Unable to setuid to $uid!\n";
unlink($pid_file);
exit;
}
}
error_reporting (4);
set_time_limit (0);
ob_implicit_flush ();
$pid_file = '/tmp/php_daemon.pid';
$underpriv_uid = '99'; $underpriv_gid = '99';
$port = 10000;
$address = 0; $quit = 0;
pcntl_signal(SIGCHLD, "sig_handler");
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");
$fh = open_pid_file($pid_file);
if (($sock = socket_create (AF_INET, SOCK_STREAM, 0)) < 0) {
print "socket_create() failed: reason: " . socket_strerror ($sock) . "\n";
}
if (($ret = socket_bind ($sock, $address, $port)) < 0) {
print "socket_bind() failed: reason: " . socket_strerror ($ret) . "\n";
}
if (($ret = socket_listen ($sock, 0)) < 0) {
print "socket_listen() failed: reason: " . socket_strerror ($ret) . "\n";
}
change_identity($underpriv_uid,$underpriv_gid);
print "Server ready. Waiting for connections.....\n";
$pid = become_daemon();
fputs($fh,$pid);
fclose($fh);
while(!$quit) {
if (($connection = socket_accept($sock)) < 0) {
next;
}
if( ($child = pcntl_fork()) == -1 ) {
print "Could not fork!!\n";
print "Dying...\n";
$quit++;
}
elseif($child == 0) {
socket_close($sock);
interact($connection);
exit;
}
socket_close($connection);
}
if(posix_getpid() == $pid) {
unlink($pid_file);
}
judeman at yahoo dot com
05-Jun-2001 05:49
After several hours of working with sockets in an attempt to do UDP broadcasting, I thought a little help was in order for anyone else looking to do something similar, since it uses a number of those "undocumented" functions. Here's how I did it:
<?php
$sock = socket(AF_INET, SOCK_DGRAM, 0);
if($sock < 0)
{
echo "socket() failed, error: " . strerror($sock) . "\n";
}
$opt_ret = setsockopt($sock, 1, 6, TRUE);
if($opt_ret < 0)
{
echo "setsockopt() failed, error: " . strerror($opt_ret) . "\n";
}
$send_ret = sendto($sock, $broadcast_string, strlen($broadcast_string), 0, '255.255.255.255', 4096);
if($send_ret < 0)
{
echo "sendto() failed, error: " . strerror($send_ret) . "<BR>\n"; }
close($sock);
| |