PHP socket_accept throws warnings in non-blocking mode - php

I want to create an easy PHP server (TCP-based) that would serve actual time and close the connection immediately. I've done that already. I wanted to add crtl-C handling so I needed to replace blocking socket_accept with non-blocking (this is because when the blocking socket_accept instruction is reached and I send SIGINT /ctrl-C/ then the server will still be alive until the first client is server and then it closes itself - and I didn't want this behavior).
My current code looks like this:
<?php
error_reporting(E_ALL);
ob_implicit_flush();
if ($argc != 2)
die("Wrong params");
$address = 'localhost';
$port = $argv[1];
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false)
die(socket_strerror(socket_last_error()) . "\n");
if (socket_bind($sock, $address, $port) === false)
die(socket_strerror(socket_last_error($sock)) . "\n");
if (socket_listen($sock, 5) === false)
die(socket_strerror(socket_last_error($sock)) . "\n");
socket_set_nonblock($sock);
$remote_host = $remote_port = $msgsock = null;
declare(ticks = 1);
function sig_handler($signo)
{
switch ($signo) {
case SIGTERM:
case SIGINT:
global $sock;
socket_shutdown($sock);
socket_close($sock);
echo "Terminating...\n";
exit;
}
}
pcntl_signal(SIGTERM, "sig_handler");
pcntl_signal(SIGINT, "sig_handler");
echo "Starting server\n";
while (1) {
do {
$msgsock = socket_accept($sock);
usleep(100000);
} while ($msgsock === false);
socket_getpeername($msgsock, $remote_host, $remote_port);
echo "Connection made from {$remote_host}:{$remote_port}\n";
$msg = date('r', time()) . "\n";
socket_write($msgsock, $msg, strlen($msg));
socket_close($msgsock);
};
socket_close($sock);
Everything works fine except for one detail... I get the following PHP warning every 0.1 second (= 100000 microseconds):
PHP Warning: socket_accept(): unable to accept incoming connection [11]: Resource temporarily unavailable in /home/tomasz/Development/Python/twisted/time-server.php on line 55
PHP Stack trace:
PHP 1. {main}() /home/tomasz/Development/Python/twisted/time-server.php:0
PHP 2. socket_accept() /home/tomasz/Development/Python/twisted/time-server.php:55
What I've tried to achieve is non-blocking accept: PHP uses the server socket, checks if there's any connection awaiting to be served. I not - wait 0.1 second. If there is a pending connection, serve it. All functionality is OK except that I've got no idea why is this warning thrown - I just want to check if there's any connection to be served. Modifying error_reporting to E_ERROR makes the warnings quiet, but I hope there's a better way to solve that...
edit:
modifying socket_accept($sock) to #socket_accept($sock) will just suspress warnings from being thrown, but still this doesn't state why it is thrown...

I located your question while searching for a solution to the exact same problem. Additionally, I found a few other posts asking about the same thing, but also without solutions. Some posts indicated that there was no way to prevent socket_accept() from throwing warnings, as it is by design to indicate "there is no waiting connection to accept." I couldn't confirm this as it doesn't seem to be mentioned in the PHP manual page for socket_accept()
Like you, I wasn't satisfied with using any sort of error suppression. It seemed the next logical step was to wrap socket_accept() in some sort of conditional so that it would only execute if I knew there was a connection waiting. I couldn't find anything that did this. But when using stream_*() functions instead of just socket_*() functions, it looks like there is. So I switched out to using streams (seems there are some other advantages as well), as follows:
if (( $socket = stream_socket_server( "tcp://$address:$port", $errno, $errstr )) === FALSE ) {
die( "failed to create socket: $errstr\n" );
}
echo "Waiting for clients to connect...\n";
while ( $server_listening ) {
$read = array( $socket );
$array = array();
if ( stream_select( $read, $array, $array, 0 )) {
$connection = stream_socket_accept( $socket, 0 );
client_handler( $socket, $connection );
} else {
usleep( 100 );
}
}
This seems to work well. My script only attempts to accept the connection if it first determines that there is a connection waiting. After going through this, I did find that there was an equivalent socket function for this, socket_select(), which appears to work the same way. So you may be able to do something similar if you wanted to stick with the socket_*() functions.
On a side note, I'm glad I made the switch to streams as they seem easier to work with. And since my application is limited to TCP, I don't seem to be missing any functionality that I would get with more low-level sockets.

There is something that does that, and it's select(). Nonblocking socket IO should use select(), period. In PHP that means socket_select(). See any reference on BSD sockets for more info.

Related

PHP/Go socket communication

Im trying to communicate between Go and PHP with a socket. The code im using is:
Go:
fmt.Println("Launching server...")
ln, _ := net.Listen("tcp", ":8080")
conn, _ := ln.Accept()
for {
message, _ := bufio.NewReader(conn).ReadString('\n')
fmt.Print("Message Received:", string(message))
conn.Write([]byte("test" +"\n"))
}
PHP:
$address = gethostbyaddr($ip);
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if($socket === false){
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error());
}
$result = socket_connect($socket, $address, $port);
if ($result === false) {
echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket));
}
socket_write($socket, "test", 4);
socket_read($socket, 4);
The problem is that the Go server keeps thinking it's receiving something all the time so it prints "Message Recieved:" constantly. If i do if(message!="") it sort of works but the cpu usage is high.
Another problem is that the server doesn't receive "test" unless i comment out the socket_read($socket, 4); in PHP.
The documentation for ReadString says:
If ReadString encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF).
This means that you're getting io.EOF (indicating that there's no more data to be read from the connection) and an empty string.
If you want to block on ReadString when no data is available, don't use bufio but rather read directly from the connection.
See also: documentation for net.Conn
Another problem is that the server doesn't receive "test" unless i comment out the socket_read($socket, 4); in PHP.
That's described here, socket_write buffers:
socket_write() does not necessarily write all bytes from the given buffer. [...]
use
fflush($socket);
after the write.

php socket occasionally unresponsive and no errors thrown

I've written a database application using MySQL and PHP on the server side, and Flex on the client side. I use a php socket to have it automatically update all clients whenever changes are made to the database.
The whole system works swimmingly, but every now and then the socket seems to stop responding. The strange thing is that the connection is still good – any changes a client performs are implemented, but the socket doesn't broadcast the message. The socket file isn't throwing any errors (though when I run error_log from the socket those messages appear). Memory use of the socket doesn't change on the server, and no disconnect signal is sent. Stranger still, eventually the socket starts working again, after about half an hour or so. If I restart the socket that also solves the problem.
I'm working on a hacky solution allowing the client to restart the socket if it becomes unresponsive, but that's unsatisfying and open to mistakes. What I'd really like is to learn why this might be happening. Does the socket somehow get "saturated" after a certain number of connections? Should I be doing something to clean up the socket server? I've tried three different physical servers (one local and two online) and the same thing happens, so it's definitely me.
I feel like there's something basic that I'm doing wrong. Here's the code I'm using for the socket server (it's a slightly modified version of socket written by Raymond Fain on kirupa.com, so I've left his original comment at the top):
#!/usr/bin/php -q
<?php
/*
Raymond Fain
Used for PHP5 Sockets with Flash 8 Tutorial for Kirupa.com
For any questions or concerns, email me at ray#obi-graphics.com
or simply visit the site, www.php.net, to see if you can find an answer.
*/
//ini_set('display_errors',1);
//ini_set('display_startup_errors',1);
error_reporting(E_ALL);
ini_set('error_log', 'socket_errors.log');
ini_set('log_errors', 'On');
ini_set('display_errors', 'Off');
set_time_limit(0);
ob_implicit_flush();
error_log('testing');
$address = 'xxx.xxx.xx.xx';
$port = xxxxx;
function send_Message($allclient, $socket, $buf)
{
$buf = str_replace("\0","",$buf);
//echo "<mbFeed>$buf</mbFeed>\n\0";
foreach($allclient as $client)
{
socket_write($client, "<mbFeed>$buf</mbFeed>\n\0");
}
}
echo "connecting...
";
//---- Start Socket creation for PHP 5 Socket Server -------------------------------------
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";
}
echo 'socket bind successfull.
';
if (($ret = socket_listen($master, 5)) < 0)
{
echo "socket_listen() failed, reason: " . socket_strerror($ret) . "\n";
}
$read_sockets = array($master);
echo "connected.";
//---- Create Persistent Loop to continuously handle incoming socket messages ---------------------
while (true)
{
$changed_sockets = $read_sockets;
$num_changed_sockets = socket_select($changed_sockets, $write = NULL, $except = NULL, NULL);
foreach($changed_sockets as $key => $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, 8192, 0);
if ($bytes == 0)
{
unset($read_sockets[$key]);
unset($changed_sockets[$key]);
socket_close($socket);
}
else
{
$allclients = $read_sockets;
array_shift($allclients);
//any messages starting with ::: are not to be broadcast, and may be used for other things. This message
//usually comes from the client.
if (substr($buffer, 0, 3) == ":::") handleSpecial(substr($buffer, 3));
else
{
//otherwise the message comes from a php file that will be closed, so the socket needs to be closed.
unset($read_sockets[$key]);
unset($changed_sockets[$key]);
socket_close($socket);
send_Message($allclients, $socket, $buffer);
}
}
}
}
}
function handleSpecial($message)
{
error_log($message);
}
?>
As the sockets in use seem to be blocking the call to socket_recv() might not return until the amount of data requested was read. And with this does not handle any other reading sockets, including the accecpting socket.
To get around this use socket_set_nonblock() to make the sockets unblocking. Please note that a call to socket_recv() on a non-blocking socket might return having read less bytes than requested, and therefore the amount of data read shall be tracked for each socket.

