|
|
XCVII. Socket FunktionenEinf�hrung
Die Socket-Erweiterung stellt eine hardwarenahe Schnittstelle
zu den Datenaustauschfunktionen �ber eine Socket-Verbindung zur
Verf�gung, die auf den bekannten BSD Sockets aufbaut, und bietet
die M�glichkeit, entweder als Socket-Server oder als Client zu
agieren.
F�r eine allgemeinere clientseitige Socket Schnittstelle siehe
fsockopen() und
pfsockopen().
Wenn man diese Funktionen benutzt, darf man nicht vergessen,
dass, obwohl viele der Funktionen gleichlautende Namen wie ihre
Entsprechungen in C haben, sie dennoch oft unterschiedliche
Deklarationen haben. Bitte lesen Sie die Deklarationen, um
Verwirrung zu vermeiden.
Das heisst, wem Socketprogrammierung fremd ist, der findet eine
grosse Menge n�tzlicher Materialien in den entsprechenden Unix
manpages und es gibt jede Menge von Tutorials und Informationen
�ber Socketprogrammierung in C im Internet. Vieles davon kann mit
leichten �nderungen f�r die Socketprogrammierung mit PHP benutzt
werden.
Die ist
daf�r ein guter Anfang.
Warnung | Diese Erweiterung ist
EXPERIMENTELL. Das Verhalten dieser Erweiterung,
einschlie�lich der Funktionsnamen, und alles Andere was hier dokumentiert
ist, kann sich in zuk�nftigen PHP-Versionen ohne Ank�ndigung �ndern. Seien
Sie gewarnt und verwenden Sie diese Erweiterung auf eigenes Risiko.
|
AnforderungenDiese Erweiterung ben�tigt zur Erstellung keine externen Bibliotheken. Installation
Die hier beschriebenen Socket Funktionen sind Teil einer PHP
Erweiterung, die beim Kompilieren mit der Option --enable-sockets beim Befehl
configure eingebunden werden muss.
Laufzeit KonfigurationDiese Erweiterung definiert keine Konfigurationseinstellungen in der php.ini. Resource TypenDiese Erweiterung definiert keine Resource-Typen. Vordefinierte Konstanten
Folgende Konstanten werden von dieser Erweiterung definiert und
stehen nur zur Verf�gung, wenn die Erweiterung entweder statisch
in PHP kompiliert oder dynamisch zur Laufzeit geladen wurde.
Socket Fehler
Die Socket-Erweiterung wurde geschrieben, um eine n�tzliche
Schnittstelle zu den m�chtigen BSD-Funktionen zur Verf�gung zu
stellen. Es wurde darauf geachtet, dass die Funktionen sowohl
unter Win32 als auch unter Unix-Implementierungen arbeiten. Fast
alle Socket-Funktionen k�nnen unter besonderen Umst�nden
fehlschlagen und deshalb eine E_WARNING-
Meldung ausgeben, die den aufgetretenen Fehler beschreibt. Dies
ist manchmal nicht w�nschenswert f�r den Entwickler. Zum Beispiel
kann die Funktion socket_read() pl�tzlich eine
E_WARNING-Meldung ausgeben, weil die
Verbindung unerwartet beendet wurde. Normalerweise werden solche
Meldungen mit dem @-Operator unterdr�ckt und
der Fehlercode innerhalb der Anwendung mit der Funktion
socket_last_error() aufgefangen. Sie k�nnen
auch die Funktion socket_strerror() mit diesem
Fehlercode aufrufen, um einen String, der den Fehler beschreibt,
zu erhalten. N�here Informationen finden Sie bei diesen beiden
Funktionen.
Anmerkung:
Die E_WARNING-Meldungen, die von der
Socket-Extension generiert werden, sind auf Englisch, aber die
Meldungen, die Sie extra abfragen k�nnen, erscheinen in der
entsprechenden Sprache, die Sie eingestellt haben
(LC_MESSAGES):
Warning - socket_bind() unable to bind address [98]: Die Adresse wird bereits verwendet |
Beispiele
Beispiel 1. Socket Beispiel: Einfacher TCP/IP Server
Dieses Beispiel zeigt einen einfachen Echo-Server. Passen Sie
die Variablen address und
port an Ihre Systemumgebung an und f�hren
Sie das Skript aus. Dann k�nnen Sie sich mit einem Befehl wie:
telnet 192.168.1.53 10000 (bei dem adress
und port an Ihre Umgebung angepasst ist) mit dem Server
verbinden. Alles, was Sie eingeben, wird an den Server geschickt
und von diesem wieder an Sie zur�ckgegeben. Um die Verbindung
zu trennen, geben sie 'quit' ein.
#!/usr/local/bin/php -q
<?php
error_reporting (E_ALL);
/* Das Skript wartet auf hereinkommende Verbindungsanforderungen. */
set_time_limit (0);
/* Die implizite Ausgabe wird eingeschaltet, so dass man sieht, was gesendet wurde. */
ob_implicit_flush ();
$address = '192.168.1.53';
$port = 10000;
if (($sock = socket_create (AF_INET, SOCK_STREAM, 0)) < 0) {
echo "socket_create() fehlgeschlagen: Grund: " . socket_strerror ($sock) . "\n";
}
if (($ret = socket_bind ($sock, $address, $port)) < 0) {
echo "socket_bind() fehlgeschlagen: Grund: " . socket_strerror ($ret) . "\n";
}
if (($ret = socket_listen ($sock, 5)) < 0) {
echo "socket_listen() fehlgeschlagen: Grund: " . socket_strerror ($ret) . "\n";
}
do {
if (($msgsock = socket_accept($sock)) < 0) {
echo "socket_accept() fehlgeschlagen: Grund: " . socket_strerror ($msgsock) . "\n";
break;
}
/* Anweisungen senden. */
$msg = "\nWillkommen auf dem PHP Testserver. \n" .
"Um zu beenden, geben Sie 'quit' ein. Um den Server herunterzufahren, geben Sie 'shutdown' ein.\n";
socket_write($msgsock, $msg, strlen($msg));
do {
if (FALSE === ($buf = socket_read ($msgsock, 2048))) {
echo "socket_read() fehlgeschlagen: Grund: " . 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: Sie haben '$buf' eingegeben.\n";
socket_write ($msgsock, $talkback, strlen ($talkback));
echo "$buf\n";
} while (true);
socket_close ($msgsock);
} while (true);
socket_close ($sock);
?> |
|
Beispiel 2. Socket Beispiel: Einfacher TCP/IP Client
Dieses Beispiel zeigt einen einfachen HTTP-Client, der einen
einzelnen Request absetzen kann. Er verbindet sich mit einer
Seite, setzt einen HEAD-Request ab, gibt die Antwort zur�ck und
wird beendet.
<?php
error_reporting (E_ALL);
echo "<h2>TCP/IP Verbindung</h2>\n";
/* Den Port des WWW-Dienstes holen. */
$service_port = getservbyname ('www', 'tcp');
/* Die IP-Adresse des Zielhosts holen. */
$address = gethostbyname ('www.example.com');
/* einen TCP/IP Socket erzeugen. */
$socket = socket_create (AF_INET, SOCK_STREAM, 0);
if ($socket < 0) {
echo "socket_create() fehlgeschlagen: Grund: " . socket_strerror ($socket) . "\n";
} else {
echo "OK.\n";
}
echo "Versuche, eine Verbindung zu '$address' auf Port '$service_port' aufzubauen ...";
$result = socket_connect ($socket, $address, $service_port);
if ($result < 0) {
echo "socket_connect() fehlgeschlagen.\nGrund: ($result) " . socket_strerror($result) . "\n";
} else {
echo "OK.\n";
}
$in = "HEAD / HTTP/1.0\r\n\r\n";
$out = '';
echo "Einen HTTP HEAD Request senden ...";
socket_write ($socket, $in, strlen ($in));
echo "OK.\n";
echo "Die Antwort lesen:\n\n";
while ($out = socket_read ($socket, 2048)) {
echo $out;
}
echo "Den Socket schliessen ...";
socket_close ($socket);
echo "OK.\n\n";
?> |
|
- Inhaltsverzeichnis
- socket_accept -- Akzeptiert eine Verbindung an einem Socket
- socket_bind -- Verkn�pft einen Namen mit einem Socket
- socket_clear_error -- L�scht entweder einen Fehler oder den letzten Fehlercode
eines Sockets
- socket_close -- Schliesst einen Socket
- socket_connect -- Baut eine Verbindung zu einem Socket auf
- socket_create_listen -- �ffnet einen Socket um Verbindungen an einem gegebenen Port aufzubauen
- socket_create_pair -- Erzeugt ein paar von unverwechselbaren Sockets und speichert sie in fds.
- socket_create -- Erzeugt einen Socket (Endpunkt f�r die Kommunikation)
- socket_get_option -- Holt die Socket-Optionen f�r einen Socket
- socket_getpeername --
Fragt das entfernte Ende eines gegebenen Sockets ab. Das Ergebnis
ist vom Typ abh�ngig und ist entweder das Paar host/port oder ein
Pfad des Unix-Dateisystems.
- socket_getsockname --
Fragt das lokale Ende eines gegebenen Sockets ab. Das Ergebnis
ist vom Typ abh�ngig und ist entweder das Paar host/port oder ein
Pfad des Unix-Dateisystems.
- socket_iovec_add -- F�gt einen neuen Vektor in das scatter/gather Array ein
- socket_iovec_alloc -- ...]) Erstellt einen 'struct iovec' zur Benutzung mit sendmsg, recvmsg, writev und readv
- socket_iovec_delete -- L�scht einen Vektor aus einem Array von Vektoren
- socket_iovec_fetch -- Gibt die Daten aus dem iovec zur�ck, der durch
iovec_id[iovec_position] bestimmt wurde.
- socket_iovec_free -- Gibt den Speicher des durch iovec_id bezeichneten iovec frei.
- socket_iovec_set -- Setzt die Daten in iovec_id[iovec_position] auf new_val
- socket_last_error -- Gibt den letzten Fehler zur�ck, der an einem Socket aufgetreten ist.
- socket_listen -- H�rt einen Socket nach Verbindungsanforderungen ab
- socket_read -- Liest h�chstens die angegebene Anzahl Bytes von einem Socket.
- socket_readv -- Liest aus einem Dateideskriptor. Dabei wird das
scatter-gather Array benutzt, welches von iovec_id definiert wird.
- socket_recv -- Empf�ngt Daten von einem verbundenen Socket
- socket_recvfrom -- Empf�ngt Daten von einem Socket, unabh�ngig von einer Verbindung
- socket_recvmsg -- Wird benutzt, um Nachrichten an einem Socket zu empfangen,
ob verbindungsorientiert oder nicht.
- socket_select -- F�hrt einen select() Systemaufruf auf den gegebenen
Socket-Arrays aus, wobei mit tv_sec und tv_usec ein Zeitlimit
bestimmt wird.
- socket_send -- Sendet Daten an einen verbundenen Socket
- socket_sendmsg -- Sendet eine Nachricht an einen Socket, egal ob dieser
verbindungsorientiert ist oder nicht.
- socket_sendto -- Sendet eine Nachricht an einen Socket, egal ob dieser verbunden ist oder nicht.
- socket_set_nonblock -- Setzt den nonblocking-Modus f�r den Datei-Deskriptor fd.
- socket_set_option -- Setzt die Socket-Optionen f�r einen Socket
- socket_shutdown -- F�hrt einen Socket zum Empfangen oder zum Senden oder zu beidem, herunter.
- socket_strerror -- Gibt einen String zur�ck, der einen socket-Fehler beschreibt.
- socket_write -- Schreibt in den Socket
- socket_writev -- Schreibt in einen Datei-Deskriptor fd, unter Benutzung des scatter-gather Arrays, das von iovec_id definiert wird.
User Contributed Notes Socket Funktionen |
|
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
// here is a basic opening of the a socket.
AF_INET specifies the internet domain. SOCK_DGRAM
// specifies the
Datagram socket type the 0 specifies that I want to use the default
protcol (which in this
// case is UDP)
$sock = socket(AF_INET,
SOCK_DGRAM, 0);
// if the file handle assigned to socket is less
than 0 then opening the socket failed
if($sock <
0)
{
echo "socket() failed, error: " .
strerror($sock) . "\n";
}
// here's where I set
the socket options, this is essential to allow broadcasting. An earlier
comment (as of
// June 4th, 2001) explains what the parameters are.
For my purposes (UDP broadcasting) I need to set
// the broadcast
option at the socket level to true. In C, this done using SOL_SOCKET as
the level param
// (2) and SO_BROADCAST as the type param (3). These
may exist in PHP but I couldn't reference them
// so I used the
values that referencing these variables in C returns (namely 1 and 6
respectively). This
// function is basically just a wrapper to the C
function so check out the C documentation for more info
$opt_ret =
setsockopt($sock, 1, 6, TRUE);
// if the return value is less
than one, an error occured setting the options
if($opt_ret <
0)
{
echo "setsockopt() failed, error: " .
strerror($opt_ret) . "\n";
}
// finally I am
ready to broad cast something. The sendto function allows this without
any
// connections (essential for broadcasting). So, this function
sends the contents of $broadcast_string to the
// general broadcast
address (255.255.255.255) on port 4096. The 0 (param 4) specifies no
special
// options, you can read about the options with man sendto
$send_ret = sendto($sock, $broadcast_string,
strlen($broadcast_string), 0, '255.255.255.255', 4096);
// if the
return value is less than 0, an error has occured
if($send_ret <
0)
{
echo "sendto() failed, error: " .
strerror($send_ret) . " \n"; }
// be sure to close your
socket when you're done
close($sock);
|
|
davem at olsusa dot com
18-Feb-2002 01: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
/*
PHP
forked daemon Standalone PHP binary must be compiled with
--enable-sockets and --enable-pcntl Dave M. -2002 Online
Services USA */
function sig_handler($signo) {
switch($signo) { case SIGTERM: // handle
shutdown tasks exit; break;
case SIGHUP: // handle restart tasks
break; case SIGUSR1: print "Caught
SIGUSR1...\n"; break; case SIGCHLD:
while( pcntl_waitpid(-1,$status,WNOHANG)>0 ) {
} break; case SIGINT: exit;
default: // not implemented yet...
break; }
}
function interact($sock) {
//
Custom code goes here... e.g: socket_read()
socket_write()...
}
function become_daemon()
{
$child = pcntl_fork(); if($child) { exit; // kill
parent } posix_setsid(); // become session
leader chdir("/"); umask(0); // clear umask 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'; // uid 99 == user nobody, at least on my system. $underpriv_gid =
'99';
$port = 10000; $address = 0; // 0 binds to all addresses,
may not work on fbsd
$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); }
|
|
daniel[at]lorch.cc
22-Feb-2002 04: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.
|
|
daniel[at]lorch.cc
02-Apr-2002 01:31 |
|
Although this is not the primary use of PHP, writing a server daemon in PHP
is easily feasible.
The most advanced webserver I've seen so far
(with an incredible feature-list) is nanoweb:
<advertizing>
I'm
trying to do the same thing, but I'm less advanced than nanoweb. My
primary goal is to create a "framework" on top other developers
should be able to easily create *any* server. Feel free to join the
project :)
</advertizing>
|
|
Pieter
09-Apr-2002 09:03 |
|
On Windows it's also possible to use sockets
Just open your PHP.ini
and look for the following
part:
;extension=extensions\php_sockets.dll
Unmark it like
this
extension=extensions\php_sockets.dll
and there's
Sockets to be build in your programs
|
|
billyuser at examples dot com
16-Jun-2002 02:08 |
|
Great, that warning about versions. No I use 4.1.2 and can only use the
function socket_clear_error (PHP 4 >= 4.2.0). The rest is 4.1.0. I'm
impressed! Billy
|
|
sethmail at poczta dot fm
08-Jul-2002 11:03 |
|
To Ramar: All you have to do is to use fopen() function.
Here is an
example of using POST method: $host = "www.php.net"; $data
= "lang=en_US&pattern=sockets&show=quickref";
$fp
= fsockopen($host,80);
fputs($fp, "POST /search.php
HTTP/1.1\n"); fputs($fp, "Host: $host\n"); fputs($fp,
"Content-type:
application/x-www-form-urlencoded\n"); fputs($fp,
"Content-length: " . strlen($data) .
"\n"); fputs($fp, "User-Agent:
MSIE\n"); fputs($fp, "Connection:
close\n\n"); fputs($fp, $data);
while (!feof($fp)) $buf
.= fgets($fp,128); fclose($fp);
echo $buf;
|
|
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)
|
|
gv (at) damnsw (dot) net
20-Sep-2002 09:18 |
|
#!/usr/bin/php -q <?php /* This is a remake of a simple, one-shot
HTTP client which is more or less nonfunctional with the current PHP
sockets implementation. The original can currently be found in the
'examples' section of the main sockets documentation --
Use by `chmod 755`'ing it, then running in the command line:
./thisfile.php
This example requires the sockets extension
compiled into the PHP Commandline Module () to
run without modification. It can, theoretically, be used with the
web service module (Remove the "#!/usr/bin/php -q" line),
but that would reduce the already minimal functional use to about
nill. Of course, it will function as an example of working sockets
regardless. */
error_reporting (E_ALL);
/* Get the port
for the WWW service. This could be prevented by just using '80' in
the socket_connect() call, but this is a slightly more
human-readable method, or something. */ $service_port =
getservbyname( "www", "tcp" );
/* This part is
actually rather important -- socket_connect() takes an IP, not a
hostname. */ $address = gethostbyname( "www.example.com"
); /* Create a TCP/IP socket. */ $socket = socket_create(
AF_INET, SOCK_STREAM, 0 ); if( $socket < 0 ) { echo
"socket_create() failed: reason: " . socket_strerror( $socket )
. "\n"; exit; /* There's no reason to continue to execute
the script -- adding intelligent retry code would be beyond the
scope of the original example. */ }
/* Attempt to
connect to $address on $service_port */ if( !( @socket_connect(
$socket, $address, $service_port ) ) ) { /* Print an error message if
the socket failed to connect; supress PHP's builtin error message.
*/ $result = socket_last_error( $socket ); socket_clear_error(
$socket ); print "Error connecting to $ip:$port -- " .
socket_strerror( $result ) . " ($result)\n"; exit; /* Once
again, we're not going to bother retrying. */ } $in =
"GET / HTTP/1.0\r\n\r\n"; $out = '';
/* Send the
request for data. */ socket_write( $socket, $in, strlen( $in )
);
/* Read data. */ while( ($out = socket_read( $socket, 2048,
PHP_BINARY_READ ) ) != false ) { /* As can be seen in the
socket_read() documentation, the optional "type" (in this
case, PHP_BINARY_READ) determines when socket_read() stops reading.
The binary read type will read until there's no more data or the
limit has been reached -- PHP_NORMAL_READ will stop at \r or
\n. Doing a normal read in this situation would
complicate matters because we'd need to re-add the line breaks.
While that may sound easy, correctly guessing if we're at the end
of the line or we're just seeing the read limit kicking in would be
a little iffy. Besides, there are no disadvantages to the
binary read. */ echo $out; }
/* Theoretically, the
webserver has already closed the socket, as it has finished sending
data. However, it doesn't hurt to socket_close(), as it does
deallocational stuff that will not happen automagically when the
remote side dies. Presumably, PHP will do this anyway when the
script ends, but it doesn't hurt. */ socket_close( $socket
); /* *sniffle* My poor, innocent indenting. Waah. A
better formatted copy can be found here:
*/
?>
|
|
talmage at usi-rpg dot com
04-Jan-2003 07: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] !!!!
|
|
bart at mediawave dot nl
06-Jan-2003 10:42 |
|
Here is a good article on some of the differences between HTTP 1.0 and HTTP
1.1.
|
|
Benjamin Kuz
17-Mar-2003 09:46 |
|
The talkback example shown on this page has an error, and may not function
as intended on newer versions of PHP.
the line that
contains
if (FALSE === ($buf = socket_read ($msgsock, 2048)))
{
needs to be
if (FALSE === ($buf = socket_read ($msgsock,
2048, PHP_NORMAL_READ))) {
This is due to changes in how PHP starts
socket_read(), and may be confusing to people trying to use the
example.
The way it is, it appears that the socket_read() function
is executed as quickly as possible returning whatever is in it's buffer
before an entire line can be sent. Adding PHP_NORMAL_READ makes it so
that the buffer isn't returned until either the buffer length is reached,
or a linefeed is sent.
|
|
|
| |