You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Socket.php 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. <?php
  2. /**
  3. * Net_Socket
  4. *
  5. * PHP Version 4
  6. *
  7. * Copyright (c) 1997-2013 The PHP Group
  8. *
  9. * This source file is subject to version 2.0 of the PHP license,
  10. * that is bundled with this package in the file LICENSE, and is
  11. * available at through the world-wide-web at
  12. * http://www.php.net/license/2_02.txt.
  13. * If you did not receive a copy of the PHP license and are unable to
  14. * obtain it through the world-wide-web, please send a note to
  15. * license@php.net so we can mail you a copy immediately.
  16. *
  17. * Authors: Stig Bakken <ssb@php.net>
  18. * Chuck Hagenbuch <chuck@horde.org>
  19. *
  20. * @category Net
  21. * @package Net_Socket
  22. * @author Stig Bakken <ssb@php.net>
  23. * @author Chuck Hagenbuch <chuck@horde.org>
  24. * @copyright 1997-2003 The PHP Group
  25. * @license http://www.php.net/license/2_02.txt PHP 2.02
  26. * @link http://pear.php.net/packages/Net_Socket
  27. */
  28. require_once 'PEAR.php';
  29. define('NET_SOCKET_READ', 1);
  30. define('NET_SOCKET_WRITE', 2);
  31. define('NET_SOCKET_ERROR', 4);
  32. /**
  33. * Generalized Socket class.
  34. *
  35. * @category Net
  36. * @package Net_Socket
  37. * @author Stig Bakken <ssb@php.net>
  38. * @author Chuck Hagenbuch <chuck@horde.org>
  39. * @copyright 1997-2003 The PHP Group
  40. * @license http://www.php.net/license/2_02.txt PHP 2.02
  41. * @link http://pear.php.net/packages/Net_Socket
  42. */
  43. class Net_Socket extends PEAR
  44. {
  45. /**
  46. * Socket file pointer.
  47. * @var resource $fp
  48. */
  49. var $fp = null;
  50. /**
  51. * Whether the socket is blocking. Defaults to true.
  52. * @var boolean $blocking
  53. */
  54. var $blocking = true;
  55. /**
  56. * Whether the socket is persistent. Defaults to false.
  57. * @var boolean $persistent
  58. */
  59. var $persistent = false;
  60. /**
  61. * The IP address to connect to.
  62. * @var string $addr
  63. */
  64. var $addr = '';
  65. /**
  66. * The port number to connect to.
  67. * @var integer $port
  68. */
  69. var $port = 0;
  70. /**
  71. * Number of seconds to wait on socket operations before assuming
  72. * there's no more data. Defaults to no timeout.
  73. * @var integer|float $timeout
  74. */
  75. var $timeout = null;
  76. /**
  77. * Number of bytes to read at a time in readLine() and
  78. * readAll(). Defaults to 2048.
  79. * @var integer $lineLength
  80. */
  81. var $lineLength = 2048;
  82. /**
  83. * The string to use as a newline terminator. Usually "\r\n" or "\n".
  84. * @var string $newline
  85. */
  86. var $newline = "\r\n";
  87. /**
  88. * Connect to the specified port. If called when the socket is
  89. * already connected, it disconnects and connects again.
  90. *
  91. * @param string $addr IP address or host name (may be with protocol prefix).
  92. * @param integer $port TCP port number.
  93. * @param boolean $persistent (optional) Whether the connection is
  94. * persistent (kept open between requests
  95. * by the web server).
  96. * @param integer $timeout (optional) Connection socket timeout.
  97. * @param array $options See options for stream_context_create.
  98. *
  99. * @access public
  100. *
  101. * @return boolean|PEAR_Error True on success or a PEAR_Error on failure.
  102. */
  103. function connect($addr, $port = 0, $persistent = null,
  104. $timeout = null, $options = null)
  105. {
  106. if (is_resource($this->fp)) {
  107. @fclose($this->fp);
  108. $this->fp = null;
  109. }
  110. if (!$addr) {
  111. return $this->raiseError('$addr cannot be empty');
  112. } else if (strspn($addr, ':.0123456789') == strlen($addr)) {
  113. $this->addr = strpos($addr, ':') !== false ? '['.$addr.']' : $addr;
  114. } else {
  115. $this->addr = $addr;
  116. }
  117. $this->port = $port % 65536;
  118. if ($persistent !== null) {
  119. $this->persistent = $persistent;
  120. }
  121. $openfunc = $this->persistent ? 'pfsockopen' : 'fsockopen';
  122. $errno = 0;
  123. $errstr = '';
  124. $old_track_errors = @ini_set('track_errors', 1);
  125. if ($timeout <= 0) {
  126. $timeout = @ini_get('default_socket_timeout');
  127. }
  128. if ($options && function_exists('stream_context_create')) {
  129. $context = stream_context_create($options);
  130. // Since PHP 5 fsockopen doesn't allow context specification
  131. if (function_exists('stream_socket_client')) {
  132. $flags = STREAM_CLIENT_CONNECT;
  133. if ($this->persistent) {
  134. $flags = STREAM_CLIENT_PERSISTENT;
  135. }
  136. $addr = $this->addr . ':' . $this->port;
  137. $fp = stream_socket_client($addr, $errno, $errstr,
  138. $timeout, $flags, $context);
  139. } else {
  140. $fp = @$openfunc($this->addr, $this->port, $errno,
  141. $errstr, $timeout, $context);
  142. }
  143. } else {
  144. $fp = @$openfunc($this->addr, $this->port, $errno, $errstr, $timeout);
  145. }
  146. if (!$fp) {
  147. if ($errno == 0 && !strlen($errstr) && isset($php_errormsg)) {
  148. $errstr = $php_errormsg;
  149. }
  150. @ini_set('track_errors', $old_track_errors);
  151. return $this->raiseError($errstr, $errno);
  152. }
  153. @ini_set('track_errors', $old_track_errors);
  154. $this->fp = $fp;
  155. $this->setTimeout();
  156. return $this->setBlocking($this->blocking);
  157. }
  158. /**
  159. * Disconnects from the peer, closes the socket.
  160. *
  161. * @access public
  162. * @return mixed true on success or a PEAR_Error instance otherwise
  163. */
  164. function disconnect()
  165. {
  166. if (!is_resource($this->fp)) {
  167. return $this->raiseError('not connected');
  168. }
  169. @fclose($this->fp);
  170. $this->fp = null;
  171. return true;
  172. }
  173. /**
  174. * Set the newline character/sequence to use.
  175. *
  176. * @param string $newline Newline character(s)
  177. * @return boolean True
  178. */
  179. function setNewline($newline)
  180. {
  181. $this->newline = $newline;
  182. return true;
  183. }
  184. /**
  185. * Find out if the socket is in blocking mode.
  186. *
  187. * @access public
  188. * @return boolean The current blocking mode.
  189. */
  190. function isBlocking()
  191. {
  192. return $this->blocking;
  193. }
  194. /**
  195. * Sets whether the socket connection should be blocking or
  196. * not. A read call to a non-blocking socket will return immediately
  197. * if there is no data available, whereas it will block until there
  198. * is data for blocking sockets.
  199. *
  200. * @param boolean $mode True for blocking sockets, false for nonblocking.
  201. *
  202. * @access public
  203. * @return mixed true on success or a PEAR_Error instance otherwise
  204. */
  205. function setBlocking($mode)
  206. {
  207. if (!is_resource($this->fp)) {
  208. return $this->raiseError('not connected');
  209. }
  210. $this->blocking = $mode;
  211. stream_set_blocking($this->fp, (int)$this->blocking);
  212. return true;
  213. }
  214. /**
  215. * Sets the timeout value on socket descriptor,
  216. * expressed in the sum of seconds and microseconds
  217. *
  218. * @param integer $seconds Seconds.
  219. * @param integer $microseconds Microseconds, optional.
  220. *
  221. * @access public
  222. * @return mixed True on success or false on failure or
  223. * a PEAR_Error instance when not connected
  224. */
  225. function setTimeout($seconds = null, $microseconds = null)
  226. {
  227. if (!is_resource($this->fp)) {
  228. return $this->raiseError('not connected');
  229. }
  230. if ($seconds === null && $microseconds === null) {
  231. $seconds = (int) $this->timeout;
  232. $microseconds = (int) (($this->timeout - $seconds) * 1000000);
  233. } else {
  234. $this->timeout = $seconds + $microseconds/1000000;
  235. }
  236. if ($this->timeout > 0) {
  237. return stream_set_timeout($this->fp, (int) $seconds, (int) $microseconds);
  238. }
  239. else {
  240. return false;
  241. }
  242. }
  243. /**
  244. * Sets the file buffering size on the stream.
  245. * See php's stream_set_write_buffer for more information.
  246. *
  247. * @param integer $size Write buffer size.
  248. *
  249. * @access public
  250. * @return mixed on success or an PEAR_Error object otherwise
  251. */
  252. function setWriteBuffer($size)
  253. {
  254. if (!is_resource($this->fp)) {
  255. return $this->raiseError('not connected');
  256. }
  257. $returned = stream_set_write_buffer($this->fp, $size);
  258. if ($returned == 0) {
  259. return true;
  260. }
  261. return $this->raiseError('Cannot set write buffer.');
  262. }
  263. /**
  264. * Returns information about an existing socket resource.
  265. * Currently returns four entries in the result array:
  266. *
  267. * <p>
  268. * timed_out (bool) - The socket timed out waiting for data<br>
  269. * blocked (bool) - The socket was blocked<br>
  270. * eof (bool) - Indicates EOF event<br>
  271. * unread_bytes (int) - Number of bytes left in the socket buffer<br>
  272. * </p>
  273. *
  274. * @access public
  275. * @return mixed Array containing information about existing socket
  276. * resource or a PEAR_Error instance otherwise
  277. */
  278. function getStatus()
  279. {
  280. if (!is_resource($this->fp)) {
  281. return $this->raiseError('not connected');
  282. }
  283. return stream_get_meta_data($this->fp);
  284. }
  285. /**
  286. * Get a specified line of data
  287. *
  288. * @param int $size Reading ends when size - 1 bytes have been read,
  289. * or a newline or an EOF (whichever comes first).
  290. * If no size is specified, it will keep reading from
  291. * the stream until it reaches the end of the line.
  292. *
  293. * @access public
  294. * @return mixed $size bytes of data from the socket, or a PEAR_Error if
  295. * not connected. If an error occurs, FALSE is returned.
  296. */
  297. function gets($size = null)
  298. {
  299. if (!is_resource($this->fp)) {
  300. return $this->raiseError('not connected');
  301. }
  302. if (is_null($size)) {
  303. return @fgets($this->fp);
  304. } else {
  305. return @fgets($this->fp, $size);
  306. }
  307. }
  308. /**
  309. * Read a specified amount of data. This is guaranteed to return,
  310. * and has the added benefit of getting everything in one fread()
  311. * chunk; if you know the size of the data you're getting
  312. * beforehand, this is definitely the way to go.
  313. *
  314. * @param integer $size The number of bytes to read from the socket.
  315. *
  316. * @access public
  317. * @return $size bytes of data from the socket, or a PEAR_Error if
  318. * not connected.
  319. */
  320. function read($size)
  321. {
  322. if (!is_resource($this->fp)) {
  323. return $this->raiseError('not connected');
  324. }
  325. return @fread($this->fp, $size);
  326. }
  327. /**
  328. * Write a specified amount of data.
  329. *
  330. * @param string $data Data to write.
  331. * @param integer $blocksize Amount of data to write at once.
  332. * NULL means all at once.
  333. *
  334. * @access public
  335. * @return mixed If the socket is not connected, returns an instance of
  336. * PEAR_Error.
  337. * If the write succeeds, returns the number of bytes written.
  338. * If the write fails, returns false.
  339. * If the socket times out, returns an instance of PEAR_Error.
  340. */
  341. function write($data, $blocksize = null)
  342. {
  343. if (!is_resource($this->fp)) {
  344. return $this->raiseError('not connected');
  345. }
  346. if (is_null($blocksize) && !OS_WINDOWS) {
  347. $written = @fwrite($this->fp, $data);
  348. // Check for timeout or lost connection
  349. if (!$written) {
  350. $meta_data = $this->getStatus();
  351. if (!is_array($meta_data)) {
  352. return $meta_data; // PEAR_Error
  353. }
  354. if (!empty($meta_data['timed_out'])) {
  355. return $this->raiseError('timed out');
  356. }
  357. }
  358. return $written;
  359. } else {
  360. if (is_null($blocksize)) {
  361. $blocksize = 1024;
  362. }
  363. $pos = 0;
  364. $size = strlen($data);
  365. while ($pos < $size) {
  366. $written = @fwrite($this->fp, substr($data, $pos, $blocksize));
  367. // Check for timeout or lost connection
  368. if (!$written) {
  369. $meta_data = $this->getStatus();
  370. if (!is_array($meta_data)) {
  371. return $meta_data; // PEAR_Error
  372. }
  373. if (!empty($meta_data['timed_out'])) {
  374. return $this->raiseError('timed out');
  375. }
  376. return $written;
  377. }
  378. $pos += $written;
  379. }
  380. return $pos;
  381. }
  382. }
  383. /**
  384. * Write a line of data to the socket, followed by a trailing newline.
  385. *
  386. * @param string $data Data to write
  387. *
  388. * @access public
  389. * @return mixed fwrite() result, or PEAR_Error when not connected
  390. */
  391. function writeLine($data)
  392. {
  393. if (!is_resource($this->fp)) {
  394. return $this->raiseError('not connected');
  395. }
  396. return fwrite($this->fp, $data . $this->newline);
  397. }
  398. /**
  399. * Tests for end-of-file on a socket descriptor.
  400. *
  401. * Also returns true if the socket is disconnected.
  402. *
  403. * @access public
  404. * @return bool
  405. */
  406. function eof()
  407. {
  408. return (!is_resource($this->fp) || feof($this->fp));
  409. }
  410. /**
  411. * Reads a byte of data
  412. *
  413. * @access public
  414. * @return 1 byte of data from the socket, or a PEAR_Error if
  415. * not connected.
  416. */
  417. function readByte()
  418. {
  419. if (!is_resource($this->fp)) {
  420. return $this->raiseError('not connected');
  421. }
  422. return ord(@fread($this->fp, 1));
  423. }
  424. /**
  425. * Reads a word of data
  426. *
  427. * @access public
  428. * @return 1 word of data from the socket, or a PEAR_Error if
  429. * not connected.
  430. */
  431. function readWord()
  432. {
  433. if (!is_resource($this->fp)) {
  434. return $this->raiseError('not connected');
  435. }
  436. $buf = @fread($this->fp, 2);
  437. return (ord($buf[0]) + (ord($buf[1]) << 8));
  438. }
  439. /**
  440. * Reads an int of data
  441. *
  442. * @access public
  443. * @return integer 1 int of data from the socket, or a PEAR_Error if
  444. * not connected.
  445. */
  446. function readInt()
  447. {
  448. if (!is_resource($this->fp)) {
  449. return $this->raiseError('not connected');
  450. }
  451. $buf = @fread($this->fp, 4);
  452. return (ord($buf[0]) + (ord($buf[1]) << 8) +
  453. (ord($buf[2]) << 16) + (ord($buf[3]) << 24));
  454. }
  455. /**
  456. * Reads a zero-terminated string of data
  457. *
  458. * @access public
  459. * @return string, or a PEAR_Error if
  460. * not connected.
  461. */
  462. function readString()
  463. {
  464. if (!is_resource($this->fp)) {
  465. return $this->raiseError('not connected');
  466. }
  467. $string = '';
  468. while (($char = @fread($this->fp, 1)) != "\x00") {
  469. $string .= $char;
  470. }
  471. return $string;
  472. }
  473. /**
  474. * Reads an IP Address and returns it in a dot formatted string
  475. *
  476. * @access public
  477. * @return Dot formatted string, or a PEAR_Error if
  478. * not connected.
  479. */
  480. function readIPAddress()
  481. {
  482. if (!is_resource($this->fp)) {
  483. return $this->raiseError('not connected');
  484. }
  485. $buf = @fread($this->fp, 4);
  486. return sprintf('%d.%d.%d.%d', ord($buf[0]), ord($buf[1]),
  487. ord($buf[2]), ord($buf[3]));
  488. }
  489. /**
  490. * Read until either the end of the socket or a newline, whichever
  491. * comes first. Strips the trailing newline from the returned data.
  492. *
  493. * @access public
  494. * @return All available data up to a newline, without that
  495. * newline, or until the end of the socket, or a PEAR_Error if
  496. * not connected.
  497. */
  498. function readLine()
  499. {
  500. if (!is_resource($this->fp)) {
  501. return $this->raiseError('not connected');
  502. }
  503. $line = '';
  504. $timeout = time() + $this->timeout;
  505. while (!feof($this->fp) && (!$this->timeout || time() < $timeout)) {
  506. $line .= @fgets($this->fp, $this->lineLength);
  507. if (substr($line, -1) == "\n") {
  508. return rtrim($line, $this->newline);
  509. }
  510. }
  511. return $line;
  512. }
  513. /**
  514. * Read until the socket closes, or until there is no more data in
  515. * the inner PHP buffer. If the inner buffer is empty, in blocking
  516. * mode we wait for at least 1 byte of data. Therefore, in
  517. * blocking mode, if there is no data at all to be read, this
  518. * function will never exit (unless the socket is closed on the
  519. * remote end).
  520. *
  521. * @access public
  522. *
  523. * @return string All data until the socket closes, or a PEAR_Error if
  524. * not connected.
  525. */
  526. function readAll()
  527. {
  528. if (!is_resource($this->fp)) {
  529. return $this->raiseError('not connected');
  530. }
  531. $data = '';
  532. while (!feof($this->fp)) {
  533. $data .= @fread($this->fp, $this->lineLength);
  534. }
  535. return $data;
  536. }
  537. /**
  538. * Runs the equivalent of the select() system call on the socket
  539. * with a timeout specified by tv_sec and tv_usec.
  540. *
  541. * @param integer $state Which of read/write/error to check for.
  542. * @param integer $tv_sec Number of seconds for timeout.
  543. * @param integer $tv_usec Number of microseconds for timeout.
  544. *
  545. * @access public
  546. * @return False if select fails, integer describing which of read/write/error
  547. * are ready, or PEAR_Error if not connected.
  548. */
  549. function select($state, $tv_sec, $tv_usec = 0)
  550. {
  551. if (!is_resource($this->fp)) {
  552. return $this->raiseError('not connected');
  553. }
  554. $read = null;
  555. $write = null;
  556. $except = null;
  557. if ($state & NET_SOCKET_READ) {
  558. $read[] = $this->fp;
  559. }
  560. if ($state & NET_SOCKET_WRITE) {
  561. $write[] = $this->fp;
  562. }
  563. if ($state & NET_SOCKET_ERROR) {
  564. $except[] = $this->fp;
  565. }
  566. if (false === ($sr = stream_select($read, $write, $except,
  567. $tv_sec, $tv_usec))) {
  568. return false;
  569. }
  570. $result = 0;
  571. if (count($read)) {
  572. $result |= NET_SOCKET_READ;
  573. }
  574. if (count($write)) {
  575. $result |= NET_SOCKET_WRITE;
  576. }
  577. if (count($except)) {
  578. $result |= NET_SOCKET_ERROR;
  579. }
  580. return $result;
  581. }
  582. /**
  583. * Turns encryption on/off on a connected socket.
  584. *
  585. * @param bool $enabled Set this parameter to true to enable encryption
  586. * and false to disable encryption.
  587. * @param integer $type Type of encryption. See stream_socket_enable_crypto()
  588. * for values.
  589. *
  590. * @see http://se.php.net/manual/en/function.stream-socket-enable-crypto.php
  591. * @access public
  592. * @return false on error, true on success and 0 if there isn't enough data
  593. * and the user should try again (non-blocking sockets only).
  594. * A PEAR_Error object is returned if the socket is not
  595. * connected
  596. */
  597. function enableCrypto($enabled, $type)
  598. {
  599. if (version_compare(phpversion(), "5.1.0", ">=")) {
  600. if (!is_resource($this->fp)) {
  601. return $this->raiseError('not connected');
  602. }
  603. return @stream_socket_enable_crypto($this->fp, $enabled, $type);
  604. } else {
  605. $msg = 'Net_Socket::enableCrypto() requires php version >= 5.1.0';
  606. return $this->raiseError($msg);
  607. }
  608. }
  609. }