PHP5 fsockopen connection error break site, no matter how I capture it

I'm trying to use fsockopen to communicate with a game server, which responds with some basic stats. It works perfectly when the server is online, but if the server is ever offline, the following code causes php to stop displaying the page that reads the data.
try {
$socket = fsockopen($host, $port, $errno, $errstr, 10);
if ($socket !== false) {
fwrite($socket, "\xFE");
$data = "";
$data = fread($socket, 1024);
fclose($socket);
if ($data !== false && substr($data, 0, 1) == "\xFF") {
// get into
} else {
// Server did not send back proper data, or reading from socket failed.
print "Server not available.";
}
} else {
// ...
}
} catch(Exception $e){
// ...
}
I've tried the try/catch, I've tried adding a custom handler to the exception. My only idea is to run this outside of the web requests and store the response so that the web request isn't initiating it.
Any thoughts?
First, I'd add a couple of echo commands, either side of the fsockopen call:
echo date("Y-m-d H:i:s")."Before open\n";
$socket = fsockopen($host, $port, $errno, $errstr, 10);
echo date("Y-m-d H:i:s")."After open (socket=".($socket===false?"Bad":"OK")."\n";
This is to confirm the 10 second timeout is working. If you never see the second message then the timeout is not working, and the problem is more obscure.
Anyway, if you are getting a valid $socket, but the lock-up happens later, then try:
if ($socket !== false) {
stream_set_timeout($socket,2); //2 second timeout
stream_set_blocking($socket,false); //no blocking
fwrite($socket, "\xFE");
...
P.S. If adding those two commands solves the problem, then experiment to see if just one of them solves it. That would give a big clue what the real problem is.
It seems that by moving the logic outside the html generation worked. The lookup happens before any html is rendered, so if it fails it doesn't interrupt the html output.

PHP Socket and proc_open

I am trying to create a inetd-like service for Windows in PHP for future use with my other application.
So all I can think of is to use Steam Server and proc_open to pipe the stream directly to the process (like inetd). Because on Windows there is no pcntl_fork(), and PHP doesn't support threading.
So far, here is my code. The inetdtest program is a simple program with single printf (written in C). But the problem is that when I connected to my server (via netcat), I got no response message.
<?php
define ('SERVICE_COMMAND', 'inetdtest');
define ('SERVICE_PORT', 35123);
function main() {
echo "Simple inetd starting...\n";
$socket = stream_socket_server('tcp://0.0.0.0:' . SERVICE_PORT, $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN);
if ($socket === false) {
echo "Can't bind to service port.\n";
echo "[$errno] $errstr";
die(1);
}
$processes = array();
while (true) {
$current = #stream_socket_accept($socket, 5, $host);
if ($current !== false) {
echo 'Incomming connection from client ' . $host . "\n";
echo "Lunching child process... ";
$io = array(
0 => $current,
1 => $current,
2 => array('file', 'stderr.log', 'a')
);
$proc = proc_open(SERVICE_COMMAND, $io, $pipes, NULL, NULL, array('bypass_shell'));
$status = proc_get_status($proc);
echo " DONE! PID : {$status['pid']}\n";
$processes[] = array($current, $proc);
}
foreach ($processes as $k=>$v) {
$status = proc_get_status($v[1]);
if (false === $status['running']) {
echo "Finalizing process {$status['pid']}... ";
fflush($v[0]);
fclose($v[0]);
proc_close($v[1]);
unset($processes[$k]);
echo "DONE!\n";
}
}
}
}
main();
The code justs works as it stands here (using cat as program and on linux), so the problem lies somewhere in the windows side of things.
For one thing, the option you are passing, to bypass the shell, should be given as
array('bypass_shell'=>true)
This may fix things already. The tricky part with these things, is that you're passing a socket fd to a process, which may or may not be expected to handle that properly. I don't know how these things are done in windows, but cutting cmd out of the equation can only help.
If it still doesn't work, you should create a loop which waits for data (either from network or child processes) and sends data from the network socket to the process pipe, and vice versa.

Does my basic PHP Socket Server need optimization?

Like many people, I can do a lot of things with PHP. One problem I do face constantly is that other people can do it much cleaner, much more organized and much more structured. This also results in much faster execution times and much less bugs.
I just finished writing a basic PHP Socket Server (the real core), and am asking you if you can tell me what I should do different before I start expanding the core. I'm not asking about improvements such as encrypted data, authentication or multi-threading.
I'm more wondering about questions like "should I maybe do it in a more object oriented way (using PHP5)?", or "is the general structure of the way the script works good, or should some things be done different?". Basically, "is this how the core of a socket server should work?"
In fact, I think that if I just show you the code here many of you will immediately see room for improvements. Please be so kind to tell me. Thanks!
#!/usr/bin/php -q
<?
// config
$timelimit = 180; // amount of seconds the server should run for, 0 = run indefintely
$address = $_SERVER['SERVER_ADDR']; // the server's external IP
$port = 9000; // the port to listen on
$backlog = SOMAXCONN; // the maximum of backlog incoming connections that will be queued for processing
// configure custom PHP settings
error_reporting(1); // report all errors
ini_set('display_errors', 1); // display all errors
set_time_limit($timelimit); // timeout after x seconds
ob_implicit_flush(); // results in a flush operation after every output call
//create master IPv4 based TCP socket
if (!($master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) die("Could not create master socket, error: ".socket_strerror(socket_last_error()));
// set socket options (local addresses can be reused)
if (!socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1)) die("Could not set socket options, error: ".socket_strerror(socket_last_error()));
// bind to socket server
if (!socket_bind($master, $address, $port)) die("Could not bind to socket server, error: ".socket_strerror(socket_last_error()));
// start listening
if (!socket_listen($master, $backlog)) die("Could not start listening to socket, error: ".socket_strerror(socket_last_error()));
//display startup information
echo "[".date('Y-m-d H:i:s')."] SERVER CREATED (MAXCONN: ".SOMAXCONN.").\n"; //max connections is a kernel variable and can be adjusted with sysctl
echo "[".date('Y-m-d H:i:s')."] Listening on ".$address.":".$port.".\n";
$time = time(); //set startup timestamp
// init read sockets array
$read_sockets = array($master);
// continuously handle incoming socket messages, or close if time limit has been reached
while ((!$timelimit) or (time() - $time < $timelimit)) {
$changed_sockets = $read_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 "[".date('Y-m-d H:i:s')."] Socket_accept() failed, error: ".socket_strerror(socket_last_error())."\n";
continue;
} else {
array_push($read_sockets, $client);
echo "[".date('Y-m-d H:i:s')."] Client #".count($read_sockets)." connected (connections: ".count($read_sockets)."/".SOMAXCONN.")\n";
}
} else {
$data = #socket_read($socket, 1024, PHP_NORMAL_READ); //read a maximum of 1024 bytes until a new line has been sent
if ($data === false) { //the client disconnected
$index = array_search($socket, $read_sockets);
unset($read_sockets[$index]);
socket_close($socket);
echo "[".date('Y-m-d H:i:s')."] Client #".($index-1)." disconnected (connections: ".count($read_sockets)."/".SOMAXCONN.")\n";
} else {
if ($data = trim($data)) { //remove whitespace and continue only if the message is not empty
switch ($data) {
case "exit": //close connection when exit command is given
$index = array_search($socket, $read_sockets);
unset($read_sockets[$index]);
socket_close($socket);
echo "[".date('Y-m-d H:i:s')."] Client #".($index-1)." disconnected (connections: ".count($read_sockets)."/".SOMAXCONN.")\n";
break;
default: //for experimental purposes, write the given data back
socket_write($socket, "\n you wrote: ".$data);
}
}
}
}
}
}
socket_close($master); //close the socket
echo "[".date('Y-m-d H:i:s')."] SERVER CLOSED.\n";
?>
One small thing, personally i'd create a function for outputting instead of just using echo, that way its easy to turn it off, change the format etc.. eg
function log($message = '')
{
echo '['.date('Y-m-d H:i:s').']'.$message;
}
and then you can use :
log("SERVER CREATED (MAXCONN: ".SOMAXCONN.").\n");
instead of
echo "[".date('Y-m-d H:i:s')."] SERVER CREATED (MAXCONN: ".SOMAXCONN.").\n";
Oh and be sure to use === instead of == otherwise you might get some odd results.
I'd move your switch $data into a function as that will likely expand.
Also since it looks like you're using a text based protocol, might want to explode/strtok to get the first level command and check it against an array of valid commands. Could also have an array that describes what internal function to call and use call_user_func_array to dispatch the call.

Categories