socket_write: How to send multiple packets without closing the connection? - php

I have this code:
$requestCount = 0;
$maxRequestCount = 10;
$ip = "192.168.0.100";
$port = 10000;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$connect = socket_connect($socket, $ip, $port);
while(true){
if($requestCount == $maxRequestCount){break;}
$write = socket_write($socket, $getHTTP, strlen($getHTTP));
echo "Sending TCP message... OK (lenght = $write).<br>";
$out = '';
while($out = socket_read($socket, 65536)){echo "Reading response... OK (lenght = ". strlen($out).")<br>";}
echo "<br>";
usleep(100);
$requestCount++;
}
socket_close($socket);
When the first request is made the connection is already closed (FYN, ACK).
How do i send 10 packets and then the connection is closed?

You can't. Just like a real-world conversation, there is no way to force somebody who isn't interested to keep listening. In the same way, you can't stop the computer on the other end of your socket from closing it.
Judging from variable names in your code, it looks like you're sending HTTP requests (just on a different port). HTTP servers have the option of closing the connection after they respond to the first request they get in that connection. That's what appears to be happening here. You will have to create a new socket and reconnect to send each request.
Another note: TCP doesn't have "packets". It is a stream oriented connection. I know that sounds like a pedantic difference, but it doesn't make sense to ask how you would "send multiple packets without closing the connection", because you don't get to control how TCP sends your messages.

From the packet capture it can be seen that you send 342 bytes to the peer (line 4) and then the peer responds with 1446 bytes (line 6) and after that closes the connection (FIN in line 7). From then on the server will not accept more data from the client and thus any attempts to send more data will be rejected with RST.
I don't know what you are trying to achieve, but since the server closes the connection before the client is done sending the data there is probably some error. You might get more details from the servers response or it might simply be a protocol validation, i.e. the client does not speak the same protocol as the server or not in a proper way. For instance if you would try to use your code to speak with an HTTP server it would be simply wrong because you don't care about keep-alive, body length etc.

Probably the remote end closed the connection.
Probably it's because you have to control what socket_write returns. You have no warranty that socket_write will write your whole buffer at once. If socket_write return 8142 (for example), you have to cut your buffer $getHTTP = substr($getHTTP, 8142); and try a socket_write again. If socket_write(...) === false there is an error and the connection is closed, you have to test it too.

Related

PHP socket server, check if client is alive

I have a php server listening for 1 c# client.
When a connection is established, it is kept alive until client sends the command "quit" which kills the PHP server.
But when the c# client disconnects without the "quit" command (ie : clicking the close (x) button in the windows form) the server just keep listening, and can't receive any other connection from that client.
Is there a way to check from the server side (PHP) if connection is still alive with client?
My php server code is based on example1 of: http://php.net/manual/en/sockets.examples.php
If someone is interested in reproducing the bug/error behavior, paste code from example1 : http://php.net/manual/en/sockets.examples.php, connect by telnet from a remote client in lan, unplug client wire... php server will hang around forever, no new connection is accepted.
In your loop, you need to check the return value of socket_read(). If it returns FALSE, then there was a read error (which can be caused by the remote host closing the connection). The example code in the link you provided covers this case.
If you need to gracefully handle certain error states, you can always check the socket error code using socket_last_error() -- this note decribes the possible codes.
Edit:
When using putty for telnet, if i close with X button, connecion is closed properly in PHP, but if i unplug the ethernet wire of the putty machine, PHP server just hangs around.
The reason that the connection is closed when killing PuTTY is that PuTTY closes its open connection(s) when exiting. This causes socket_read() to return with an error code (I believe ECONNRESET). If you pull the network cable, it doesn't have a chance to do that.
Depending on how your network is configured, the TCP connection should eventually fail. You can attempt to control the timeout by setting SO_RCVTIMEO with socket_set_option(), but this doesn't always work on all platforms (I'm looking at you, WinSock).
Alternatively, you can roll your own polling loop using socket_select() with a reasonable timeout. If none of the connected sockets have data to send after your timeout, then kill the server.
I did this by checking if the socket_select() $read array includes the connection in question but socket_read() data is empty (twice).
It seems that socket_select() adds disconnected clients to the $read array and socket_read() gives an empty string '' when trying to read the disconnected client's data.
The following code will show the state of connection.
$address='example.com';
$port = '9065';
if (isset($port) && ($socket=socket_create(AF_INET, SOCK_STREAM, SOL_TCP))
&& (socket_connect($socket, $address, $port))) {
$text="Connection successful on IP $address, port $port";
socket_close($socket);
}
else {
$text='Unable to connect<pre>'.socket_strerror(socket_last_error()).'</pre>';
}
echo $text;
As Michael Dodd says, using socket_select() can have all sockets that have received data plus those they have closed. Also you can have all those that their buffer can accept data for transmission, and / or those that have raised some exception. Do have this info, a (separate) copy of sockets array must be placed in 2nd and / 3d parameter of function.
(if not needed, this function must have $null as a variable equal to null, not just null)
The following example shows how to read data and test sockets if they are still open. I am using this code for sockets that have been created by socket_accept() on a listening socket, and it is working without problems. Instead of using socket_recv, socket_read can be used.
//all sockets created have been inserted to an array $socketArray
//get a copy of the sockets array
$tempArray = $socketArray;
//this command will remove from $tempArray all sockets that have no data and are alive, with a timeout of 0 sec, 100 msec
socket_select($tempArray, $null, $null, 0, 100);
if (count($tempArray)) {
//if we have some sockets in the array
foreach($tempArray as $socket) {
//read some data
$count = socket_recv($socket, $socketData, 1024, 0);
if ($count) {
//your code to do what you want with $socketData
} else {
//find socket position in initial socket array
$index = array_search($socket, $socketArray);
//if found, remove it from array and close the socket
if ($index !== false) {
array_splice($socketArray, $index, 1);
socket_close($socket);
}
}
}
}

