PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/pear/Net/Server/Driver/Sequential.php

http://akelosframework.googlecode.com/
PHP | 348 lines | 177 code | 42 blank | 129 comment | 45 complexity | c33cd10d221587c28e1019e5c1c3513d MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?PHP
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2002 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Stephan Schmidt <schst@php.net> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Sequential.php,v 1.8 2004/08/18 20:48:53 schst Exp $
  20. /**
  21. * Sequential server class.
  22. *
  23. * @category Networking
  24. * @package Net_Server
  25. * @author Stephan Schmidt <schst@php.net>
  26. */
  27. /**
  28. * needs the driver base class
  29. */
  30. require_once 'Net/Server/Driver.php';
  31. /**
  32. * Sequential server class.
  33. *
  34. * This class will handles all connection in one server process.
  35. * This allows you to build servers, where communication between
  36. * the clients is easy. The drawback is that clients are served
  37. * sequentially (hence the name). If you send large blocks of data
  38. * to a client, the others will have to wait.
  39. * For servers where communication between clients is not needed,
  40. * use Net_Server_Fork instead.
  41. *
  42. * Events that can be handled:
  43. * - onStart
  44. * - onConnect
  45. * - onConnectionRefused
  46. * - onClose
  47. * - onReceiveData
  48. * - onShutdown
  49. *
  50. * @category Networking
  51. * @package Net_Server
  52. * @author Stephan Schmidt <schst@php.net>
  53. */
  54. class Net_Server_Driver_Sequential extends Net_Server_Driver
  55. {
  56. /**
  57. * amount of clients
  58. * @var integer $clients
  59. */
  60. var $clients = 0;
  61. /**
  62. * set maximum amount of simultaneous connections
  63. *
  64. * @access public
  65. * @param int $maxClients
  66. */
  67. function setMaxClients($maxClients)
  68. {
  69. $this->maxClients = $maxClients;
  70. }
  71. /**
  72. * start the server
  73. *
  74. * @access public
  75. */
  76. function start()
  77. {
  78. $this->initFD = @socket_create(AF_INET, SOCK_STREAM, 0);
  79. if (!$this->initFD) {
  80. return $this->raiseError("Could not create socket.");
  81. }
  82. // adress may be reused
  83. socket_setopt($this->initFD, SOL_SOCKET, SO_REUSEADDR, 1);
  84. // bind the socket
  85. if (!@socket_bind($this->initFD, $this->domain, $this->port)) {
  86. $error = $this->getLastSocketError($this->initFd);
  87. @socket_close($this->initFD);
  88. return $this->raiseError("Could not bind socket to ".$this->domain." on port ".$this->port." (".$error.").");
  89. }
  90. // listen on selected port
  91. if (!@socket_listen($this->initFD, $this->maxQueue)) {
  92. $error = $this->getLastSocketError($this->initFd);
  93. @socket_close($this->initFD);
  94. return $this->raiseError("Could not listen (".$error.").");
  95. }
  96. $this->_sendDebugMessage("Listening on port ".$this->port.". Server started at ".date("H:i:s", time()));
  97. // this allows the shutdown function to check whether the server is already shut down
  98. $GLOBALS["_Net_Server_Status"] = "running";
  99. if (method_exists($this->callbackObj, "onStart")) {
  100. $this->callbackObj->onStart();
  101. }
  102. while (true)
  103. {
  104. $readFDs = array();
  105. array_push($readFDs, $this->initFD);
  106. // fetch all clients that are awaiting connections
  107. for ($i = 0; $i < count($this->clientFD); $i++) {
  108. if (isset($this->clientFD[$i]))
  109. array_push($readFDs, $this->clientFD[$i]);
  110. }
  111. // block and wait for data or new connection
  112. $ready = @socket_select($readFDs, $this->null, $this->null, NULL);
  113. if ($ready === false) {
  114. $this->_sendDebugMessage("socket_select failed.");
  115. $this->shutdown();
  116. }
  117. // check for new connection
  118. if (in_array($this->initFD, $readFDs)) {
  119. $newClient = $this->acceptConnection($this->initFD);
  120. // check for maximum amount of connections
  121. if ($this->maxClients > 0) {
  122. if ($this->clients > $this->maxClients) {
  123. $this->_sendDebugMessage("Too many connections.");
  124. if (method_exists($this->callbackObj, "onConnectionRefused")) {
  125. $this->callbackObj->onConnectionRefused($newClient);
  126. }
  127. $this->closeConnection($newClient);
  128. }
  129. }
  130. if (--$ready <= 0) {
  131. continue;
  132. }
  133. }
  134. // check all clients for incoming data
  135. for($i = 0; $i < count($this->clientFD); $i++) {
  136. if (!isset($this->clientFD[$i])) {
  137. continue;
  138. }
  139. if (in_array($this->clientFD[$i], $readFDs)) {
  140. $data = $this->readFromSocket($i);
  141. // empty data => connection was closed
  142. if ($data === false) {
  143. $this->_sendDebugMessage("Connection closed by peer");
  144. $this->closeConnection($i);
  145. }
  146. else {
  147. $this->_sendDebugMessage("Received ".trim($data)." from ".$i);
  148. if (method_exists($this->callbackObj, "onReceiveData")) {
  149. $this->callbackObj->onReceiveData($i, $data);
  150. }
  151. }
  152. }
  153. }
  154. }
  155. }
  156. /**
  157. * accept a new connection
  158. *
  159. * @access private
  160. * @param resource &$socket socket that received the new connection
  161. * @return int $clientID internal ID of the client
  162. */
  163. function acceptConnection(&$socket)
  164. {
  165. for($i = 0 ; $i <= count($this->clientFD); $i++) {
  166. if (!isset($this->clientFD[$i]) || $this->clientFD[$i] == NULL) {
  167. $this->clientFD[$i] = socket_accept($socket);
  168. socket_setopt($this->clientFD[$i], SOL_SOCKET, SO_REUSEADDR, 1);
  169. $peer_host = "";
  170. $peer_port = "";
  171. socket_getpeername($this->clientFD[$i], $peer_host, $peer_port);
  172. $this->clientInfo[$i] = array(
  173. "host" => $peer_host,
  174. "port" => $peer_port,
  175. "connectOn" => time()
  176. );
  177. $this->clients++;
  178. $this->_sendDebugMessage("New connection (".$i.") from ".$peer_host." on port ".$peer_port);
  179. if (method_exists($this->callbackObj, "onConnect")) {
  180. $this->callbackObj->onConnect($i);
  181. }
  182. return $i;
  183. }
  184. }
  185. }
  186. /**
  187. * check, whether a client is still connected
  188. *
  189. * @access public
  190. * @param integer $id client id
  191. * @return boolean $connected true if client is connected, false otherwise
  192. */
  193. function isConnected($id)
  194. {
  195. if (!isset($this->clientFD[$id])) {
  196. return false;
  197. }
  198. return true;
  199. }
  200. /**
  201. * get current amount of clients
  202. *
  203. * @access public
  204. * @return int $clients amount of clients
  205. */
  206. function getClients()
  207. {
  208. return $this->clients;
  209. }
  210. /**
  211. * send data to a client
  212. *
  213. * @access public
  214. * @param int $clientId ID of the client
  215. * @param string $data data to send
  216. * @param boolean $debugData flag to indicate whether data that is written to socket should also be sent as debug message
  217. */
  218. function sendData($clientId, $data, $debugData = true)
  219. {
  220. if (!isset($this->clientFD[$clientId]) || $this->clientFD[$clientId] == NULL) {
  221. return $this->raiseError("Client does not exist.");
  222. }
  223. if ($debugData) {
  224. $this->_sendDebugMessage("sending: \"" . $data . "\" to: $clientId" );
  225. }
  226. if (!@socket_write($this->clientFD[$clientId], $data)) {
  227. $this->_sendDebugMessage("Could not write '".$data."' client ".$clientId." (".$this->getLastSocketError($this->clientFD[$clientId]).").");
  228. }
  229. return true;
  230. }
  231. /**
  232. * send data to all clients
  233. *
  234. * @access public
  235. * @param string $data data to send
  236. * @param array $exclude client ids to exclude
  237. */
  238. function broadcastData($data, $exclude = array())
  239. {
  240. if (!empty($exclude) && !is_array($exclude)) {
  241. $exclude = array($exclude);
  242. }
  243. for($i = 0; $i < count($this->clientFD); $i++) {
  244. if (isset($this->clientFD[$i]) && $this->clientFD[$i] != NULL && !in_array($i, $exclude)) {
  245. if (!@socket_write($this->clientFD[$i], $data)) {
  246. $this->_sendDebugMessage("Could not write '".$data."' client ".$i." (".$this->getLastSocketError($this->clientFD[$i]).").");
  247. }
  248. }
  249. }
  250. }
  251. /**
  252. * get current information about a client
  253. *
  254. * @access public
  255. * @param int $clientId ID of the client
  256. * @return array $info information about the client
  257. */
  258. function getClientInfo($clientId)
  259. {
  260. if (!isset($this->clientFD[$clientId]) || $this->clientFD[$clientId] == NULL) {
  261. return $this->raiseError("Client does not exist.");
  262. }
  263. return $this->clientInfo[$clientId];
  264. }
  265. /**
  266. * close connection to a client
  267. *
  268. * @access public
  269. * @param int $clientID internal ID of the client
  270. */
  271. function closeConnection($id = 0)
  272. {
  273. if (!isset($this->clientFD[$id])) {
  274. return $this->raiseError( "Connection already has been closed." );
  275. }
  276. if (method_exists($this->callbackObj, "onClose")) {
  277. $this->callbackObj->onClose($id);
  278. }
  279. $this->_sendDebugMessage("Closed connection (".$id.") from ".$this->clientInfo[$id]["host"]." on port ".$this->clientInfo[$id]["port"]);
  280. @socket_shutdown($this->clientFD[$id], 2);
  281. @socket_close($this->clientFD[$id]);
  282. $this->clientFD[$id] = NULL;
  283. unset($this->clientInfo[$id]);
  284. $this->clients--;
  285. }
  286. /**
  287. * shutdown server
  288. *
  289. * @access public
  290. */
  291. function shutDown()
  292. {
  293. if (method_exists($this->callbackObj, "onShutdown")) {
  294. $this->callbackObj->onShutdown();
  295. }
  296. $maxFD = count($this->clientFD);
  297. for($i = 0; $i < $maxFD; $i++) {
  298. $this->closeConnection($i);
  299. }
  300. @socket_shutdown($this->initFD, 2);
  301. @socket_close($this->initFD);
  302. $this->_sendDebugMessage("Shutdown server.");
  303. exit();
  304. }
  305. }
  306. ?>