PHP socket connections: read and send data in loop - php

I have to make a function, in which I send a packet to server and then read data. After a while I sometimes require to send a data to server again using the same socket (it's required that I use the same one).
For some reason second send, which is after read doesn't send data (it returns correct number (not false), but server doesn't receive packet). If I create new socket and send data - it works.
Here is an example:
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, '192.168.1.179', 1455);
socket_set_option($socket,SOL_SOCKET, SO_RCVTIMEO, array("sec"=>5, "usec"=>0));
$this->socket_functions->send_message($socket,$sendMsg,$funcName);
$tmp = trim($this->socket_functions->read_packet($socket));
for($i =0; $i<10; $i++)
{
$tmp = trim($this->socket_functions->read_packet($socket));
$this->socket_functions->send_message($socket, 'AAAA', $funcName); //doesn't work
/*
///works
$socket2 = $this->socket_functions->create_socket();
$this->socket_functions->send_message($socket2, 'AAAA', $funcName);
$this->socket_functions->disconnect($socket2);
*/
}
Function create_socket does the same as first 3 lines so the connection data is the same. I just brought it out so you are able to see my configuration.
For read and write I use functions socket_send() and socket_write().
Read packet function:
$resultMsg = "";
while(strlen($currentData = socket_read($socket,256))==256)
{
$resultMsg .=$currentData;
}
$resultMsg.=$currentData;
if(strlen($resultMsg)>1 && strpos($resultMsg,'<')>0)
{
$resultMsg = substr($resultMsg,strpos($resultMsg,'<'));
}
return $resultMsg;
Sending packet:
function create_packet($msg, $type)
{
$msg = $type.$this->convert_data->IntToAsciiChars(strlen($msg)).$msg;
$msg = chr(0).$this->convert_data->IntToAsciiChars(strlen($msg)).$msg;
return $msg;
}
function send_message($socket,$msg,$type)
{
$packet = $this->create_packet($msg,$type);
$res = socket_send($socket,$packet,strlen($packet),0);
if($res === false) return false;
return true;
}
EDIT
I did more testing and found out, that this only occurs, if the server, to which I'm connected keeps sending data. As in - the server sends multiple packets in row. If I try to send packet before the server has sent all of them, the packet isn't received. Does anyone knows why is this happening? I have desktop application, which does the same thing (sends data using the same socket while server is sending data), but it works there. Is there something specific?

I finally managed to fix this. After testing I found out, if I used send n+1 times to send the message, where n is amount of times read was called , I was able to send a message then.
I really don't know why this worked - maybe someone here knows?

Related

PHP-Websocket: Problems with socket_recv() and socket_getpeername()

I'm using PHP Websocket and sometimes, I get the following both warnings.
Warning-1:
socket_recv(): An existing connection was forcibly closed by the remote host
Warning-2:
socket_getpeername(): unable to retrieve peer name [107]: Transport endpoint is not connected
I send messages to the Websocket by another PHP-Script. This PHP-Script sends a message and if it's finised, the Socket-Call ends.
So this Warning is probably correct, because the "endpoint" (= the PHP-Script) is no loger connected.
But this Warning is not beautiful..
So this is the following Code for Websocket, which is received the messages:
define('HOST_NAME',$host_ip);
define('PORT',$socket_port);
$null = NULL;
$socketHandler = new SocketHandler();
$socketResource = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($socketResource, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($socketResource, 0, PORT);
socket_listen($socketResource);
$clientSocketArray = array($socketResource);
while (true) {
$newSocketArray = $clientSocketArray;
socket_select($newSocketArray, $null, $null, 0, 10);
if (in_array($socketResource, $newSocketArray)) {
$newSocket = socket_accept($socketResource);
$clientSocketArray[] = $newSocket;
$header = socket_read($newSocket, 1024);
$socketHandler->doHandshake($header, $newSocket, HOST_NAME, PORT);
socket_getpeername($newSocket, $client_ip_address);
$newSocketIndex = array_search($socketResource, $newSocketArray);
unset($newSocketArray[$newSocketIndex]);
}
foreach ($newSocketArray as $newSocketArrayResource) {
while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1){
$socketMessage = $socketHandler->unseal($socketData);
$messageObj = json_decode($socketMessage);
break 2;
}
$socketData = #socket_read($newSocketArrayResource, 1024, PHP_NORMAL_READ);
if ($socketData === false) {
socket_getpeername($newSocketArrayResource, $client_ip_address);
$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
unset($clientSocketArray[$newSocketIndex]);
}
}
}
socket_close($socketResource);
The command, which produces the Warning-1
socket_recv(): An existing connection was forcibly closed by the remote host
is:
while(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1)
In the PHP-Manual there is mentioned to get the return-value of socket_recv(). But in the examples there is only shown to execute socket_recv() once, by using if().
But I use socket_recv() within the loop-header.
So my question:
How do I check return-value in my case - by using socket_recv() within a loop-header?
Warning-2
socket_getpeername(): unable to retrieve peer name [107]: Transport endpoint is not connected
was produced with the following code:
socket_getpeername($newSocketArrayResource, $client_ip_address);
My question is:
What have I to do in order not to execute socket_getpeername(), if the client is no longer connected?
Maybe someone can help me to fix these Warnings. It's very difficult do watch, because these Warings do not always appear..
Bye,
Chris
How do I check return-value in my case - by using socket_recv() within a loop-header?
you can capture the return value of socket_recv and check it's value, but it's important that you do it in the correct order, and you can dictate in which order php does it, by using ()'s, eg:
while(($bytes_recieved=socket_recv($newSocketArrayResource, $socketData, 1024, 0)) >= 1)
{
// now you can check it inside the loop, it's $bytes_recieved
}
// you can also check it outside the loop, it's still in $bytes_recieved
(however, note, if you place the ()'s wrong, it will instead become the bool result of >=, for example, this is wrong: while($bytes_recieved=(socket_recv($newSocketArrayResource, $socketData, 1024, 0) >= 1)) , with this, $bytes_recieved would instead become the bool result of >=.)
What have I to do in order not to execute socket_getpeername(), if the client is no longer connected?
well, if you apply the above patch, i think you can check if $bytes_recieved is true-ish, eg
if (!!$bytes_recieved) {
socket_getpeername($newSocketArrayResource, $client_ip_address);
$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
unset($clientSocketArray[$newSocketIndex]);
}
doing !! will convert it to bool(true-ish), and it's probably true-ish if the client is still connected, and probably false-ish otherwise. - although, this may get a false positive if the client didn't send anything any bytes a while, so it might be safer to check if it's directly false instead of false-ish, which you can check by using ===, eg
if ($bytes_recieved === false) {
socket_getpeername($newSocketArrayResource, $client_ip_address);
$newSocketIndex = array_search($newSocketArrayResource, $clientSocketArray);
unset($clientSocketArray[$newSocketIndex]);
}
... btw, i don't know for sure, but it looks like your code here just randomly selects a client sharing $newSocketArrayResource 's ip address, instead of choosing $newSocketArrayResource specifically. if your server never receive more than 1 connection per ip address, i guess this is OK (still a shitty design choice, imo, but not directly bugged), but i think you should select clients by the resource id, rather than the ip address, then your server would be able to support more than 1 connection per ip address. i think you should set the key to the resource id, and instead do:
if ($bytes_recieved === false) {
unset($clientSocketArray[(int)$newSocketArrayResource]);
}
... and lastly, looks like you have a resource leak here, just unsetting it will leave you with memory leaks (and handle leaks), i think you want:
if ($bytes_recieved === false) {
unset($clientSocketArray[(int)$newSocketArrayResource]);
socket_close($clientSocketArray);
}
that will leave you without memory/handle leaks.

PHP - Cant connect to RETS via PHRETS

I am trying to get all my MLS listing via PHP using PHRETS which I downloaded from here:
https://github.com/dangodev/PHRETS-Example/blob/master/lib/phrets.php
and I used this example to download my listings into a csv format:
https://github.com/troydavisson/PHRETS/wiki/Connect,%20download%20listing%20data%20in%20CSV%20format,%20disconnect
I got the RETS URL, username and password from my MLS board, but I still can’t connect.
My code returns false when call the PHRETS library here:
require_once("phrets.php");
// start rets connection
$rets = new phRETS;
echo "+ Connecting to {$rets_login_url} as {$rets_username}<br>\n";
$connect = $rets->Connect($rets_login_url, $rets_username, $rets_password);
if ($connect) {
echo " + Connected<br>\n";
}
else {
echo " + Not connected:<br>\n";
print_r($rets->Error());
exit;
}
When I goto to library and look at the method Connect, that code returns false here:
// make request to Login transaction
$result = $this->RETSRequest($this->capability_url['Login']);
if (!$result) {
return false;
}
And when I look at my RETSRequest Method, it returns false here because the response code is 0 and not 200
if ($response_code != 200) {
$this->set_error_info("http", $response_code, $response_body);
return false;
}
and here is where its trying to connect:
if ($this->ua_auth == true) {
$session_id_to_calculate_with = "";
// calculate RETS-UA-Authorization header
$ua_a1 = md5($this->static_headers['User-Agent'] .':'. $this->ua_pwd);
$session_id_to_calculate_with = ($this->use_interealty_ua_auth == true) ? "" : $this->session_id;
$ua_dig_resp = md5(trim($ua_a1) .':'. trim($this->request_id) .':'. trim($session_id_to_calculate_with) .':'. trim($this->static_headers['RETS-Version']));
$request_headers .= "RETS-UA-Authorization: Digest {$ua_dig_resp}\r\n";
}
$this->last_request_url = $request_url;
curl_setopt($this->ch, CURLOPT_URL, $request_url);
curl_setopt($this->ch, CURLOPT_HTTPHEADER, array(trim($request_headers)));
// do it
$response_body = curl_exec($this->ch);
$response_code = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
Why can’t I connect?
I was able to login via http://retsmd.com with the url, username and password. I really need to get my listings in the format of CSV.
PLEASE HELP
I do have curl installed on my server, I checked with this method:
Check to see if cURL is installed locally?
A response code of 0 usually indicates that your server failed to open a connection with the server, likely due to firewall issues between you and them. Some RETS servers still run on a port 6103, so if your server (hosting company, etc.) prevent outbound connections from being opened on that port, that could be the cause for what you're seeing.
I'd recommend trying your code example from a different server or computer that doesn't have any connection restrictions. You could also try https://retsmd.com/auth as a way to verify that the credentials you've been given will work (assuming your local environment has what it needs for PHRETS to run).
Adding to troydavisson's answer,
You can include the following extra line of code to get the log of your connection for checking the issue.
// start rets connection
$rets = new phRETS;
$rets->SetParam('debug_mode', true);//extra line of code to get log
You will get full log in the rets_debug.txt file.

Incorrect Client IP in PHP

I am getting a weird result for the client IP in PHP in some cases.
Result in Most Cases (Expected Result) :
192.123.132.123
Erroneous Result Type 1:
for="192.123.132.123"
Erroneous Result Type 2:
for="192.123.132.123:1232"
Code for getting the IP:
<?php
function getIP(){
$ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '-';
$proxy = false;
if (!empty($_SERVER['HTTP_VIA']) || !empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$proxy = true;
} elseif (!empty($_SERVER['REMOTE_HOST'])) {
$aProxyHosts = array('proxy','cache','inktomi');
foreach ($aProxyHosts as $proxyName) {
if (strpos($_SERVER['REMOTE_HOST'], $proxyName) !== false) {
$proxy = true;
break;
}
}
}
// Has the viewer come via an HTTP proxy?
if ($proxy) {
// Try to find the "real" IP address the viewer has come from
$aHeaders = array('HTTP_FORWARDED','HTTP_FORWARDED_FOR','HTTP_X_FORWARDED','HTTP_X_FORWARDED_FOR','HTTP_CLIENT_IP');
foreach ($aHeaders as $header) {
if (!empty($_SERVER[$header])) {
$ip = $_SERVER[$header];
break;
}
}
}
if (!empty($ip)) {
// The "remote IP" may be a list, ensure that
// only the last item is used in that case
$ip = explode(',', $ip);
$ip = trim($ip[count($ip) - 1]);
}
return $ip;
}
?>
I know that I can clean the result to get the correct value (IP) but I am puzzled at why is this happening in the first place.
PS: 192.123.132.123 is an arbitrary IP used to explain the issue.
You're reading arbitrary HTTP headers... not all of them contain purely the IP, some are in the form of for=... and some include the port as well.
Using any HTTP header instead $_SERVER['REMOTE_ADDR'] means you're allowing anyone to mask/fake their IP address by simply sending an HTTP header. You should be perfectly aware of where such headers may be set, which usually means you know they're set by a proxy you control. In this case you obviously don't know where those headers are coming from, so you should not use them.
If you decide to use an HTTP header, you should know which one exactly you want to read and what format it's in. If its format is for=..., then parse that format correctly.

Creating a client for a chat in PHP

I'm trying to create a PHP chat, so I have server.php that starts the server on the terminal, which is listen to client connections:
<?php
function chat_leave( $sock, $chat_id = 0 )
{
if( $chat_room_id[ $chat_id ] )
{
unset( $chat_room_id[ $chat_id ] );
return true;
}
socket_close($sock);
return false;
}
function client( $input )
{
/*
Simple php udp socket client
*/
//Reduce errors
error_reporting(~E_WARNING);
$server = '127.0.0.1';
$port = 9999;
if(!($sock = socket_create(AF_INET, SOCK_DGRAM, 0)))
{
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("Couldn't create socket: [$errorcode] $errormsg \n");
}
//Communication loop
while(1)
{
//Send the message to the server
if( ! socket_sendto($sock, $input , strlen($input) , 0 , $server , $port))
{
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("Could not send data: [$errorcode] $errormsg \n");
}
//Now receive reply from server and print it
if(socket_recv ( $sock , $reply , 2045 , MSG_WAITALL ) === FALSE)
{
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("Could not receive data: [$errorcode] $errormsg \n");
}
return $reply;
}
}
/*
* chat_join
* a new user joins the chat
* #username: String
* #password: String
*
* add a new listener to the server
*
*/
function chat_join( $username = "", $password = "" )
{
$users = array(
"batman" => "batman123",
"robin" => "robin123",
"joe" => "joe123"
);
if( $users[$username] == $password )
{
return true;
}
return false;
}
function main()
{
$chat_room_id = array();
$username = stripslashes( $_POST['username'] );
$password = stripslashes( $_POST['password'] );
$action = stripslashes( $_POST['action'] );
$port = intval( $_POST['port'] );
$domain = stripslashes( $_POST['domain'] );
$chat_id = intval( $_POST['chat_room_id'] );
if( strcmp( $action, "login" ) == 0 )
{
$status = chat_join( $username, $password );
if( $status )
{
$chat_room_id[] = $chat_id;
echo json_encode( $status );
}
}
else if( strcmp( $action, "chat" ) == 0 )
{
$msg = stripslashes( $_POST['message'] );
// take the message, send through the client
$reply = client( $msg );
echo json_encode( $reply );
}
else if( strcmp( $action, "logout") == 0 )
{
}
else
{
echo json_encode( false );
}
return;
}
main();
?>
The function client() is the code I have from a client.php file, which when I execute on the terminal, is able to send and receive messages from the server.php. Now I would like to use my main.php file, so once the user is logged in he will send messages to the server, which will reply back the messages that user haven't seen.
When I run server.php and client.php from two different terminals, I'm able to send and receive messages, however I would like to do that using main.php, transform that reply message into a JSON object and send back to the html page where it will get appended to a textarea box.
My problem is: how can I get the reply that client.php received and send it back to the html page?
When I execute it on the terminal, I have:
Enter a message to send : hello
Reply : hello
I use AJAX to send the user input in the chat, so I wanted to be able to take that message, and send it to the server, which I started on the terminal and take the reply back and forward to the webpage and append that to the text box area.
How can I accomplish that? Should I start client.php as a service through main.php? Or should I use the client($input) function to send a message and then return what it sends, back?
However, I wanted that client to be running until the use logs out, because other clients may connect to the chat. How can I accomplish that is kind of fuzzy for me. The code in client( $input ) is the same as in client.php.
Sorry for off-topic, but if you can, it would be better to use XMPP ready solution like ejabberd server with http-bind module. Sure, there is some cons it such solution, but cons are greater. Just look in this solution, maybe it will solve your problem with low cost.
see related answer with brief desc on XMPP solution
I think I understand what's going on. Sounds like you might be missing a listener? Usually chat programs have client-side ajax that checks or "listens" for messages for a particular user at regular intervals.
For example, if someone left a message for user x in your database (or wherever you're storing messages), the you might have some javascript that calls a php script every second to see if there are any messages on the server for user x. If there are, you can echo the message or messages back and received them via your ajax callback function.
If you're familiar with jQuery, check out the $.get method: http://api.jquery.com/jQuery.get/
As i understand your question, you want to send a message from the client to the server and as soon as this message gets to the server it will reply to all clients... i'm correct???
I do some chat like using nodejs and other javascripts tech... and must say a great option here is using web sockets. Realize that browser support is limited, but since you not specified what browsers need to run this i think a great way to go.
Check this related links:
How to Use Sockets in JavaScript\HTML?
http://socket.io/
A possible way of doing that only using php + js is make some function and put in a setInterval to make request to the server every 12 seconds. I made some kind of asp chat in 2005 that uses this approach. And i must say the web socket is much better.
I don't know if that answers your question... let me know!
I developed something along these lines before using PHP and jQuery. The solution I went for was due to the restrictions on the server setup(out of my control). I used a PHP core script to create the whole layout of the page from message window to message submission box. Any user that came to the page was given a randomly generated user like user123234234 generated off a unix timestamp from the time they entered the chat page.
All messages submitted were stored in an XML file and a new XMl file was created daily. The user was kept in a message node like below with details of the user for every message using different node attributes.
The message window was refreshed every 5 seconds using jquery AJAX call to another PHP script that read in the XML that days XML file only from the time the user entered the chat page.
<messages>
<message user="user123456" ip="127.0.0.1" session="{users session ID here}" time="{unix timestamp}"><![CDATA[User message here]]></message>
</messages>
There is a lot more logic behind it but I found it the easiest to develop and maintain, I hope it help point you in the right direction. And it works on all major browsers and from IE7+.

socket_read() not blocking

Trying to get my socket_read() to block when the data has already been read before.
I need it to wait for the $spawn socket to have new data (can be equal i.e. would like it to read both times when it receives "FE") before it sends again.
How do I get it to block when there isn't any new data?
$i=0;
while ($i<10){
//Read and Parse Client input
$input = strtoupper(bin2hex(socket_read($spawn, 1)));
echo $input . "\n";
//Deal with requests
if ($input == "FE"){ //Server Ping
socket_write($spawn, $ping_packet);
echo "Ping Attempt \n";
} elseif ($input == "02"){ //Handshake Request
socket_write($spawn, $handshake_packet);
echo "Handshake Attempt \n";
} elseif($input == "01"){ //Login Request
socket_write($spawn, $kick_packet);
echo "Login Attempt \n";
}
$i++;
}
You can set the socket to be blocking by using socket_set_block. See socket_select for how to handle a large number of sockets at the same time.
socket_read will return an empty string if no data is ready to be read. To make a blocking read, use socket_recv with the flag MSG_WAITALL.

Categories