PHP socket not working properly

I have established a server to listen to incoming requests through port 1122 on my 127.0.0.1
Here is my code :
<?php
// set some variables
$host = "127.0.0.1";
$port = 1122;
// don't timeout!
//Since this is a server, it's also a good idea to use the set_time_limit()
//function to ensure that PHP doesn't time out and die() while waiting for
//incoming client connections.
set_time_limit(0);
// create socket
// The AF_INET parameter specifies the domain, while the SOCK_STREAM
// parameter tells the function what type of socket
// to create (in this case, TCP).
//If you wanted to create a UDP socket, you could use the
//following line of code instead:socket_create(AF_INET, SOCK_DGRAM, 0)
$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create
socket\n");
// bind socket to port
//Once a socket handle has been created, the next step is to attach,
//or "bind", it to the specified address and port.
//This is accomplished via the socket_bind() function.
$result = socket_bind($socket, $host, $port) or die("Could not bind to
socket\n");
// start listening for connections
//With the socket created and bound to a port, it's time to start listening
//for incoming connections. PHP allows you to set the socket up as a listener
//via its socket_listen() function, which also allows you to specify the number
//of queued connections to allow as a second parameter (here 3)
$result = socket_listen($socket, 3) or die("Could not set up socket
listener\n");
// accept incoming connections
// spawn another socket to handle communication
//Once a client connection is received, the socket_accept() function springs
//into action, accepting the connection request and spawning another
//child socket to handle messaging between the client and the server.
//This child socket will now be used for all subsequent communication
//between the client and server.
$spawn = socket_accept($socket) or die("Could not accept incoming
connection\n");
// read client input
//With a connection established, the server now waits for the client
//to send it some input - this input is read via the socket_read() function,
//and assigned to the PHP variable $input.
//The second parameter to socket_read() specifies the number of bytes of input to read -
//you can use this to limit the size of the data stream read from the client.
//Note that the socket_read() function continues to read data from the client
//until it encounters a carriage return (\n), a tab (\t) or a \0 character.
//This character as treated as the end-of-input character, and triggers
//the next line of the PHP script.
$input = socket_read($spawn, 1024) or die("Could not read input\n");
// clean up input string
//The server now must now process the data sent by the client - in this example,
//this processing merely involves reversing the input string
//and sending it back to the client.
//This is accomplished via the socket_write() function, which makes it possible
//to send a data stream back to the client via the communication socket.
//The socket_write() function needs three parameters: a reference to the socket,
//the string to be written to it, and the number of bytes to be written.
$input = trim($input);
// reverse client input and send back
$output = strrev($input) . "\n";
socket_write($spawn, $output, strlen ($output)) or die("Could not write
output\n");
// close sockets
//Once the output has been sent back to the client, both generated sockets
//are terminated via the socket_close() function.
socket_close($spawn);
socket_close($socket);
?>
this will listen to the client and return a reversed value of the inout.
The code will not give any errors. in fact when I go to localhost/socket.php it will keep loading.
Now I open a telnet connection using terraterm and a white page appears, If I input any value or strike any key the connection will be lost.
Any idea why this is happening ?
Kind Regards
The problem is probably that your tera term is using the "character mode" which means that every character typed is directly send to the server.
This causes the socket_read to immediately return with just one character read.
You could set the PHP_NORMAL_READ when calling socket_read:
$input = socket_read($spawn, 1024, PHP_NORMAL_READ)
or die("Could not read input\n");
Without that flag socket_read does not read until a \n is written.
Otherwise it behaves normally. The script doesn't write any output to the browser so the browser just waits until some timeout sets in (your Webserver might terminate the PHP-script because nothing is written).

How do I generate a connection reset programatically?

I'm sure you've seen the "the connection was reset" message displayed when trying to browse web pages. (The text is from Firefox, other browsers differ.)
I need to generate that message/error/condition on demand, to test workarounds.
So, how do I generate that condition programmatically? (How to generate a TCP RST from PHP -- or one of the other web-app languages?)
Caveats and Conditions:
It cannot be a general IP block. The test client must still be able to see the test server when not triggering the condition.
Ideally, it would be done at the web-application level (Python, PHP, Coldfusion, Javascript, etc.). Access to routers is problematic. Access to Apache config is a pain.
Ideally, it would be triggered by fetching a specific web-page.
Bonus if it works on a standard, commercial web host.
Update:
Sending RST is not enough to cause this condition. See my partial answer, below.
I've a solution that works on a local machine, Now need to get it working on a remote host.
I would recommend doing this via a custom socket via CLI as messing with the apache process could be messy:
#!/usr/bin/php -q
<?php
set_time_limit (0);
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
socket_bind($sock, '1.1.1.1', 8081) or die('Could not bind to address');
socket_listen($sock);
$client = socket_accept($sock);
sleep(1);
$pid = getmypid();
exec("kill -9 $pid");
?>
This will generate the desired error in Firefox as the connection is closed before read.
If you feel insanely daring, you could throw this into a web script but I wouldn't even venture trying that unless you own the box and know what you're doing admin wise.
I believe you need to close the low-level socket fairly abruptly. You won't be able to do it from Javascript. For the other languages you'll generally need to get a handle on the underlying socket object and close() it manually.
I also doubt you can do this through Apache since it is Apache and not your application holding the socket. At best your efforts are likely to generate a HTTP 500 error which is not what you're after.
Update:
This script worked well enough to test our connection-reset workaround, so it's a partial answer.
If someone comes up with the full solution that works on a remote host, I'll gladly mark that as the answer.
The following script works every time when running and tested on the same machine. But when running on a remote host, the browser gets the following last 3 packets:
Source Dest Protocol Info
<server> <client> TCP 8081 > 1835 [RST] Seq=2 Len=0
<server> <client> TCP 8081 > 1835 [RST] Seq=2 Len=0
<server> <client> TCP http > 1834 [ACK] Seq=34 Ack=1 Win=6756 Len=0
As you can see, the RST flag is set and sent. But Firefox fails silently with a blank page -- no messages of any kind.
Script:
<?php
$time_lim = 30;
$listen_port = 8081;
echo
'<h1>Testing generation of a connection reset condition.</h1>
<p><a target="_blank" href="http://' .$_SERVER["HTTP_HOST"]. ':' .$listen_port. '/">
Click here to load page that gets reset. You have ' . $time_lim . ' seconds.</a>
</p>
'
;
flush ();
?>
<?php
//-- Warning! If the script blocks, below, this is not counted against the time limit.
set_time_limit ($time_lim);
$socket = #socket_create_listen ($listen_port);
if (!$socket) {
print "Failed to create socket!\n";
exit;
}
socket_set_nonblock ($socket); //-- Needed, or else script executes until a client interacts with the socket.
while (true) {
//-- Use # to suppress warnings. Exception handling didn't work.
$client = #socket_accept ($socket);
if ($client)
break;
}
/*--- If l_onoff is non-zero and l_linger is zero, all the unsent data will be
discarded and RST (reset) is sent to the peer in the case of a connection-
oriented socket.
*/
$linger = array ('l_linger' => 0, 'l_onoff' => 1);
socket_set_option ($socket, SOL_SOCKET, SO_LINGER, $linger);
//--- If we just close, the Browser gets the RST flag but fails silently (completely blank).
socket_close ($socket);
echo "<p>Done.</p>";
?>

Correct usage of socket_select()

What is the correct way to use socket_select within PHP to send and receive data?
I have a connection to the server that allows for both TCP & UDP packet connections, I am utilizing both. Within these connections I'm both sending and receiving packets on the same port, but the TCP packet will be sent on one port (29999) and UDP will be sent on another port (30000). The transmission type will be that of AF_INET. The IP address will be loopback 127.0.0.1.
I have many questions on how to create a socket connection within this scenario. For example, is it better to use socket_create_pair to make the connection, or use just socket_create followed by socket_connect, and then implement socket_select?
There is a chance that no data will be sent from the server to the client, and it is up to the client to maintain the connection. This will be done by utilizing the time out function within the socket_select call. Should no data be sent within the time limit, the socket_select function will break and a keep alive packet can then be sent. The following script is of the client.
// Create
$TCP = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$UDP = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
// Misc
$isAlive = TRUE;
$UDPPort = 30000;
define('ISP_ISI', 1);
// Connect
socket_connect($TCP, '127.0.0.1', 29999);
socket_connect($UDP, '127.0.0.1', $UDPPort);
// Construct Parameters
$recv = array($TCP, $UDP);
$null = NULL;
// Make The Packet to Send.
$packet = pack('CCCxSSxCSa16a16', 44, ISP_ISI, 1, $UDPPort, 0, '!', 0, 'AdminPass', 'SocketSelect');
// Send ISI (InSim Init) Packet
socket_write($TCP, $packet);
/* Main Program Loop */
while ($isAlive == TRUE)
{
// Socket Select
$sock = socket_select($recv, $null, $null, 5);
// Check Status
if ($sock === FALSE)
$isAlive = FALSE; # Error
else if ($sock > 0)
# How does one check to find what socket changed?
else
# Something else happed, don't know what as it's not in the documentation, Could this be our timeout getting tripped?
}
I'm a bit confused - you seem to be trying to deal with asynchronous requests coming in via 2 sockets but both are acting as clients? This is a very unusual scenario. To be trying to implement them using different protocols (tcp and udp) is even odder (H323 VOIP is the only applciation I know of which does this). A quick google suggests you are trying to write a client for LFS - but why do you need a TCP and UDP client running at the same time? (BTW they publish suitable PHP client code on their Wiki at http://en.lfsmanual.net )
The socket which has data waiting to be read will be in the $recv array after the call to socket_select() (i.e. the array is trimmed down and needs to be repopulated before the next iteration of socket_select()).
If socket_select returns 0 it just means that the sockets are non-blocking and none of them have any data available.
HTH
C.

PHP: socket listen problem

Here is my code:
<?php
$host = "127.0.0.1";
$portListen = 1234;
$portSend = 1240;
// create socket
$sSender = socket_create(AF_INET, SOCK_STREAM, 0);
socket_connect($sSender, $host, $portListen);
socket_write($sSender, "test", strlen ("test"));
$sListen = socket_create(AF_INET, SOCK_STREAM, 0);
socket_set_option($sListen, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($sListen, $host, $portSend);
socket_listen($sListen,1);
$dataSock = socket_accept($sListen);
echo socket_read($dataSock, 3, PHP_NORMAL_READ);
// close sockets
socket_close($sSender);
socket_close($sListen);
?>
I send "test" to another application, it receives, and send back "ack". Problem is, I can only do it once. If I refresh, I get the address is already used error. I tried the solutions suggested on php.net but to no avail. Trying to socket_shutdown() before socket_close() only give me not connected warning, and upon refreshing will give me a never ending loading.
From what I understand the reason socket is not immediately closed is because there is still data in the buffer. But as you can see I explicitly state to listen to only 1 connection. Plus I am only sending 3 characters from my application and reading 3 in this script.
What am I doing wrong?
edit: The reason I'm using 2 sockets is because I cannot listen() after write() which give me socket is already connected error. Skipping listen() and going straight for read() after write() give me invalid argument error.
I see, after having a few hours sleep and re-analyzing my code and the documentations, I managed to fix everything. You guys are right, 1 socket is indeed enough and the correct way:
<?php
$host = "127.0.0.1";
$portListen = 1234;
$sSender = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socket\n");
socket_connect($sSender, $host, $portListen) or die("Could not connect\n");
socket_write($sSender, "test", strlen ("test")) or die("Could not write output\n");
echo socket_read($sSender, 3, PHP_NORMAL_READ);
socket_close($sSender);
?>
So simple!
After a connection is closed, the socket enters a linger state so that if (during the close) packets were lost and retransmitted, the response would be a clean acknowledgement instead of RST (reset) indicating no such socket was open. It's part of the TCP specification, you can't make it stop happening.
Listen(1) doesn't mean accept only one connection, it means maintain a queue of up 1 connections waiting for an application to accept() them. So as soon as you accept the first, the socket is ready to listen for more.
Like everybody else, I'm wondering why the odd design, but I assume it's a boiled-down example that presents your problem and doesn't necessarily present your real plan.
Why do you need to create 2 sockets for read and write? It looks like an odd design. Client apps usually open a socket connection to the server, then send a request and read the server's response on the same socket.
Also, creating a listening socket (iow a server) won't scale past any firewall or NAT gateway.
Answer to yor comment: No need to listen. just read (possibly blocking operation if your server hasn't replied yet).

Categories