I am using fsockopen() to send data via TCP to a remote host:
$s = fsockopen($host, $port);
fwrite($s, $data);
fclose($s);
How can I detect afterwards if the connection was closed (with a FIN) or aborted (with a RST) by the remote host?
According to the documentation socket_last_error() and socket_send() can be helpful for you:
socket_send() returns the number of bytes sent, or FALSE on error.
socket_last_error() returns the last error on the socket
I believe, that PHP is able to detect if the connection was closed, however not sure about this.
Related
I have a PHP server-like script that runs indefinitely. It connects to APNS (Apple Push Notification Service) with a stream_socket_client object in PHP.
The problem is that while this works most of the time, I cannot figure out how to validate if the connection to APNS is available when I want to send a push notification. The script receives incoming connections and copies the content to APNS.
When it doesn't work, the script reconnects (the return value of stream_copy_to_stream is 0 on 0 bytes written, which triggers reconnect) and then the next incoming token will succeed, but it logs the following and of course does not deliver the message:
PHP Warning: stream_copy_to_stream(): SSL: An established connection was
aborted by the software in your host machine.
Is there any way to detect the above and thus reconnect to APNS before trying to write to the socket.
And doing this before trying to copy to APNS:
if (!$socketObject) {
// reconnect
}
Does not work, as the object still exists (var_dump will print a description). So how can I check if the connection is still open?
Writing anything to the socket that is not a valid token and payload will cause APNS to disconnect.
The code that performs the writing looks like this:
function writeToAPNS($client) {
return stream_copy_to_stream ( $client, $this->connectionAPNS );
}
Where $client is returned from stream_socket_accept ( $this->socket, -1 ) and $this->socket is a variable containing the local socket object listening for incoming connections. -1 means there is no timeout when waiting for an incoming connection. When it works this function returns the number of bytes written. When it fails it returns 0.
$this->connectionAPNS is the object returned from stream_socket_client when connecting to APNS. This is what I want to check for validity before I perform the stream_copy_to_stream function.
So basically I want to detect if $this->connectionAPNS is valid.
I tried dumping the metadata for the connection object, but it doesn't say anything about the validity, only if it's timed out, protocol type etc.
Did you see this note in the official stream_socket_client documentation?
UDP sockets will sometimes appear to have opened without an error, even if the remote host is unreachable. The error will only become apparent when you read or write data to/from the socket. The reason for this is because UDP is a "connectionless" protocol, which means that the operating system does not try to establish a link for the socket until it actually needs to send or receive data.
Please try this methiod stream_get_meta_data on your connection to try to detect a difference. I can't test it right now, but it might help you. Especially the timed_out property.
Another option to at least mute the error messages, since you are handling them, is to set an appropriate errno on the stream_socket_client.
Well, as explained by M.K., this is how UDP works. And by the way, your code works. Your problem is the warning, which is fairly easy to get rid of by adding an # to your write function.
Your code would then look like :
function writeToAPNS($client) {
return #stream_copy_to_stream ( $client, $this->connectionAPNS );
}
I have a server in PHP which binds to a port and listens to sockets. My server is started in a PHP script with:
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socket, 0, $port);
It then listens to the port:
socket_listen($socket);
When a HTTP message arrives from the client, the server reads the header:
$header = socket_read($socket_new,1024);
and then stores the connection in a Memcache storage. This works for most browsers including safari, firefox and Chrome's Canary. However, it doesn't work on chrome. The browser throws an error message:
WebSocket connection to 'ws://xyz.com:9001/chat_server.php'
failed: Error during WebSocket handshake: Incorrect
'Sec-WebSocket-Accept' header value
My version of Chrome is: Version 38.0.2125.111 m (64-bit)
We had the same issue and we could solve it by increasing the "maximum number of bytes" parameter in the socket_read() function. You can try
socket_tead($socket_new, 2048);
The reason is that websocket header in chrome sometimes is greater that 1024 bytes. So, when your server reads 1024 bytes, it does not get Sec-Websocket-Key parameter and it can not generate valid Sec-Websocket-Accept value.
You can also use fsockopen() and fread() instead of socket_read() function.
I created a socket server with 10 possible clients by using socket_accept(socket).
It is possible to connect up to 10 times now.
The problem appears after the first client submitted a message to the server which is caught by
$data = #socket_read($clients[$i]['socket'], 1024, PHP_NORMAL_READ);
Then the script tries to listen for clients and messages again by socket_accept and socket_read, but all socket_accept-requests fail with the error string "Invalid argument".
var_dump indicates that the parameter is a resource(14) of type (Socket), though.
Already connected clients can continue using the "server" script as nothing happened and remain connected. Only new clients are unable to connect and the port seems to close (no telnet and netcat requests are possible - connection refused)
Any ideas would be helpful. Thanks!
Discovered that I assumed to disconnect all sockets when the destructor of the socket handler class was called - which terminated all connections once the child process closed.
Removing the socket_shutdown() and socket_close() from the destructor solved the problem.
So Im trying to get my head wrapped around this....
I open the port
$remip = $_SERVER['SERVER_ADDR']; //Grab my server address
$fp = fsockopen($remip, 80, $errno, $errstr, 10);//Godaddy hosting only 80 and 443 ports work
//fsockopen(ip address , port, IDK, IDK, timeout delay)
so now the ports open or if not maybe some error checking to be sure
if (!$fp) { echo "$errstr ($errno)<br>\n"; exit; } //Not sure what this echos out but its clear how it stops errors
So now that the port is open any ip/client can connect on this port????
Ill assume I can now connect....
So on my client I open a socket to my server ip address port tcp connection.....
The php file includes something like
else {$out = "hello, 80\r\n"; //out specifies the string to be written , bytes to write
fwrite($fp, $out); //$fp is the handle
fclose($fp)}//close the connection
at this point ill assume that my client gets the hello written to it ..
finish up by closing the connection
Im entirely new to this so Im attempting to understand some sample code here...
So how long is this socket open for? If i want to keep this port open do i need to do a cron job to launch this file periodically.
Im 100% sure that I have got something wrong here so please set me straight.
I think you have a misconception of what fsockopen does. In your example your fsockopen does not actually open port 80 (as in opening a server socket), but it opens a client socket that connects to port 80 on the server itself. It actually does open a (client) port which gets a (not completely) random number.
After you connected using fsockopen you can send HTTP commands to the webserver such as GET /index.php
What you need to use is socket_listen() and socket_bind(). There are a few places in the docs that show you how to get PHP listening on a socket: http://www.php.net/manual/en/function.socket-listen.php
I suggest you read and try them out by simply testing then with a unix tool called netcat (nc <ip_address> <port> command normally)
I'm trying to make a simple listener on port 8195. When I try the following code block in PHP CLI conditions, it only shows 'Test' once, then hangs. If I delete the file 'votifier.run', the file designed to be the on/off switch, it still continues to hang. It never shows 'Client connected'.
Furthermore, if I try to connect to the host via Telnet on port 8195 while the script is running, I simply get a connection failed message. It's like it's looking for one connection and just not giving up.
// Set the IP and port to listen to
$address = 'localhost';
$port = 8195;
// Create a TCP Stream socket
$sock = socket_create(AF_INET, SOCK_STREAM, 0);
// Bind the socket to an address/port
socket_bind($sock, $address, $port);
// Start listening for connections
socket_listen($sock);
// Loop continuously
while ( file_exists('votifier.run') ) {
echo 'Test';
$client = socket_accept($sock);
if( $client ) {
echo 'Client connected';
// Don't hang on slow connections
socket_set_timeout($client, 5);
// Send them our version
socket_write("VOTIFIER MCWEBLINK\n");
// Read the 256 byte block
$block = socket_read($client, 256);
...
The answer:
socket_accept() will usually hang until a connection is made. If a connection attempt was made, the script would continue, but because the socket was being created on localhost, it would only accept connections to it from localhost.
The fix is to use your external IP rather than 'localhost' or '127.0.0.1'. Then you can Telnet to it.
I'm just guessing here, but could it be that the address you are trying to bind to should not be a hostname?
If the socket is of the AF_INET family, the address is an IP in dotted-quad notation (e.g. 127.0.0.1).
EDIT
Ok, I've taken your script and tried to reproduce your error but couldn't. There are a couple of flaws in it but none that would cause a telnet client's connection attempt to fail.
Since none of the aforementioned applies, let's go thru the checklist one by one:
sockets module loaded/compiled
localhost does resolve to 127.0.0.1
the port isn't taken by any other application running
there's no rule of any sort of firewall that would prevent communication between the telnet client and your server
the machine which you connect from is allowed to connect to the server host (try the same host if it isn't)
the file that's being checked in the while-loop does exist
you are sure that there isn't another fatal error within your script that would prevent the snippet you posted from running
These are all the possible error sources I can think of, atm. Try fixing up the minor flaws first, then go thru the checklist.
if( $client ) {
echo 'Client connected';
// Don't hang on slow connections
socket_set_option(
$client,
SOL_SOCKET,
SO_RCVTIMEO | SO_SNDTIMEO,
array('sec' => 5, 'usec' => 0)
);
// Send them our version
socket_write($client, "VOTIFIER MCWEBLINK\n");
^^^^^^^
// Read the 256 byte block
$block = socket_read($client, 256);
You should be using threads. If the client never sends anything your code will block in the read() method. Each accepted socket should be completely handled in a new thread.
You may want to check this:
PHP Votifier example for Minecraft Topsites
It explains how the code works, it's the basic function that makes the encryption, fills up the 256 blank spaces and sends the packet too. You can work a little with it as you may want to improve it.
You can see a live demo of the running php for the plugin here: http://topg.org/test_votifier