PHP SocketServer not acception more than 255 concurrent connections - php

I'm running a PHP (5.4.12) script on a Windows Server 2008 machine.
When the total concurrent connections hit 255, the script crashes.
On a PHP 5.2.x system the SocketServer end with an error:
"An operation was attempted on something that is not a socket."
PHP 5.4.12, does not crash but does not handle the connections anymore.
I'm using the following code, taken from the PHP.net website:
http://php.net/manual/en/sockets.examples.php
<?php
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$address = '192.168.1.7';
$port = 8676;
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
echo "socket_create() last error " . socket_strerror(socket_last_error()) . "\n";
}
if (socket_bind($sock, $address, $port) === false) {
echo "socket_bind() last error " . socket_strerror(socket_last_error($sock)) . "\n";
}
if (socket_listen($sock, 5) === false) {
echo "socket_listen() last error " . socket_strerror(socket_last_error($sock)) . "\n";
}
$clients = array();
do {
$read = array();
$read[] = $sock;
$read = array_merge($read,$clients);
$write = NULL;
$except = NULL;
if(socket_select($read,$write , $except , $tv_sec = 5)===false)
{
echo(PHP_EOL.'\n\ncrash\n\n'.PHP_EOL);
exit();
}
if (in_array($sock, $read)) {
if (($msgsock = socket_accept($sock)) === false) {
echo "socket_accept() falló: razón: " . socket_strerror(socket_last_error($sock)) . "\n";
// break;
}
$clients[] = $msgsock;
$key = array_keys($clients, $msgsock);
$msg = "{$key[0]} welcome msg\n".PHP_EOL;
socket_write($msgsock, $msg, strlen($msg));
}
// Handle Input
foreach ($clients as $key => $client) { // for each client
if (in_array($client, $read)) {
if (false === ($buf = socket_read($client, 2048))) {
echo "socket_read() last error: " . socket_strerror(socket_last_error($client)) . "\n";
// break 2;
}
if (!$buf = trim($buf)) {
continue;
}
if ($buf == 'quit') {
$talkback = print_r($clients);
socket_write($client, $talkback, strlen($talkback));
unset($clients[$key]);
socket_close($client);
break;
}
if ($buf == 'shutdown') {
socket_close($client);
break 2;
}
$talkback = "Client {$key}: Echo: '$buf'.\n";
socket_write($client, $talkback, strlen($talkback));
echo "$buf\n";
}
}
} while (true);
socket_close($sock);
?>
I've tested the script by running "EchoServerTest.exe". I creates a number of concurrent connections.
First I thought it was a problem in WinSock, but I can't find that there is a limitation on 255 connections with WinSock.
I also changed the registry settings TcpNumConnections, TcpTimedWaitDelay and MaxUserPort.
I used the same code on a Ubuntu server, so the problem much be related to Windows.
I tested this on two different servers, both Windows Server 2008 (32bit and 64bit).
One locally and one on the internet. So firewall or other external problems should be ruled out.
I'm out of options.
Before you ask, do you really need so many connections? Yes.
There are many 'dead' connections, so I've altered the code. Now those 'dead' connection are now 'Timed-out'. So I have some time to solve this, but eventually the number of concurrent connections will reach 255 again.
Any ideas on this one?

