berrywu 发表于 2013-2-7 00:20:16

PHP+Flex实现多人聊天

      平常我们用PHP+JS语言写的多人聊天室代码都是使用Ajax技术,每隔一定的时间刷新聊天数据。
现在使用Flex+PHP语言,通过socket实现来实现客户端和服务端的交互。只要客户端发送新的消息给服务端,服务端就会自动把客户端的消息推送到每个客户端上。具体实现如下:
1、服务端代码(PHP实现)
<?PHP/*** patServer* PHP socket server base class* Events that can be handled:*   * onStart*   * onConnect*   * onConnectionRefused*   * onClose*   * onShutdown*   * onReceiveData** @version 1.1* @author   Stephan Schmidt <schst@php-tools.de>* @package patServer*/class patServer{/*** information about the project* @var array $systemVars*/var $systemVars   = array(    "appName"   => "patServer",    "appVersion"   => "1.1",    "author"   => array("Stephan Schmidt <schst@php-tools.de>", )   );/*** port to listen* @var integer   $port*/   var $port   = 10000;/*** domain to bind to* @var string $domain*/   var $domain   = "localhost";/*** maximum amount of clients* @var integer $maxClients*/   var $maxClients = -1;/*** buffer size for socket_read* @var integer $readBufferSize*/   var $readBufferSize   = 128;/*** end character for socket_read* @var integer $readEndCharacter*/   var $readEndCharacter = "\n";/*** maximum of backlog in queue* @var integer $maxQueue*/   var $maxQueue = 500;/*** debug mode* @var boolean $debug*/   var $debug   = true;/*** debug mode* @var string $debugMode*/   var $debugMode = "text";/*** debug destination (filename or stdout)* @var string $debugDest*/   var $debugDest = "stdout";/*** empty array, used for socket_select* @var array $null*/   var $null   = array();/*** all file descriptors are stored here* @var array $clientFD*/   var $clientFD = array();/*** needed to store client information* @var array $clientInfo*/   var $clientInfo = array();/*** needed to store server information* @var array $serverInfo*/   var $serverInfo = array();/*** amount of clients* @var integer   $clients*/   var $clients = 0;/*** create a new socket server** @access public* @param string   $domain   domain to bind to* @param integer   $port   port to listen to*/function patServer( $domain = "localhost", $port = 8083 ){   $this->domain = $domain;   $this->port   = $port;   $this->serverInfo["domain"]         = $domain;   $this->serverInfo["port"]         = $port;   $this->serverInfo["servername"]   = $this->systemVars["appName"];   $this->serverInfo["serverversion"] = $this->systemVars["appVersion"];   set_time_limit( 0 );}/*** set maximum amount of simultaneous connections** @access public* @param int $maxClients*/function setMaxClients( $maxClients ){   $this->maxClients = $maxClients;}/*** set debug mode** @access public* @param mixed $debug * @param string $dest destination of debug message (stdout to output or filename if log should be written)*/function setDebugMode( $debug, $dest = "stdout" ){   if( $debug === false ){    $this->debug = false;    return true;   }   $this->debug   = true;   $this->debugMode = $debug;   $this->debugDest = $dest;}/*** start the server** @access public* @param int $maxClients*/function start(){   $this->initFD = @socket_create( AF_INET, SOCK_STREAM, 0 );   if( !$this->initFD )    die( "patServer: Could not create socket." );   // adress may be reused   socket_setopt( $this->initFD, SOL_SOCKET, SO_REUSEADDR, 1 );   // bind the socket   if( !@socket_bind( $this->initFD, $this->domain, $this->port ) ){    @socket_close( $this->initFD );    die( "patServer: Could not bind socket to ".$this->domain." on port ".$this->port." ( ".$this->getLastSocketError( $this->initFd )." )." );   }   // listen on selected port   if( !@socket_listen( $this->initFD, $this->maxQueue ) )    die( "patServer: Could not listen ( ".$this->getLastSocketError( $this->initFd )." )." );   $this->sendDebugMessage( "Listening on port ".$this->port.". Server started at ".date( "H:i:s", time() ) );   // this allows the shutdown function to check whether the server is already shut down   $GLOBALS["_patServerStatus"] = "running";   // this ensures that the server will be sutdown correctly   register_shutdown_function( array( $this, "shutdown" ) );   if( method_exists( $this, "onStart" ) )    $this->onStart();   $this->serverInfo["started"] = time();   $this->serverInfo["status"]   = "running";   while( true ){    $readFDs = array();    array_push( $readFDs, $this->initFD );    // fetch all clients that are awaiting connections    for( $i = 0; $i < count( $this->clientFD ); $i++ )   if( isset( $this->clientFD[$i] ) )      array_push( $readFDs, $this->clientFD[$i] );    // block and wait for data or new connection    $ready = @socket_select( $readFDs, $this->null, $this->null, NULL );    if( $ready === false ){   $this->sendDebugMessage( "socket_select failed." );   $this->shutdown();    }    // check for new connection    if( in_array( $this->initFD, $readFDs ) ){   $newClient = $this->acceptConnection( $this->initFD );   // check for maximum amount of connections   if( $this->maxClients > 0 ){      if( $this->clients > $this->maxClients ){       $this->sendDebugMessage( "Too many connections." );       if( method_exists( $this, "onConnectionRefused" ) )      $this->onConnectionRefused( $newClient );       $this->closeConnection( $newClient );      }   }   if( --$ready <= 0 )      continue;    }    // check all clients for incoming data    for( $i = 0; $i < count( $this->clientFD ); $i++ ){   if( !isset( $this->clientFD[$i] ) )      continue;   if( in_array( $this->clientFD[$i], $readFDs ) ){      $data = $this->readFromSocket( $i );      // empty data => connection was closed      if( !$data ){       $this->sendDebugMessage( "Connection closed by peer" );       $this->closeConnection( $i );      }else{       $this->sendDebugMessage( "Received ".trim( $data )." from ".$i );       if( method_exists( $this, "onReceiveData" ) )      $this->onReceiveData( $i, $data );      }   }    }   }}/*** read from a socket** @access private* @param integer $clientId internal id of the client to read from* @return string $data   data that was read*/function readFromSocket( $clientId ){   // start with empty string   $data   = "";   // read data from socket   while( $buf = socket_read( $this->clientFD[$clientId], $this->readBufferSize ) ){    $data .= $buf;    $endString = substr( $buf, - strlen( $this->readEndCharacter ) );    if( $endString == $this->readEndCharacter )   break;    if( $buf == NULL )   break;   }   if( $buf === false )    $this->sendDebugMessage( "Could not read from client ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." )." );   return $data;}/*** accept a new connection** @access public* @param resource &$socket socket that received the new connection* @return int    $clientID internal ID of the client*/function acceptConnection( &$socket ){   for( $i = 0 ; $i <= count( $this->clientFD ); $i++ ){    if( !isset( $this->clientFD[$i] ) || $this->clientFD[$i] == NULL ){   $this->clientFD[$i] = socket_accept( $socket );   socket_setopt( $this->clientFD[$i], SOL_SOCKET, SO_REUSEADDR, 1 );   $peer_host = "";   $peer_port = "";   socket_getpeername( $this->clientFD[$i], $peer_host, $peer_port );   $this->clientInfo[$i] = array(            "host"   => $peer_host,            "port"   => $peer_port,            "connectOn" => time()             );   $this->clients++;   $this->sendDebugMessage( "New connection ( ".$i." ) from ".$peer_host." on port ".$peer_port );   if( method_exists( $this, "onConnect" ) )      $this->onConnect( $i );   return $i;    }   }}/*** check, whether a client is still connected** @access public* @param integer $id client id* @return boolean $connected true if client is connected, false otherwise*/function isConnected( $id ){   if( !isset( $this->clientFD[$id] ) )    return false;   return true;}/*** close connection to a client** @access public* @param int $clientID internal ID of the client*/function closeConnection( $id ){   if( !isset( $this->clientFD[$id] ) )    return false;   if( method_exists( $this, "onClose" ) )    $this->onClose( $id );   $this->sendDebugMessage( "Closed connection ( ".$id." ) from ".$this->clientInfo[$id]["host"]." on port ".$this->clientInfo[$id]["port"] );   @socket_close( $this->clientFD[$id] );   $this->clientFD[$id] = NULL;   unset( $this->clientInfo[$id] );   $this->clients--;}/*** shutdown server** @access public*/function shutDown(){   if( $GLOBALS["_patServerStatus"] != "running" )    exit;   $GLOBALS["_patServerStatus"] = "stopped";   if( method_exists( $this, "onShutdown" ) )    $this->onShutdown();   $maxFD = count( $this->clientFD );   for( $i = 0; $i < $maxFD; $i++ )    $this->closeConnection( $i );   @socket_close( $this->initFD );   $this->sendDebugMessage( "Shutdown server." );   exit;}/*** get current amount of clients** @access public* @return int $clients amount of clients*/function getClients(){   return $this->clients;}/*** send data to a client** @access public* @param int   $clientId ID of the client* @param string $data   data to send* @param boolean $debugData flag to indicate whether data that is written to socket should also be sent as debug message*/function sendData( $clientId, $data, $debugData = true ){   if( !isset( $this->clientFD[$clientId] ) || $this->clientFD[$clientId] == NULL )    return false;   if( $debugData )    $this->sendDebugMessage( "sending: \"" . $data . "\" to: $clientId" );   if( !@socket_write( $this->clientFD[$clientId], $data ) )    $this->sendDebugMessage( "Could not write '".$data."' client ".$clientId." ( ".$this->getLastSocketError( $this->clientFD[$clientId] )." )." );}/*** send data to all clients** @access public* @param string $data   data to send* @param array $exclude client ids to exclude*/function broadcastData( $data, $exclude = array(), $debugData = true ){   if( !empty( $exclude ) && !is_array( $exclude ) )    $exclude = array( $exclude );   for( $i = 0; $i < count( $this->clientFD ); $i++ ){    if( isset( $this->clientFD[$i] ) && $this->clientFD[$i] != NULL && !in_array( $i, $exclude ) ){   if( $debugData )      $this->sendDebugMessage( "sending: \"" . $data . "\" to: $i" );   if( !@socket_write( $this->clientFD[$i], $data ) )      $this->sendDebugMessage( "Could not write '".$data."' client ".$i." ( ".$this->getLastSocketError( $this->clientFD[$i] )." )." );    }   }}/*** get current information about a client** @access public* @param int   $clientId ID of the client* @return array $info   information about the client*/function getClientInfo( $clientId ){   if( !isset( $this->clientFD[$clientId] ) || $this->clientFD[$clientId] == NULL )    return false;   return $this->clientInfo[$clientId];}/*** send a debug message** @access private* @param string $msg message to debug*/function sendDebugMessage( $msg ){   if( !$this->debug )    return false;   $msg = date( "Y-m-d H:i:s", time() ) . " " . $msg;   switch( $this->debugMode ){    case "text":   $msg = $msg."\n";   break;    case "html":   $msg = htmlspecialchars( $msg ) . "<br />\n";   break;   }   if( $this->debugDest == "stdout" || empty( $this->debugDest ) ){    echo $msg;    flush();    return true;   }   error_log( $msg, 3, $this->debugDest );   return true;}/*** return string for last socket error** @access public* @return string $error last error*/function getLastSocketError( &$fd ){   $lastError = socket_last_error( $fd );   return "msg: " . socket_strerror( $lastError ) . " / Code: ".$lastError;}function onReceiveData($ip,$data){$this->broadcastData( $data,array(), true );}}$patServer = new patServer();$patServer->start();?> 
2、客户端(Flex实现)
<?xml version="1.0" encoding="utf-8"?><mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" fontSize="12" creationComplete="initApp()" width="369" height="326"><mx:Script>      <!.length>1)                                        {                                                //正则表达式,回车符                                                var myPattern:RegExp=/\r/;                                                //删除回车符                                                arr=arr.replace(myPattern,'');                                                //在聊天框中输出                                                myText.text+=arr+"\n";                                        }                              }                              myText.verticalScrollPosition = myText.maxVerticalScrollPosition;//滚动到最下面                        }                                                }      ]]></mx:Script>      <mx:TextArea x="10" y="42" width="344" height="247" id="myText"/>      <mx:TextInput x="10" y="297" width="270" id="myInput"/>      <mx:Button x="288" y="298" label="发送" id="myBtn" click="sendMessage(myName.text+':'+myInput.text)"/>      <mx:TextInput x="10" y="10" width="344" text="名字" id="myName"/>      </mx:Application> 
注意:该代码客户端放在本地可以执行,但是放到服务器上会出现安全沙箱问题,本人至今还未解决,遗憾
 
 
页: [1]
查看完整版本: PHP+Flex实现多人聊天