I'm very new to PHP so this might be something simple. Anyway, I'm trying to create 2-way communication between a PHP script (activated by a web client) and a local process (written in C++). The PHP script should send some information to the C++ process and then wait for a response. My problem is that the only way to set up this kind of communication seems to be to use socket_bind, but when I do, it fails with the 'address already in use' error. The socket file in question, /tmp/sock, has already been created by the C++ process, which is running continuously (it can not be launched by the PHP script). If I use socket_connect and just write something to the C++ process, that works just fine; but I need to bind before I can listen to that socket from the PHP script. Here's my code:
<?php
error_reporting(E_ALL);
/* Allow the script to hang around waiting for connections. */
set_time_limit(0);
/* Turn on implicit output flushing so we see what we're getting
* as it comes in. */
ob_implicit_flush();
//Adapted from http://www.php.net/manual/en/sockets.examples.php
if (($sock = socket_create(AF_UNIX, SOCK_STREAM,0)) === false)
{
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
exit();
}
if (socket_bind($sock, '/tmp/sock') === false) //Fails here
{
echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
....
Thanks--
Your C++ and PHP codes have to mark the socket as shared (SO_REUSE_ADDR).
It turns out that what's needed is simply socket_read(). I didn't realize you could do both operations on the same socket (when it was merely connected and not bound). User hamishcool3 at PHP socket_read has a great utility function for reading from a socket until a particular character is reached (though it is probably slower than a standard byte-limited read).
Related
$socket = socket_create(AF_UNIX, SOCK_STREAM, 0);
socket_connect($socket, CLAMD_SOCKET);
socket_send($socket, 'PING', 4, 0);
socket_recv($socket, $output, 4, 0);
// check to see if $output === PONG (which it is)
$scan = 'SCAN ' . $file;
socket_send($socket, $scan, strlen($scan), 0);
socket_recv($socket, $output, 100, 0);
// $output should contain the result from the socket, but instead it's blank
// var_dump says it's a 1 character string " "
This is my original code.
However, if I close the socket after the PING command and PONG is returned (which it is), then re-open it again, the SCAN command works and it returns the result as expected.
Is this how it's supposed to work, or do I need to do something else in between the commands? Is the socket not ready for another command to be sent?
I'm not really sure what to look for, I can't find anything in the PHP manual. It does work, but I feel as if having to close the socket and re-open it is the wrong way to do things.
I can't find anything in the PHP manual.
The behavior does not depend on PHP but on the protocol spoken between the client and the server - i.e. what protocol is expected and enforced by clamd. Therefore no information about this could be found in the PHP documentation, but one would need to look into the ClamAV documentation instead.
While the clamd documentation describes the commands it unfortunately lacks the information if multiple commands are allowed. A short test (for example with netcat) shows though, that clamd closes the connection after the response to the command, so no more commands on the same connection are possible.
It does work, but I feel as if having to close the socket and re-open it is the wrong way to do things.
It is definitely not the most efficient way of doing things, but the overhead on UNIX domain sockets or even local TCP sockets for creating a new connection is low - compared to establishing a TCP connection to a remote system.
Being able to send multiple commands and therefore get multiple responses would require a clear separation on where a command and response ends though. This is kind of documented for commands, but not for responses. So the only obvious option here is to read until the end of the connection, i.e. until connection close. This design makes it impossible though to send more commands on an established connection.
This must be simple and I suspect I have disappeared down the wrong rabbit hole.
I have the apache http server running on a pi serving pages (LAMP server) exposed through our domestic router. I can get php to serve up the material I want, and thought it would be easy to get php (on the server) to talk via sockets to another machine on the local network (ie 192.168.1.73 for example)
I can get data from a web page via the server to the local machine, but cannot get the server to receive messages from the local machine. In php the call to
'''
socket_bind(...)
'''
gives the error
"unable to bind address [99]: Cannot assign requested address"
Don't know where the 99 is coming from; installed and ran ufw to open the port I'm using (would not have thought that was necessary or desirable but tried it anyway)
Any help greatly appreciated.
P
It is a PHP problem.
Implemented "quote of the day" in java, running the client on the same machine as my problematic php, and the quote server on another machine on the local network. That works fine. replace the java client with this php client:
<?php
$server_ip = '192.168.1.78';
$port = 41235;
$message = 'hello world.';
$buf = [];
$skt = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP) or die(..);
socket_sendto($skt,$message,strlen($message),0,$server_ip,$port);
// message is successfully sent and received at other end...
socket_bind($skt,$server_ip,$port)
or die("Could not bind socket\n");
// the bind fails
$bc = socket_recvfrom($skt,$buf,256,0,$server_ip,$port);
echo "Got $bc bytes back";
?>
Fails as above with error [99]
The use-case is to run a server on a machine inside a local network -lets call it insideServer, and have requests from a webpage hosted on a machine, outsideServer, responded to by code running on insideServer. Why? InsideServer's server is written in java because I like it. The right way to do this was, in the old days, to have java running on the user's machine as an applet; The modern way would be to have outsideServer run Apache Tomcat. I don't however have the luxury of doing that so how does one forward requests?
The solution is to have a PHP script in the relevant directory of outsideServer that connects via sockets to insideServer where insideServer's code can be in whatever you want. The PHP has however been problematic. This code does work however:
$insideServerIP = '192.168.1.78';
$insideServerPort = 41234;
$message = 'pretend message from web page form';
$skt = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP) or die("create failed: " . socket_last_error());
socket_connect($skt, $insideServerIP, $insideServerPort) or die("connect failed: " . socket_last_error());
socket_write($skt, $message, strlen($message));
$in = socket_read($skt, 256, PHP_BINARY_READ);
echo "$in\n";
socket_close($skt);
My research over the last few days (Arr!!!) suggests nobody knows how socket_bind() is meant to work, and it turns out you don't need it!
Please do post comments (or a better answer) if there are mistakes, but this solution works I believe.
P
I have a server online, and I wrote a PHP Code to act as a Socket Server to handle messages. The messages are coming in from devices that connect to the IP and the Port and send messages.
I found this code example that lets me handle multiple connections at the same time and manage the messages as they come in.
I have modified the code to properly parse the messages that come in from my devices.
Everything has been going well during testing. However, when we did extended testing, the PHP Socket Server would shutdown at seemingly random times and would give the error:
socket_read() failed: reason: Connection reset by Peer at line 104.
Looking at the code I am running, this is the code block for line 104, it starts at the if condition.
if (false === ($buf = socket_read($client, 115200, PHP_BINARY_READ))) {
echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($client)) . "\n";
$GLOBALS['mysqli']->close();
break 2;
}
I actually am not sure how to make sense of this error message. During our testing periods, sometimes that error would show up and shut down the Socket Server while the device is sending a message - but we are sure that the device did not send a Socket Shutdown command. At other times, when we would let the Socket Server just run through, that error would show up.
Does anyone know how to effectively keep a PHP Socket Server from shutting down and keep on running?
I know I can run a cronjob that periodically checks if the socket is being used. However, when the socket server shuts down unexpectedly, the PHP Code can't execute as it still reads that the port is still in use, even if the previous socket server instance is already down and we can no longer connect to it. It takes quite some time until the cronjob manages to put the socket back up again and accept messages - and we need the socket server to run virtually all the time.
"Connection reset by Peer" means the client disconnected unexpectedly. For instance, the user may have closed the client before it was done talking to the server.
Your server code needs to be prepared to handle errors on client sockets, and not quit when it gets an error on a client socket. When you get an error from a client socket it's appropriate to just disconnect that client.
I had to implement a communication between an application written in c++ and a web server scripted with PHP.
The basic idea was to create a socket with the c++ application, binding it and listen for the PHP connection to it.
The PHP would then send a message over TCP asking for data and the c++ would send back the answer. Single header request, single string(JSON) answer.
So far, so good.
This is the code I used for the PHP side:
<?php
error_reporting(E_ALL);
$service_port = 8080;
$address = 'localhost';
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
echo "Attempting to connect to '$address' on port '$service_port'...";
$result = socket_connect($socket, $address, $service_port);
if ($result === false) {
echo "socket_connect() failed.\nReason: ($result) " . socket_strerror(socket_last_error($socket)) . "\n";
}
$in = "request1";
$out = '';
socket_write($socket, $in, strlen($in));
$buf = 'This is my buffer.';
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";
//elaborate the $buf
?>
Now I would like to implement also an auto-update of the data, with the c++ (server side) sending data and the PHP side(client side) to collect them.
The data update should be done every minute.
Sadly I don't have much experience with web developing, so I'm kindly asking some advices on that.
The easiest thing I could do was looping the PHP code untill a known message got received. The problem I faced is that I don't get data untill the loop is over and the PHP script ends.
To overcome the problem I tried to set PHP side the socket to be non-blocking
replacing in the socket_recv the option MSG_WAITALL with MSG_DONTWAIT but nothing changed. Then I tried to break out of the PHP script, but it can't be done since I need to cycle it to get the data every second.
Plus I get another problem, during the loop cycles, the apache server the PHP is running on is getting a 503 error, Server Unavailable.
I don't know why it happens, maybe the received message buffer is full, or the script takes too many resources.
Due to my lack of experience I can't understand why.
I know there are good libraries to perform what I need but I'm working on an embedded machine so I'm limited to work with basic libraries.
How can I achieve the PHP on the Apache server to get timed data from the c++ application ? What am I overlooking ?
Thanks a lot in advance for your help.
Edit: Deleted rhetorical question.
Using php to obtain data from another process via tcp/ip will require a bit more effort compared to sending data from a php script executed via apache to another process using tcp/ip sockets.
Your php script will need to be started outside of apache and will require an infinite loop that continuously listens to the given port to read input data. The following post outlines what I believe you are after.
EDIT
In order to display the results on the web page you could use an in memory data store such as Redis. The php server application would update a data structure in redis, then the webpage onload could pull the latest value from redis and display it on screen, however this would require a page refresh.
If you require that the data be refreshed immediately once received without server postback, this will require some javascript. When a user loads the page you would initialize a websocket connection with a websocket server (people generally use node.js and socket.io for implementing this but you can code a websocket server in anything that supports the technology). The websocket server would be subscribed to a redis channel that when updated would send the new values to all connected clients. I suppose this could be done without redis as well depending on what your requirements are. The program for the websocket server could also be the server that accepts the information from your c++ program using a separate thread. When data comes in from the c++ program you could then send it directly to the clients connected via websocket.
Don't reinvent the wheel. Use a websockets. It's designed to do this async communication and you can send JSON over it just fine.
There are libraries for C++ and PHP, meaning this should be pretty easy.
I have a webserver which serves the client with an octet stream on port 20000 (it's actually a socket.io server hosted with node.js). This is running on a shared hosting account with a regular Apache server running on port 80 (this cannot be turned off, hence the socket.io server is on port 20000). Due to firewalls and such, I cannot expect the user to be able to connect to port 20000 (or any other than 80). So, how can I serve the client with the octet stream produced by the socket.io server from the Apache server (sort of like a reverse proxy)? Unfortunately I cannot use mod_proxy on my Apache server given restraints of my hosting plan. I was thinking I could do this with a PHP page that opens a socket somehow.
Update: I also have Django for Python 3 installed on my server which could be useful. Please note that the proxy cannot simply request the target page and serve it back to the client since the data has to be transferred in real time.
Re "it cannot simply serve the target page back" ... this is not true because this is all an HTTP proxy is. Most protocol proxies use a socket (versus a file or pipe) and simply copy the data from socket to socket. An HTTP proxy does the same thing except every HTTP request requires a handshake, so the proxy will require a few packets back and forth before reaching the payload. You can create an HTTP GET proxy very easily in django. A POST proxy will need extra footwork.
I am not familiar with Socket.IO, but upon researching ... how does socket.io work? ... it appears it uses only "old" HTTP features and runs everything as REST. The REST is encapsulated as a transport inside of a persistent socket.
If you were looking for an TCP or IP-level proxy within Django, its not going to happen. Your apache server, and then WSGI/CGI/whatever closes the TCP socket off from you. The only way you'll be able to access it is with sufficient permissions to those parts of the server.
Here's what I'd do ... In django make a url pattern that captures the socket.io api, and have it connect to a view that does something like the following (untested pseudo code):
import urllib2, mimetypes
from django.http import HttpResponse
def ForwardToSocketIO(request):
# Capture the URL pattern
path = request.get_full_path()
# Create a URL opener
response = urllib2.urlopen('http://localhost:20000%s' % path)
# Capture and return response
django_response = HttpResponse(response.read())
django_response['Content-Type'] = 'octet-stream'
return django_response
Hope this helps. I don't have an octet stream available so apologies for not testing.
Seems possible not impossible. I have not done it before but know the way to do but again I don't know the impact of Firewall on port openning and closing. The basic Idea about doing thing is:
Get you request from port 80 to do things and for response of that request use different port to communicate with client. It would become a tunnel to recieve request from one port and get reply from the other port. Only one thing to be properly taken care of that termination of connection as soon as possible once the purpose is resolve unless it would create memory load on the server.
With the below example you can do the above things but suggest you to use them with caution and after proper testing.
ref: Programming with Sockets through PHP
This example shows a simple talkback server. Change the address and port variables to suit your setup and execute. You may then connect to the server with a command similar to: telnet 192.168.1.53 10000 (where the address and port match your setup). Anything you type will then be output on the server side, and echoed back to you. To disconnect, enter 'quit'.
<?php
error_reporting(E_ALL);
echo "<h2>TCP/IP Connection</h2>\n";
/* Get the port for the WWW service. */
$service_port = getservbyname('www', 'tcp');
/* Get the IP address for the target host. */
$address = gethostbyname('www.example.com');
/* 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 '$service_port'...";
$result = socket_connect($socket, $address, $service_port);
if ($result === false) {
echo "socket_connect() failed.\nReason: ($result) " .
socket_strerror(socket_last_error($socket)) . "\n";
} else {
echo "OK.\n";
}
$in = "HEAD / HTTP/1.1\r\n";
$in .= "Host: www.example.com\r\n";
$in .= "Connection: Close\r\n\r\n";
$out = '';
echo "Sending HTTP HEAD request...";
socket_write($socket, $in, strlen($in));
echo "OK.\n";
echo "Reading response:\n\n";
while ($out = socket_read($socket, 2048)) {
echo $out;
}
echo "Closing socket...";
socket_close($socket);
echo "OK.\n\n";
?>