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


Posted in PHP onAugust 16, 2014
/**
* 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 = 10000 )
{
  $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();
PHP 相关文章推荐
从MySQL数据库表中取出随机数据的代码
Sep 05 PHP
php页面缓存ob系列函数介绍
Oct 18 PHP
PHP CURL CURLOPT参数说明(curl_setopt)
Sep 30 PHP
qq登录,新浪微博登录接口申请过程中遇到的问题
Jul 22 PHP
PHP中iconv函数转码时截断字符问题的解决方法
Jan 21 PHP
laravel容器延迟加载以及auth扩展详解
Mar 02 PHP
PHP实现对png图像进行缩放的方法(支持透明背景)
Jul 15 PHP
PHP内核探索:哈希表碰撞攻击原理
Jul 31 PHP
mysql desc(DESCRIBE)命令实例讲解
Sep 24 PHP
Zend Framework前端控制器用法示例
Dec 11 PHP
PHP编程实现脚本异步执行的方法
Aug 09 PHP
php多进程并发编程防止出现僵尸进程的方法分析
Feb 28 PHP
php与flash as3 socket通信传送文件实现代码
Aug 16 #PHP
php操作XML、读取数据和写入数据的实现代码
Aug 15 #PHP
php中操作memcached缓存进行增删改查数据的实现代码
Aug 15 #PHP
php上传图片之时间戳命名(保存路径)
Aug 15 #PHP
php中将一段数据存到一个txt文件中并显示其内容
Aug 15 #PHP
PHP反向代理类代码
Aug 15 #PHP
ThinkPHP中自定义目录结构的设置方法
Aug 15 #PHP
You might like
php 404错误页面实现代码
2009/06/22 PHP
黑夜路人出的几道php笔试题
2009/08/04 PHP
PHP采集相关教程之一 CURL函数库
2010/02/15 PHP
将一维或多维的数组连接成一个字符串的php代码
2010/08/08 PHP
基于simple_html_dom的使用小结
2013/07/01 PHP
Yii框架创建cronjob定时任务的方法分析
2017/05/23 PHP
PHP实现类似于C语言的文件读取及解析功能
2017/09/01 PHP
PHP注释语法规范与命名规范详解篇
2018/01/21 PHP
JQuery.validate在ie8下不支持的快速解决方法
2016/05/18 Javascript
Bootstrap实现带暂停功能的轮播组件(推荐)
2016/11/25 Javascript
javascript数据结构中栈的应用之符号平衡问题
2017/04/11 Javascript
js+html制作简单日历的方法
2017/06/27 Javascript
vue中component组件的props使用详解
2017/09/04 Javascript
NVM安装nodejs的方法实用步骤
2019/01/16 NodeJs
jquery无缝图片轮播组件封装
2020/11/25 jQuery
js实现div色块碰撞
2020/01/16 Javascript
JS操作Fckeditor的一些常用方法(获取、插入等)
2020/02/19 Javascript
详解微信小程序(Taro)手动埋点和自动埋点的实现
2021/03/02 Javascript
Python 找到列表中满足某些条件的元素方法
2018/06/26 Python
使用Python自动化破解自定义字体混淆信息的方法实例
2019/02/13 Python
基于python实现自动化办公学习笔记(CSV、word、Excel、PPT)
2019/08/06 Python
Python递归函数 二分查找算法实现解析
2019/08/12 Python
详解python中eval函数的作用
2019/10/22 Python
如何使用Python发送HTML格式的邮件
2020/02/11 Python
python 实现线程之间的通信示例
2020/02/14 Python
Python如何自动获取目标网站最新通知
2020/06/18 Python
python通用数据库操作工具 pydbclib的使用简介
2020/12/21 Python
CSS3字体效果的设置方法小结
2016/06/13 HTML / CSS
夜大毕业自我鉴定
2013/10/11 职场文书
个人职业生涯规划书1500字
2013/12/31 职场文书
幼儿园家长会欢迎词
2014/01/09 职场文书
劳动竞赛口号
2014/06/16 职场文书
酒店前台接待岗位职责
2015/04/02 职场文书
公司放假通知范文
2015/04/14 职场文书
面试通知邮件
2015/04/20 职场文书
2015暑假假期总结
2015/07/13 职场文书