I think the problem is in this line:
if (socket_listen($sock, 5) === false) {
You are manually restricting to 5 the number of backlog incoming connections queued for processing. In Linux this is silently truncated to SOMAXCONN, not that way on Windows. Try using SOMAXCONN instead of 5.

Related

PHP fsockopen() fails does not open port but telnet works

I am facing a weird network problem trying to resolve for the last 2 days. I can not get to open the port 25003 on php web page. The code does not seem to be a problem, however, its as given below.
$host = 'xx.xx.xx.xx'; // statis public ip address assigned by my isp
$ports = array(80, 25003);
foreach ($ports as $port)
{
$connection = fsockopen($host, $port);
if (is_resource($connection))
{
echo '<h2>' . $host . ':' . $port . ' ' . '(' . getservbyport($port, 'tcp') . ') is open.</h2>' . "\n";
fclose($connection);
}
else
{
echo '<h2>' . $host . ':' . $port . ' is not responding.</h2>' . "\n";
}
}
Port 80 shows open but not port 25003.
The site is hosted on Bluehost shared hosting plan with a static IP too. Cross verified with them more than 3 times the fact that port 25003 is open on both incoming and outgoing connections. Would they be lying, I don't think so.
On client PC settings :
(1) Firewall is disabled for testing purpose.
(2) Port forwarding is done correctly in router. I assume so because
I can easily telnet MY PUBLIC IP with a port 25003 within the same
LAN and from phone using sim card's internet.
(3) I did a port check from https://ping.eu/port-chk/ and it shows open.
(4) Client PC has a serproxy installed for serial to IP & Port.
(5) When I do a port check from above link, serproxy shows following message which seems to be okay on its part.
* server thread launched
* server(1) - thread started
* server(1) - EOF from sock
* server(1) - Exiting
(6) Again, when I telnet from external lan, it shows above message in Client PC's Serproxy which means it is doing its work properly. And it shows correct data from serial port to cmd line while telneting.
The problem is when I fsockopen using above pieces of code, it says CONNECTION REFUSED.
Below is my actual code which should try to connect and read data from serial port but CONNECTION REFUSED.
$fp = fsockopen("tcp://xx.xx.xx.xx", 25003, $errno, $errstr);
if (!$fp) {
echo "$errstr ($errno)<br />\n";
} else {
if (!feof($fp)) {
$weight = trim(fgets($fp, 64)," ");
}
}
echo $weight;
fclose($fp);
I think the problem lies either in bluehost shared server or client windows PC or SERPROXY or local network configuration. I am afraid there is any major config change possible other than baud rate, com port etc in the SERPROXY which is set correctly.
I am now totally clueless as to how to resolve the said problem. If somebody could help will be greatly appreciated.
I would share the public IP if somebody wants to check the connectivity.
Like I mentioned - previously when I attempted to connect all attempts failed but both methods below now work so I'd assume the problem is fixed. I'd maybe suggest restarting the webserver and browser(s)
<?php
set_time_limit( 0 );
error_reporting( E_ALL );
$address = 'xxx.xxx.xxx.xxx';
$port = 25003;
/* Method #1 */
$fp = fsockopen( "tcp://{$address}", 25003, $errno, $errstr );
if( !$fp ) echo "$errstr ($errno)<br />\n";
else {
if( !feof( $fp ) ) {
$weight = trim(fgets($fp, 64)," ");
}
}
printf('<h1>Method #1</h1>Weight: %s<br /><br />',$weight);
fclose($fp);
/* Method #2 */
function geterror(){
$obj=new stdClass;
$obj->code=socket_last_error();
$obj->error=socket_strerror( $obj->code );
return $obj;
}
function negotiate( $socket ) {
socket_recv( $socket, $buffer, 1024, 0 );
for( $chr = 0; $chr < strlen( $buffer ); $chr++ ) {
if( $buffer[ $chr ] == chr( 255 ) ) {
$send = ( isset( $send ) ? $send . $buffer[$chr] : $buffer[$chr] );
$chr++;
if( in_array($buffer[$chr], array(chr(251), chr(252))) ) $send .= chr(254);
if( in_array($buffer[$chr], array(chr(253), chr(254))) ) $send .= chr(252);
$chr++;
$send .= $buffer[$chr];
} else {
break;
}
}
if( isset($send)) socket_send($socket, $send, strlen($send), 0);
if( $chr - 1 < strlen($buffer)) return substr($buffer, $chr);
}
$socket = socket_create( AF_INET, SOCK_STREAM, SOL_TCP );
try{
if( $socket && is_resource( $socket ) ){
if( !socket_connect( $socket, $address, $port ) ){
$err=geterror();
throw new Exception( sprintf( 'socket_connect: %s', $err->error ), $err->code );
} else {
while( true ){
$e=null;
$w=null;
$r = array( $socket );
$c = socket_select( $r, $w, $e, 5 );
foreach( $r as $read_socket ) {
if( $r = negotiate( $read_socket ) ) {
exit( sprintf( '<h1>Method #2</h1>Reading: %s', print_r( $r, true ) ) );
}
}
}
}
} else {
$err=geterror();
throw new Exception( sprintf( 'Failed to create socket: %s',$err->error ), $err->code );
}
}catch( Exception $e ){
printf( "
<pre>
<h1>Error: %d</h1>\nMessage: %s\nTrace: %s\nLine: %d
</pre>",$e->getCode(),$e->getMessage(),$e->getTraceAsString(),$e->getLine() );
}
socket_close( $socket );
?>

socket_read byte by byte

I observe a strange behavior on socket read, with a timeout on read operations, when my program read byte per byte : only the beginning of the message is read, and then 'socket_read' return false.
If the read operations are done on two bytes or more, all the message is returned.
Bellow is the source code of the socket creation function and of the socket read function.
In the read function, there is a timestamp of 300 set before to read. This timestamp does not work for read operations byte per byte, since the message "BEGIN read" and "END read" are written in log file with less than one second interval.
The program need to read byte per byte to stop on a particular marker.
Anybody has an idea to enable read byte per byte with a blocking read ?
Thanks for any help !
<?php
function getSocket($port, $address)
{
// Ouverture de la socket en attente de connexion
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
detectedErrorLog("socket_create() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n");
return false;
}
//rendre la socket réutilisable (plus d'erreur "address already in use")
if (socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1) === false) {
detectedErrorLog("socket_set_option SO_REUSEADDR failed : " . socket_strerror(socket_last_error($sock)) . "\n");
return false;
}
//la connexion doit être maintenue ouverte
if (socket_set_option($sock, SOL_SOCKET, SO_KEEPALIVE, 1) === false) {
detectedErrorLog("socket_set_option SO_KEEPALIVE failed : " . socket_strerror(socket_last_error($sock)) . "\n");
return false;
}
if (socket_bind($sock, $address, $port) === false) {
detectedErrorLog("socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n");
return false;
}
if (socket_listen($sock) === false) {
detectedErrorLog( "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n");
return false;
}
return $sock;
}
function mySocketRead($msgsock, $length, $delimiter)
{
$message = "";
$cpt = 0;
$nb_octets = 1;
socket_set_option($msgsock, SOL_SOCKET, SO_RCVTIMEO, array('sec'=>300, 'usec'=>0));
$rand_number = rand();
debugLog("BEGIN read (".$rand_number."): ".time().", timeout lecture = ".print_r(socket_get_option($msgsock, SOL_SOCKET, SO_RCVTIMEO), true));
while($cpt < $length)
{
$resp = socket_read($msgsock, $nb_octets);
if($resp !== false)
{
$message .= $resp;
$cpt+=strlen($resp);
}
//S'il n'y a plus rien à lire ou qu'on arrive à la fin du message
if ($resp == false || strpos($resp, $delimiter) !== false)
{
break;
}
}
debugLog("END read (".$rand_number."): ".time().", message = ".$message);
return $message;
}
?>

socket_recv I want readable data not byte count

Hello All hopefully you guys don't shoot me with virtual guns if already asked but, here goes.
I am socket_send to an ip/port i see the server receives and responds with data back. My code receives xxxx bytes. I want to know what those bytes entail IE: xml string back so I can parse and use the data back in my application. cURL is not doable in this situation because the server receives headers and does not respond at all.
**Code:**
/* Create a TCP/IP socket. */
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
} else {
echo "OK.\n";
}
/* Create a TCP/IP socket. */
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
} else {
echo "OK.\n";
}
echo "Attempting to connect to '$address' on port '$port'...";
$result = socket_connect($socket, $address, $port);
if ($result === false) {
echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
} else {
echo "OK.\n";
}
$in = $xml;
$out = '';
echo "Sending HTTP HEAD request...";
socket_write($socket, $in, strlen($in));
echo "OK.\n";
socket_shutdown($socket, 1);
echo "Reading response:\n\n";
$buf = '';
if (false !== ($bytes = socket_recv($socket, $buf, 2048, MSG_WAITALL))) {
echo "Read $bytes bytes from socket_recv(). Closing socket...";
} else {
echo "socket_recv() failed; reason: " . socket_strerror(socket_last_error($socket)) . "\n";
}
socket_close($socket);
echo $buf . "\n";
echo "OK.\n\n";
?>
Response:
TCP/IP Connection
OK. OK. Attempting to connect to 'xxx.xxx.xxx.xxx' on port '9000'...OK. Sending HTTP HEAD request...OK. Reading response: Read 1365 bytes from socket_recv(). Closing socket... OK.
Firstly, you can use cURL and not send headers! Just an fyi.
Secondly, it's going to be tricky to determine exactly what type of content it is... Is the server returning any sort of "content type"? If so, you may be able to read that.
Afterwards, you can then use $buf and then attempt to use XML libraries to parse the string (such as PHP's SimpleXML Library).
If, for example, it doesn't parse the XML, then you know it's either a regular string or something else.
cURL should return the content type, however.
After crazy headaches and several medicine hits I found the issue: 1 line OMG
If any one else gets this issue with socket the line after socket_close($socket) I added echo html_entity_decode($buf).

PHP socket (socket_read)

I work on a personal project and i have a problem for managing socket on a multi client server.
So, according to php.net, it's possible to put in argument of socket_read() a socket create by socket_create or accepted by socket_accept...
If i understand, socket create by socket_create is a "local" socket and sockets send by socket_accept are sockets client, it's that ?
in this case, i would like to put my server socket in parameter of socket_read for wait a client among several write on this socket.
more clearly, i accept 4 clients and wait that one of them write on this socket, but i can't put in parameter a specific client socket because i don't know which of them will write...
And when i put the socket create by : socket_create, php return me an error
my native language is not English as you could see, but nobody could answer me...
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
echo "socket_create() a échoué : raison : " . socket_strerror(socket_last_error()) . "\n";
}
if (socket_bind($sock, $this->address, $this->port) === false) {
echo "socket_bind() a échoué : raison : " . socket_strerror(socket_last_error($sock)) . "\n";
}
if (socket_listen($sock, 5) === false) {
echo "socket_listen() a échoué : raison : " . socket_strerror(socket_last_error($sock)) . "\n";
}
for($i=0;$i<$this->maxClient;$i++){
if(($client = socket_accept($sock)) === false){
echo "socket_accept() a échoué : raison : " . socket_strerror(socket_last_error($sock)) . "\n";
break;
}else echo "<br/>Client $client[$i] has connected\n";
$this->joueurs[$i] = new Joueur($client,$i);
}
while($start){
$messageClient = socket_read($sock , 1024);
......
}
So,I have two problems, the first is, i need that socket_read() doesn't block my script and Scanning my sockets until it find something to read.
The second problem is that as my program does it not stopped, my client don't receive anything and when i stop my serveur my client receive the message...
Have you any idea ?
Thanks
Get familiar with socket_select - it allows for waiting on multiple sockets to become readable or/and writable, or have errors. A listening socket becomes readable when there's new connection pending/ready to be accept()-ed.
i found my problem :
for unlock socket_read()
socket_set_option($socket,SOL_SOCKET, SO_RCVTIMEO, array("sec"=>1, "usec"=>0));
socket_set_nonblock($socket);
And for clear buffer i use "/n" without that i could receive message in my client only when the server stop.

