当前位置:首页 > PHP教程 > php高级应用 > 列表

php socket实现的聊天室代码分享

发布:smiling 来源: PHP粉丝网  添加日期:2021-03-31 10:52:04 浏览: 评论:0 

这篇文章主要介绍了php socket实现的聊天室代码分享,本文实现代码来自国外友人,需要的朋友可以参考下。

  1. /** 
  2. * patServer 
  3. * PHP socket server base class 
  4. * Events that can be handled: 
  5. *  * onStart 
  6. *  * onConnect 
  7. *  * onConnectionRefused 
  8. *  * onClose 
  9. *  * onShutdown 
  10. *  * onReceiveData 
  11. * 
  12. * @version 1.1 
  13. * @author  Stephan Schmidt <schst@php-tools.de> 
  14. * @package patServer 
  15. */ 
  16. class patServer { 
  17. /** 
  18. * information about the project 
  19. * @var array $systemVars 
  20. */ 
  21. var $systemVars  = array( 
  22.   "appName"  => "patServer", 
  23.   "appVersion"  => "1.1", 
  24.   "author"  => array("Stephan Schmidt <schst@php-tools.de>", ) 
  25.   ); 
  26.   
  27. /** 
  28. * port to listen 
  29. * @var integer  $port 
  30. */ 
  31.   var $port  = 10000; 
  32.   
  33. /** 
  34. * domain to bind to 
  35. * @var string $domain 
  36. */ 
  37.   var $domain  = "localhost"; 
  38.   
  39. /** 
  40. * maximum amount of clients 
  41. * @var integer $maxClients 
  42. */ 
  43.   var $maxClients = -1; 
  44.   
  45. /** 
  46. * buffer size for socket_read 
  47. * @var integer $readBufferSize 
  48. */ 
  49.   var $readBufferSize  = 128; 
  50.   
  51. /** 
  52. * end character for socket_read 
  53. * @var integer $readEndCharacter 
  54. */ 
  55.   var $readEndCharacter = "\n"; 
  56.   
  57. /** 
  58. * maximum of backlog in queue 
  59. * @var integer $maxQueue 
  60. */ 
  61.   var $maxQueue = 500; 
  62.   
  63. /** 
  64. * debug mode 
  65. * @var boolean $debug 
  66. */ 
  67.   var $debug  = true; 
  68.   
  69. /** 
  70. * debug mode 
  71. * @var string $debugMode 
  72. */ 
  73.   var $debugMode = "text"; 
  74.   
  75. /** 
  76. * debug destination (filename or stdout) 
  77. * @var string $debugDest 
  78. */ 
  79.   var $debugDest = "stdout"; 
  80.   
  81. /** 
  82. * empty array, used for socket_select 
  83. * @var array $null 
  84. */ 
  85.   var $null  = array(); 
  86.   
  87. /** 
  88. * all file descriptors are stored here 
  89. * @var array $clientFD 
  90. */ 
  91.   var $clientFD = array(); 
  92.   
  93. /** 
  94. * needed to store client information 
  95. * @var array $clientInfo 
  96. */ 
  97.   var $clientInfo = array(); 
  98.   
  99. /** 
  100. * needed to store server information 
  101. * @var array $serverInfo 
  102. */ 
  103.   var $serverInfo = array(); 
  104.   
  105. /** 
  106. * amount of clients 
  107. * @var integer  $clients 
  108. */ 
  109.   var $clients = 0; 
  110.   
  111. /** 
  112. * create a new socket server 
  113. * 
  114. * @access public 
  115. * @param string  $domain  domain to bind to 
  116. * @param integer  $port  port to listen to 
  117. */ 
  118. function patServer( $domain = "localhost", $port = 10000 ) 
  119. { 
  120.   $this->domain = $domain; 
  121.   $this->port  = $port; 
  122.   
  123.   $this->serverInfo["domain"]     = $domain; 
  124.   $this->serverInfo["port"]     = $port; 
  125.   $this->serverInfo["servername"]   = $this->systemVars["appName"]; 
  126.   $this->serverInfo["serverversion"] = $this->systemVars["appVersion"]; 
  127.   
  128.   set_time_limit( 0 ); 
  129. } 
  130.   
  131. /** 
  132. * set maximum amount of simultaneous connections 
  133. * 
  134. * @access public 
  135. * @param int $maxClients 
  136. */ 
  137. function setMaxClients( $maxClients ) 
  138. { 
  139.   $this->maxClients = $maxClients; 
  140. } 
  141.   
  142. /** 
  143. * set debug mode 
  144. * 
  145. * @access public 
  146. * @param mixed $debug [text|htmlfalse] 
  147. * @param string $dest destination of debug message (stdout to output or filename if log should be written) 
  148. */ 
  149. function setDebugMode( $debug, $dest = "stdout" ) 
  150. { 
  151.   if( $debug === false ) 
  152.   { 
  153.   $this->debug = false; 
  154.   return true; 
  155.   } 
  156.   
  157.   $this->debug  = true; 
  158.   $this->debugMode = $debug; 
  159.   $this->debugDest = $dest; 
  160. } 
  161.   
  162. /** 
  163. * start the server 
  164. * 
  165. * @access public 
  166. * @param int $maxClients 
  167. */ 
  168. function start() 
  169. { 
  170.   $this->initFD = @socket_create( AF_INET, SOCK_STREAM, 0 ); 
  171.   if( !$this->initFD ) 
  172.   die( "patServer: Could not create socket." ); 
  173.   
  174.   // adress may be reused 
  175.   socket_setopt( $this->initFD, SOL_SOCKET, SO_REUSEADDR, 1 ); 
  176.   
  177.   // bind the socket 
  178.   if( !@socket_bind( $this->initFD, $this->domain, $this->port ) ) 
  179.   { 
  180.   @socket_close( $this->initFD ); 
  181.   die( "patServer: Could not bind socket to ".$this->domain." on port ".$this->port." ( ".$this->getLastSocketError( $this->initFd )." )." ); 
  182.   } 
  183.   
  184.   // listen on selected port 
  185.   if( !@socket_listen( $this->initFD, $this->maxQueue ) ) 
  186.   die( "patServer: Could not listen ( ".$this->getLastSocketError( $this->initFd )." )." ); 
  187.   
  188.   $this->sendDebugMessage( "Listening on port ".$this->port.". Server started at ".date( "H:i:s", time() ) ); 
  189.   
  190.   // this allows the shutdown function to check whether the server is already shut down 
  191.   $GLOBALS["_patServerStatus"] = "running"; 
  192.   // this ensures that the server will be sutdown correctly 
  193.   register_shutdown_function( array( $this, "shutdown" ) ); 
  194.   
  195.   if( method_exists( $this, "onStart" ) ) 
  196.   $this->onStart(); 
  197.   
  198.   $this->serverInfo["started"] = time(); 
  199.   $this->serverInfo["status"]  = "running"; 
  200.   
  201.   while( true ) 
  202.   { 
  203.   $readFDs = array(); 
  204.   array_push( $readFDs, $this->initFD ); 
  205.   
  206.   // fetch all clients that are awaiting connections 
  207.   for( $i = 0; $i < count( $this->clientFD ); $i++ ) 
  208.    if( isset( $this->clientFD[$i] ) ) 
  209.    array_push( $readFDs, $this->clientFD[$i] ); 
  210.   
  211.   // block and wait for data or new connection 
  212.   $ready = @socket_select( $readFDs, $this->null, $this->null, NULL ); 
  213.   
  214.   if( $ready === false ) 
  215.   { 
  216.    $this->sendDebugMessage( "socket_select failed." ); 
  217.    $this->shutdown(); 
  218.   } 
  219.   
  220.   // check for new connection 
  221.   if( in_array( $this->initFD, $readFDs ) ) 
  222.   { 
  223.    $newClient = $this->acceptConnection( $this->initFD ); 
  224.   
  225.    // check for maximum amount of connections 
  226.    if( $this->maxClients > 0 ) 
  227.    { 
  228.    if( $this->clients > $this->maxClients ) 
  229.    { 
  230.     $this->sendDebugMessage( "Too many connections." ); 
  231.   
  232.     if( method_exists( $this, "onConnectionRefused" ) ) 
  233.     $this->onConnectionRefused( $newClient ); 
  234.   
  235.     $this->closeConnection( $newClient ); 
  236.    } 
  237.    } 
  238.   
  239.    if( --$ready <= 0 ) 
  240.    continue; 
  241.   } 
  242.   
  243.   // check all clients for incoming data 
  244.   for( $i = 0; $i < count( $this->clientFD ); $i++ ) 
  245.   { 
  246.    if( !isset( $this->clientFD[$i] ) ) 
  247.    continue; 
  248.   
  249.    if( in_array( $this->clientFD[$i], $readFDs ) ) 
  250.    { 
  251.    $data = $this->readFromSocket( $i ); 
  252.   
  253.    // empty data => connection was closed 
  254.    if( !$data ) 
  255.    { 
  256.     $this->sendDebugMessage( "Connection closed by peer" ); 
  257.     $this->closeConnection( $i ); 
  258.    } 
  259.    else 
  260.    { 
  261.     $this->sendDebugMessage( "Received ".trim( $data )." from ".$i ); 
  262.   
  263.     if( method_exists( $this, "onReceiveData" ) ) 
  264.     $this->onReceiveData( $i, $data ); 
  265.    } 
  266.    } 
  267.   } 
  268.   } 
  269. } 
  270.   
  271. /** 
  272. * read from a socket 
  273. * 
  274. * @access private 
  275. * @param integer $clientId internal id of the client to read from 
  276. * @return string $data  data that was read 
  277. */ 
  278. function readFromSocket( $clientId ) 
  279. { 
  280.   // start with empty string 
  281.   $data  = ""; 
  282.   
  283.   // read data from socket 
  284.   while( $buf = socket_read( $this->clientFD[$clientId], $this->readBufferSize ) ) 
  285.   { 
  286.   $data .= $buf; 
  287.   
  288.   $endString = substr( $buf, - strlen( $this->readEndCharacter ) ); 
  289.   if( $endString == $this->readEndCharacter ) 
  290.    break; 
  291.   if( $buf == NULL ) 
  292.    break; 
  293.   } 
  294.   
  295.   if( $buf === false ) 
  296.   $this->sendDebugMessage( "Could not read from client ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." )." ); 
  297.   
  298.   return $data; 
  299. } 
  300.   
  301. /** 
  302. * accept a new connection 
  303. * 
  304. * @access public 
  305. * @param resource &$socket socket that received the new connection 
  306. * @return int  $clientID internal ID of the client 
  307. */ 
  308. function acceptConnection( &$socket ) 
  309. { 
  310.   for( $i = 0 ; $i <= count( $this->clientFD ); $i++ ) 
  311.   { 
  312.   if( !isset( $this->clientFD[$i] ) || $this->clientFD[$i] == NULL ) 
  313.   { 
  314.    $this->clientFD[$i] = socket_accept( $socket ); 
  315.    socket_setopt( $this->clientFD[$i], SOL_SOCKET, SO_REUSEADDR, 1 ); 
  316.    $peer_host = ""; 
  317.    $peer_port = ""; 
  318.    socket_getpeername( $this->clientFD[$i], $peer_host, $peer_port ); 
  319.    $this->clientInfo[$i] = array( 
  320.        "host"  => $peer_host, 
  321.        "port"  => $peer_port, 
  322.        "connectOn" => time() 
  323.        ); 
  324.    $this->clients++; 
  325.   
  326.    $this->sendDebugMessage( "New connection ( ".$i." ) from ".$peer_host." on port ".$peer_port ); 
  327.   
  328.    if( method_exists( $this, "onConnect" ) ) 
  329.    $this->onConnect( $i ); 
  330.    return $i; 
  331.   } 
  332.   } 
  333. } 
  334.   
  335. /** 
  336. * check, whether a client is still connected 
  337. * 
  338. * @access public 
  339. * @param integer $id client id 
  340. * @return boolean $connected true if client is connected, false otherwise 
  341. */ 
  342. function isConnected( $id ) 
  343. { 
  344.   if( !isset( $this->clientFD[$id] ) ) 
  345.   return false; 
  346.   return true; 
  347. } 
  348.   
  349. /** 
  350. * close connection to a client 
  351. * 
  352. * @access public 
  353. * @param int $clientID internal ID of the client 
  354. */ 
  355. function closeConnection( $id ) 
  356. { 
  357.   if( !isset( $this->clientFD[$id] ) ) 
  358.   return false; 
  359.   
  360.   if( method_exists( $this, "onClose" ) ) 
  361.   $this->onClose( $id ); 
  362.   
  363.   $this->sendDebugMessage( "Closed connection ( ".$id." ) from ".$this->clientInfo[$id]["host"]." on port ".$this->clientInfo[$id]["port"] ); 
  364.   
  365.   @socket_close( $this->clientFD[$id] ); 
  366.   $this->clientFD[$id] = NULL; 
  367.   unset( $this->clientInfo[$id] ); 
  368.   $this->clients--; 
  369. } 
  370.   
  371. /** 
  372. * shutdown server 
  373. * 
  374. * @access public 
  375. */ 
  376. function shutDown() 
  377. { 
  378.   if( $GLOBALS["_patServerStatus"] != "running" ) 
  379.   exit; 
  380.   $GLOBALS["_patServerStatus"] = "stopped"; 
  381.   
  382.   if( method_exists( $this, "onShutdown" ) ) 
  383.   $this->onShutdown(); 
  384.   
  385.   $maxFD = count( $this->clientFD ); 
  386.   for( $i = 0; $i < $maxFD; $i++ ) 
  387.   $this->closeConnection( $i ); 
  388.   
  389.   @socket_close( $this->initFD ); 
  390.   
  391.   $this->sendDebugMessage( "Shutdown server." ); 
  392.   exit; 
  393. } 
  394.   
  395. /** 
  396. * get current amount of clients 
  397. * 
  398. * @access public 
  399. * @return int $clients amount of clients 
  400. */ 
  401. function getClients() 
  402. { 
  403.   return $this->clients; 
  404. } 
  405.   
  406. /** 
  407. * send data to a client 
  408. * 
  409. * @access public 
  410. * @param int  $clientId ID of the client 
  411. * @param string $data  data to send 
  412. * @param boolean $debugData flag to indicate whether data that is written to socket should also be sent as debug message 
  413. */ 
  414. function sendData( $clientId, $data, $debugData = true ) 
  415. { 
  416.   if( !isset( $this->clientFD[$clientId] ) || $this->clientFD[$clientId] == NULL ) 
  417.   return false; 
  418.   
  419.   if( $debugData ) 
  420.   $this->sendDebugMessage( "sending: \"" . $data . "\" to: $clientId" ); 
  421.   
  422.   if( !@socket_write( $this->clientFD[$clientId], $data ) ) 
  423.   $this->sendDebugMessage( "Could not write '".$data."' client ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." )." ); 
  424. } 
  425.   
  426. /** 
  427. * send data to all clients 
  428. * 
  429. * @access public 
  430. * @param string $data  data to send 
  431. * @param array $exclude client ids to exclude 
  432. */ 
  433. function broadcastData( $data, $exclude = array(), $debugData = true ) 
  434. { 
  435.   if( !emptyempty( $exclude ) && !is_array( $exclude ) ) 
  436.   $exclude = array( $exclude ); 
  437.   
  438.   for( $i = 0; $i < count( $this->clientFD ); $i++ ) 
  439.   { 
  440.   if( isset( $this->clientFD[$i] ) && $this->clientFD[$i] != NULL && !in_array( $i, $exclude ) ) 
  441.   { 
  442.    if( $debugData ) 
  443.    $this->sendDebugMessage( "sending: \"" . $data . "\" to: $i" ); 
  444.   
  445.    if( !@socket_write( $this->clientFD[$i], $data ) ) 
  446.    $this->sendDebugMessage( "Could not write '".$data."' client ".$i." ( ".$this->getLastSocketError( $this->clientFD[$i] )." )." ); 
  447.   } 
  448.   } 
  449. } 
  450.   
  451. /** 
  452. * get current information about a client 
  453. * 
  454. * @access public 
  455. * @param int  $clientId ID of the client 
  456. * @return array $info  information about the client 
  457. */ 
  458. function getClientInfo( $clientId ) 
  459. { 
  460.   if( !isset( $this->clientFD[$clientId] ) || $this->clientFD[$clientId] == NULL ) 
  461.   return false; 
  462.   return $this->clientInfo[$clientId]; 
  463. } 
  464.   
  465. /** 
  466. * send a debug message 
  467. * 
  468. * @access private 
  469. * @param string $msg message to debug 
  470. */ 
  471. function sendDebugMessage( $msg ) 
  472. { 
  473.   if( !$this->debug ) 
  474.   return false; 
  475.   
  476.   $msg = date( "Y-m-d H:i:s", time() ) . " " . $msg; 
  477.   
  478.   switch( $this->debugMode ) 
  479.   { 
  480.   case "text": 
  481.    $msg = $msg."\n"; 
  482.    break; 
  483.   case "html": 
  484.    $msg = htmlspecialchars( $msg ) . "<br />\n"; 
  485.    break; 
  486.   } 
  487.   
  488.   if( $this->debugDest == "stdout" || emptyempty( $this->debugDest ) ) 
  489.   { 
  490.   echo $msg; 
  491.   flush(); 
  492.   return true; 
  493.   } 
  494.   
  495.   error_log( $msg, 3, $this->debugDest ); 
  496.   return true; 
  497. } 
  498.   
  499. /** 
  500. * return string for last socket error 
  501. * 
  502. * @access public 
  503. * @return string $error last error 
  504. */ 
  505. function getLastSocketError( &$fd ) 
  506. { 
  507.   $lastError = socket_last_error( $fd ); 
  508.   return "msg: " . socket_strerror( $lastError ) . " / Code: ".$lastError; 
  509. } 
  510. function onReceiveData($ip,$data){ 
  511.   
  512.   $this->broadcastData( $data,array(), true ); 
  513. } 
  514. } 
  515.   
  516.   
  517. $patServer = new patServer(); 
  518. $patServer->start(); 

Tags: socket聊天室

分享到: