六狼论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

新浪微博账号登陆

只需一步,快速开始

搜索
查看: 50|回复: 0

PHP+Flex实现多人聊天

[复制链接]

升级  17.33%

20

主题

20

主题

20

主题

秀才

Rank: 2

积分
76
 楼主| 发表于 2013-2-7 00:20:16 | 显示全部楼层 |阅读模式
      平常我们用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 [text|htmlfalse]* @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>        <![CDATA[        import flash.system.SecurityDomain;//沙箱安全尚未解决                import flash.net.Socket; //导入类包                import flash.utils.ByteArray;//ByteArray在读取数据时使用             //   private var socket: Socket;                private var sc:Socket;                internal function initApp():void                {                                  Security.allowDomain("*");//所有的写法                sc  = new Socket();                                        sc.connect("192.168.15.85",10000);//执行连接                        //监听连接成功事件                        sc.addEventListener(Event.CONNECT,funConnect);                        //监听关闭事件                        sc.addEventListener(Event.CLOSE,funClose);                        //监听服务器新信息                        sc.addEventListener(ProgressEvent.SOCKET_DATA,funSocket);                        //                }                internal function funConnect(event:Event):void                {                        myText.text+="连接已建立 \n";                }                internal function funClose(event:Event):void                {                        myText.text+="连接已关闭 \n";                }                internal function sendMessage(msg:String):void //发送数据到服务器                {                        //新建一个ByteArray来存放数据                        var message:ByteArray=new ByteArray();                        //写入数据,使用writeUTFBytes以utf8格式传数据,避免中文乱码                        message.writeUTFBytes(msg+"\n");                        //写入socket的缓冲区                        sc.writeBytes(message);//调用flush方法发送信息                        sc.flush();                        //清空消息框                        myInput.text="";                }                //接受服务器信息                internal function funSocket(event:ProgressEvent):void                {                        var msg:String="";                //循环读取数据,socket的bytesAvailable对象存放了服务器传来的所有数据                        while(event.target.bytesAvailable)                        {                                //强制使用utf8格式,避免中文乱码                                msg+=sc.readMultiByte(sc.bytesAvailable,"utf-8");                                //使用\n换行符号把信息切开                                var arr:Array=msg.split('\n');                                for(var i:int=0;i<arr.length;i++)                                {                                        if(arr[i].length>1)                                        {                                                //正则表达式,回车符                                                var myPattern:RegExp=/\r/;                                                //删除回车符                                                arr[i]=arr[i].replace(myPattern,'');                                                //在聊天框中输出                                                myText.text+=arr[i]+"\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> 
注意:该代码客户端放在本地可以执行,但是放到服务器上会出现安全沙箱问题,本人至今还未解决,遗憾
 
 
您需要登录后才可以回帖 登录 | 立即注册 新浪微博账号登陆

本版积分规则

快速回复 返回顶部 返回列表