Writing then reading back via sockets in PHP issues

I writing a command and then reading back from a server via sockets in PHP. We have 20 servers that all run a Node JS script which can receive these commands and execute them. The Node JS script will return "ok" which PHP reads back to confirm the command has gone through.
The Node JS script listens on port 9000 and is set to allow half open.
Most of the time this works fine, but when a high volume of commands are sent we get errors back occasionally that say this:
Contents: Message received back from socket was 'Unexpected token {'
Transport endpoint is not connected
The transport endpoint message suggests to me that it has not connected successfully.
I am no expert in sockets so I don't know whether the implementation I have used is "correct". It does work most of the time but I am aware that there are functions like socket_bind and socket_listen that may work better, though I am not sure what they do.
Here is the PHP code that we are using. Any suggestions would be most appreciated.
public function sendDaemonCommand($address, $template_id, $params = array()) {
$hostname = $this->getHostnameFromPrivateIP($address);
$port = 9000;
$command = array('template_id' => $template_id, 'params' => $params);
$command = json_encode($command);
// Create a TCP Stream socket
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
$this->mailError("Command Failed - " . $hostname, "Failed to create socket on " . $address . "\n\n" . socket_strerror(socket_last_error()) . "\n\nCommand:\n\n" . $command . "\n" . $this->functionTraceback());
return false;
}
// Connect to socket
if (socket_connect($sock, $address, $port) === false) {
$this->mailError("Command Failed - " . $hostname, "Failed to connect to socket on " . $address . "\n\n" . socket_strerror(socket_last_error($sock)) . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
socket_close($sock);
return false;
}
// Write command to socket
$_command = $command;
$length = strlen($_command);
while (true) {
$sent = socket_write($sock, $_command, $length);
if ($sent === false) {
$this->mailError("Command Failed - " . $hostname, "Failed to write command to socket on " . $address . "\n\n" . socket_strerror(socket_last_error($sock)) . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
socket_shutdown($sock, 2);
socket_close($sock);
return false;
}
if ($sent < $length) {
$_command = substr($_command, $sent);
$length -= $sent;
}
else {
break;
}
}
socket_shutdown($sock, 1);
// Read back from socket
if (($out = socket_read($sock, 1024)) !== false) {
#socket_shutdown($sock, 0);
$out = trim($out);
if ($out !== "ok") {
$this->mailError("Command Failed - " . $hostname, "Message received back from socket was '" . $out . "' on " . $address . "\n\n" . socket_strerror(socket_last_error($sock)) . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
socket_close($sock);
return false;
}
}
else {
$this->mailError("Command Failed - " . $hostname, "Failed to read from socket on " . $address . "\n\n" . socket_strerror(socket_last_error($sock)) . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
socket_shutdown($sock, 0);
socket_close($sock);
return false;
}
socket_close($sock);
return $out;
}
For a simple socket client such as this, I much prefer fsockopen() - it drastically reduces the complication of the client code and does not require the sockets extension, which is not available everywhere. The main disadvantage to this is that you lose the string error messages, but these are rarely that useful anyway - you still get an error string from creating the socket, and if read/write operations fail it's because the socket has become disconnected.
I'm also not sure how useful "allow half open" is in this context - I think it is likely to create more problems than it solves. The calls to socket_shutdown() are not doing anything useful here (and may be the source of your problems) - you would only use this if you are operating on a socket that will not be closed and may be operated on at a later point in time by some other code. Since you create a new socket and destroy it in the same routine, this is not the case.
Here is your code rewritten to use fsockopen() - as you can see, it is somewhat shorter and simpler. I have also modified it slightly so that it always returns a bool - your code returns either bool FALSE or string ok, and since there are only these two options it makes more sense for the function to always return a bool.
public function sendDaemonCommand($address, $template_id, $params = array()) {
// Prepare data
$hostname = $this->getHostnameFromPrivateIP($address);
$port = 9000;
$command = array('template_id' => $template_id, 'params' => $params);
$_command = $command = json_encode($command);
$length = strlen($_command);
// Connect to socket
if (!$sock = fsockopen($address, $port, $errNo, $errStr)) {
$this->mailError("Command Failed - " . $hostname, "Failed to connect to socket on " . $address . "\n\n" . $errStr . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
return false;
}
// Write command to socket
while (true) {
// Try and write data to socket
$sent = fwrite($sock, $_command, $length);
// If it failed, error out
if ($sent === false) {
$this->mailError("Command Failed - " . $hostname, "Failed to write command to socket on " . $address . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
fclose($sock);
return false;
}
// If there is data left to send, try again
if ($sent < $length) {
$_command = substr($_command, $sent);
$length -= $sent;
continue;
}
// If we get here the write operation was successful
break;
}
// Read back from socket and close it
$out = fread($sock, 1024);
fclose($sock);
// Test the response from the server
if ($out === false) {
$this->mailError("Command Failed - " . $hostname, "Failed to read from socket on " . $address . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
return false;
} else if (($out = trim($out)) !== "ok") {
$this->mailError("Command Failed - " . $hostname, "Message received back from socket was '" . $out . "' on " . $address . "\n\nCommand:\n\n" . $command. "\n" . $this->functionTraceback());
return false;
} else {
return true;
}
}

Categories