Kush Posted February 18, 2011 Share Posted February 18, 2011 Hello, I'm trying to update my database with current statistics from my gameservers, but running the loop of socket connections to all of the servers on page load takes too long. For every server in the array it takes roughly 4 seconds to complete each one.. with a massive list of say 42 it takes awhile. However, I'm running from shared hosting and was wondering if that was the reason each server takes so long to query. I was thinking about running a cron job every 1 minute to update the information so it's still relativity new and current. Will the 1 minute cron job affect anything from the shared hosting? I'm using 1and1 web-hosting on a 1&1 Business Package. Thanks Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/ Share on other sites More sharing options...
Kush Posted February 18, 2011 Author Share Posted February 18, 2011 Anyone? :'( Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176256 Share on other sites More sharing options...
BlueSkyIS Posted February 18, 2011 Share Posted February 18, 2011 yes, use a cron job. will it bother 1and1? ask them. Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176272 Share on other sites More sharing options...
Kush Posted February 18, 2011 Author Share Posted February 18, 2011 yes, use a cron job. will it bother 1and1? ask them. Okay. Is it common for the socket to take 4 seconds in total for each connection? It's recommended on 1and1 that you only run a cron job every 5 minutes, because it could queue up the server a lot, but I want to run the script every minute or minute and a half so the database is semi-current... do you think that would be too much stress on the server? Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176299 Share on other sites More sharing options...
Kush Posted February 18, 2011 Author Share Posted February 18, 2011 Is it common for the socket to take 4 seconds in total for each connection? It's taking a really long time to refresh all of the content that I need.. there has to be a better and faster way. I've seen websites doing something similar and they have 40++ servers that they're pulling data from and they refresh their database every minute so it's only taking them a minute. Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176344 Share on other sites More sharing options...
xylex Posted February 18, 2011 Share Posted February 18, 2011 Why not pull from the servers in parallel and pull them down all at once? Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176369 Share on other sites More sharing options...
Kush Posted February 18, 2011 Author Share Posted February 18, 2011 Why not pull from the servers in parallel and pull them down all at once? I'm not sure what you mean. Could you please go into detail? Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176375 Share on other sites More sharing options...
xylex Posted February 18, 2011 Share Posted February 18, 2011 What do you mean by socket connections? Are you actually connecting over with the socket library, or are you hitting the servers via CURL or SOAP or something? And can you provide a little info of the exchange that's taking place and what you're doing with that response so I can suggest how to do it in parallel? Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176385 Share on other sites More sharing options...
Kush Posted February 18, 2011 Author Share Posted February 18, 2011 What do you mean by socket connections? Are you actually connecting over with the socket library, or are you hitting the servers via CURL or SOAP or something? And can you provide a little info of the exchange that's taking place and what you're doing with that response so I can suggest how to do it in parallel? I'm trying to query my gameservers and get the map and how many players are in there. Here's the code I use to loop the game servers: foreach($serverList as $server) { $r = new rcon($server['ip'], $server['port'], $server['password']); if(!$r->isValid()) $serverInformation[$server['ip'] . ':' . $server['port']]['error'] = 'Unable to connect to server (' . $server['ip'] . ':' . $server['port'] . ')'; if(!$r->Auth()) $serverInformation[$server['ip'] . ':' . $server['port']]['error'] = 'Unable to authenticate! Wrong password?'; $status = $r->sendRconCommand("status"); $status = str_replace("\x0a", "", $status); $information = getServerInformation($status); // regex the result and return important content $serverInformation[$server['ip'] . ':' . $server['port']] = $information; // store important content in the array } Here's the rcon class too: <?php /* CS:S Rcon PHP Class - code by 1FO|zyzko 01/12/2005 www.1formatik.com - www.1fogames.com -------------------------------------------------- */ define("SERVERDATA_EXECCOMMAND", 02); define("SERVERDATA_AUTH", 03); class rcon { var $Password; var $Host; var $Port = 27015; var $_Sock = null; var $_Id = 0; var $valid = false; function __construct($Host,$Port,$Password) { return $this->init($Host,$Port,$Password); } function rcon ($Host,$Port,$Password) { return $this->init($Host,$Port,$Password); } function init($Host,$Port,$Password) { $this->Password = $Password; $this->Host = $Host; $this->Port = $Port; $this->_Sock = @fsockopen($this->Host,$this->Port, $errno, $errstr, 30);// or //die("Unable to open the port: $errstr ($errno)\n"+$this->Host+":"+$this->Port); if($this->_Sock) { $this->_Set_Timeout($this->_Sock,2,500); $this->valid = true; } } function isValid() { return $this->valid; } function Auth() { $PackID = $this->_Write(SERVERDATA_AUTH, $this->Password); $ret = $this->_PacketRead(); if ($ret[1]['ID'] == -1) { return 0; } else { return 1; } } function _Set_Timeout(&$res,$s,$m=0) { if (version_compare(phpversion(),'4.3.0','<')) { return socket_set_timeout($res,$s,$m); } return stream_set_timeout($res,$s,$m); } function _Write($cmd, $s1='', $s2='') { $id = ++$this->_Id; $data = pack("VV",$id,$cmd).$s1.chr(0).$s2.chr(0); $data = pack("V",strlen($data)).$data; fwrite($this->_Sock,$data,strlen($data)); return $id; } function _PacketRead() { $retarray = array(); while ($size = @fread($this->_Sock,4)) { $size = unpack('V1Size',$size); if ($size["Size"] > 4096) { $packet = "\x00\x00\x00\x00\x00\x00\x00\x00".fread($this->_Sock,4096); } else { $packet = fread($this->_Sock,$size["Size"]); } array_push($retarray,unpack("V1ID/V1Reponse/a*S1/a*S2",$packet)); } return $retarray; } function Read() { $Packets = $this->_PacketRead(); foreach($Packets as $pack) { if (isset($ret[$pack['ID']])) { $ret[$pack['ID']]['S1'] .= $pack['S1']; $ret[$pack['ID']]['S2'] .= $pack['S1']; } else { $ret[$pack['ID']] = array( 'Reponse' => $pack['Reponse'], 'S1' => $pack['S1'], 'S2' => $pack['S2'], ); } } return $ret; } /** * Send an rcon command the server. The command must be properly formatted before * sending to this method. This means that variables that require quotes must have * quotes put around them. * Ex. $command = "kickid ". "\"".$steamId."\" ".$banReason */ function sendRconCommand($command) { $this->_Write(SERVERDATA_EXECCOMMAND, $command, ''); $ret = $this->Read(); return $ret[$this->_Id]['S1']; } } ?> Don't get me wrong, because the code does run, but it's just really slow (4 seconds) for each loop in the foreach loop. I'm hoping there's a way to make this faster.. Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176390 Share on other sites More sharing options...
Kush Posted February 18, 2011 Author Share Posted February 18, 2011 What did you mean by running it in parallel? Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176423 Share on other sites More sharing options...
xylex Posted February 18, 2011 Share Posted February 18, 2011 It'd be a bit of work to make the changes, and fsockopen() still blocks per connection, so you'll have some delay there, but then all the servers can be polled at once. If you want to get really fancy, you can also just keep the script running and sockets open if your host and servers allow you to, which is how those bigger sites keep live data on hundreds of servers. Basically what you'd have to do is start by changing the rcon class to static so that you can pool the connections outside of it, and then make the methods take a $socket parameter that would replace the references to $this->_socket; In init(), after $this->_sock() is created, make it non blocking with stream_set_blocking(), and init() should return the socket object. That class also has two sets of send/receive methods that you use, Auth() and sendRconCommand(). You would need to split that into 4 methods, one each for sending and one each for receiving. I'll call it sendAuth()/receiveAuth() and sendRconCommand()/receiveRconCommand(). In your code then you would loop through all the servers calling rcon::init(), and create the an array of the object's socket connections with the server ip and port as the key. I'll call this one $rconsSockets You would loop through $rconsSockets and fire rcon::sendAuth() on each one, this will immediately do the sendAuth() command to all the servers. Use stream_select() and process those responses as they become available. If you were going to keep the script and those sockets alive, this next part would be in a while(true) loop or something like that. Repeat the process of sending the status command to all the servers, and then reading and processing the response as they become available. The code to do this would look something like: $numOfSockets = count($rconsSockets); foreach($rconsSockets as $socket) { rcon::sendRconCommand("status", $socket); } $n = 0; $responses = array(); $write = array(); $except = array(); while ($n < $numOfSockets) { $respondedStreams = stream_select($rconsSockets, $write, $except, 0); foreach($respondedStreams as $stream) { $key = array_search($stream, $rconsSockets, true); $response[$key] = rcon::receiveAuth($stream); } $n += count($respondedStreams); } What that code does is grabs the responses as they become available from each server and throws them into that $response array that you can either use later or do the getServerInformation() processing immediately. How that's setup will block to the slowest server, but so does the current code and that does it sequentially. A bit of work, and could probably be architected better than what I'm proposing and would need some better error handling, but this gives you an idea of how to do iwhat you're trying to do very quickly and efficiently. If you end up doing it, I'm sure the rcon community would appreciate it being kicked back to them as well. Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176440 Share on other sites More sharing options...
Kush Posted February 18, 2011 Author Share Posted February 18, 2011 xylex, when reading that it made sense ( to a degree ), but that's far too advanced for me... I'm still just a beginner. Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176454 Share on other sites More sharing options...
Kush Posted February 19, 2011 Author Share Posted February 19, 2011 No other way to speed it up though? :'( Quote Link to comment https://forums.phpfreaks.com/topic/228096-looping-socket-connections/#findComment-1176576 Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.