server = new swoole_websocket_server($host, $port,SWOOLE_PROCESS,SWOOLE_SOCK_TCP | SWOOLE_SSL); $this->server = new swoole_websocket_server($host, $port); $this->server->set([ // 输出限制 "buffer_output_size" => 1024 * 1024 * 1024, "max_connection" => 10240, "pipe_buffer_size" => 1024 * 1024 * 1024, "log_file" => WEBPATH_DIR.'cache/swoole.log', //'ssl_cert_file' => '/data/https/game.websocket/1_game.gamephp.com_bundle.crt', //'ssl_key_file' => '/data/https/game.websocket/2_game.gamephp.com.key', 'open_tcp_nodelay' => 1, 'open_cpu_affinity' => 1, 'task_worker_num' =>8, 'dispatch_mode'=>2, 'daemonize' => 0,//守护进程化。设置daemonize => 1时,程序将转入后台作为守护进程运行。长时间运行的服务器端程序必须启用此项。 'reactor_num' => 2,//2个处理线程的数量 'worker_num' => 4,//4个线程 'max_request' => 0,//最大10万个连接请求 'heartbeat_idle_time' => 50,//如果客户端超过600秒未向服务器发消息 'heartbeat_check_interval' =>55//每60秒检查一次所有的连接 ]); } public function start(){ $this->_echo_log(__CLASS__."->".__FUNCTION__); $this->server->on('start', [$this, 'onStart']); $this->server->on('shutdown', [$this, 'onShutdown']); $this->server->on('workerStart', [$this, 'onWorkerStart']); $this->server->on('workerStop', [$this, 'onWorkerStop']); $this->server->on('workerError', [$this, 'onWorkerError']); $this->server->on('connect', [$this, 'onConnect']); $this->server->on('request', [$this, 'onRequest']); $this->server->on('open', [$this, 'onOpen']); $this->server->on('message', [$this, 'onMessage']); $this->server->on('task', [$this, 'onTask']); $this->server->on('finish', [$this, 'onFinish']); $this->server->on('close', [$this, 'onClose']); $this->server->start(); } public function onOpen($server, $request) { $this->_echo_log(__CLASS__."->".__FUNCTION__); $this->_echo_log("1连接参数为:"); if(isset($request->get) && !empty($request->get)){ $tmp_arr = $request->get; //检查是否有关键数据 if(is_array($tmp_arr) && isset($tmp_arr['token'])){ $tmp_arr['token'] = urldecode($tmp_arr['token']); $tmp_arr['fd'] = $request->fd; $tmp_arr['ip'] = $request->server['remote_addr']; //如果数据正确,则记录连接成功的在线用户 if($this->_user_open($tmp_arr)){ $this->_echo_log("用户连接成功:".$tmp_arr['fd']."|".$tmp_arr['ip']); $data = array(); $data['fd'] = $tmp_arr['fd']; $data['ip'] = $tmp_arr['ip']; $data['a'] = "open"; $data['m'] = "user_login"; $data['d'] = array("code"=>'1',"msg"=>"连接成功!"); $this->server->task($data); return true; }else{//如果数据不正确,则不能连接 $this->_echo_log("用户连接失败了:".$tmp_arr['token']); $this->server->close($request->fd); } }else{//如果数据不正确则不能连接 $this->_echo_log("key数据不正确:",$tmp_arr); $this->server->close($request->fd); } }else{ $this->_echo_log("get数据不正确:\r\n"); $this->server->close($request->fd); } return true; } public function onMessage($server, $frame) { $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); $this->_echo_log("\r\n
上传的数据:".$frame->data." \r\n
"); $data = json_decode($frame->data,true); //如果有合法的参数,则执行请求的操作 if(!empty($data) && isset($data["a"]) && isset($data["m"]) && isset($data["d"]) ) { //判断两个要执行的内容是否正常 if (!preg_match("/^[a-zA-Z0-9_]+$/", $data["a"]) || !preg_match("/^[a-zA-Z0-9_]+$/", $data["m"])) { $this->_echo_log("要执行的方法里只能包含字母、数字、下画线\r\n"); return false; } //判断是否有这个类 if (!class_exists($data['m'])) { $this->_echo_log(__FILE__ . "找不到对应的类: " . $data['m'] . ":" . __LINE__); return false; } $data['fd'] = $frame->fd; $data['ip'] = $this->server->connections[$frame->fd]['remote_ip']; $tmp_task_id = $this->server->task($data); $this->_echo_log("进行task任务:".$tmp_task_id); }else{ $this->_echo_log($frame->data); $this->server->send_user($frame->fd,$data); } return true; } public function onClose($server, $fd) { $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); $this->_echo_log("client fd:{$fd} closed\r\n"); if($GLOBALS['USER_LIST_FD']->exist($fd)){ $uid = $GLOBALS['USER_LIST_FD']->get($fd); if(!empty($uid['uid'])){ $this->_user_close($uid['uid']); } $GLOBALS['USER_LIST_FD']->del($fd); $GLOBALS['USER_LIST']->del($uid['uid']); } return true; } public function onRequest($request,$response){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); $a = $request->post['a']; $m = $request->post['m']; $this->_echo_log($a."=>".$m."\r\n"); if ($a == 'login') { $response->end("true"); return true; } if(empty($request->post['token']) || empty($request->server['remote_addr'])){ $this->_echo_log("数据格式不正确"); $response->end("false"); return false; } $token = urldecode($request->post['token']); $ip = $request->server['remote_addr']; $act = new ajax_user_msg(); $tmp_data = $act->check_right($token,$ip); if($tmp_data){//检查数据格式以及数据是否正确 //判断两个要执行的内容是否正常 if ( !preg_match("/^[a-zA-Z0-9_]+$/", $tmp_data["a"]) || !preg_match("/^[a-zA-Z0-9_]+$/", $tmp_data["m"]) || !preg_match("/^[a-zA-Z0-9_]+$/", $tmp_data["do_a"]) || !isset($tmp_data["uid"]) || !isset($tmp_data["do_a"]) ) { $this->_echo_log("要执行的方法里只能包含字母、数字、下画线\r\n",$tmp_data); $response->end("false"); }else{ $tmp_data['ip'] = $ip; $tmp_return = $act->{$tmp_data['do_a']}($this,$tmp_data); if(is_array($tmp_return)){ $tmp_return = return_ajax($tmp_return); } $response->end($tmp_return); } }else{ $this->_echo_log($ip."|数据不正确|:".$token); $response->end("false"); } } public function onConnect($server, $fd){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); } public function onStart($server){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); } public function onShutdown($server){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); } public function onWorkerStart($server, $workerId){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); //在第一个工作进程时启动定时器,每秒执行一次 if($workerId==0){ /* $tmp_redis = new myredis(); swoole_timer_tick(1000,function() use($tmp_redis) { while($mydata = get_reids_push($tmp_redis,SYS_INFO_LIST)){ if($mydata){//从队列里拿消息数据出来发送 $tmp_fd = $this->get_user_by_uid($mydata['uid']); if(!empty($tmp_fd)){//如果是在当前服务器 $mydata['fd'] =$tmp_fd['fd']; $mydata['ip'] ='127.0.0.1'; $this->server->task($mydata); }else{//如果不是在当前服务器,则把数据放回去 set_reids_push($tmp_redis,USER_INFO_LIST,$mydata); } }else{ break; } } }); */ } } public function onWorkerStop($server, $workerId){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); } public function onWorkerError($server, $workerId, $workerPid, $exitCode, $sigNo){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); } public function onTask($server,$task_Id, $from_Id, $data){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); $data['server'] = $this; $act = new $data['m'](); //是否有这个方法在 if (!method_exists($act, $data['a'])) { $this->_echo_log(__FILE__ . "|" . $data['m'] . "找不到对应的方法: " . $data['a'] . ":" . __LINE__); unset($data,$act); $this->server->finish($task_Id); return false; } if($act->set_data($data)){ $this->_echo_log("执行情况:".$act->{$data['a']}() ); $this->server->finish($task_Id); return true; }else{ $this->_echo_log("执行出错:".$data['m']."->".$data['a']."\r\n"); $this->server->finish($task_Id); return false; } } public function onFinish($server, $data){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); } //根据用户ID查用户的连接记录 public function get_user_by_uid($uid){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); if($GLOBALS['USER_LIST']->exist($uid)){ $tmp_val = $GLOBALS['USER_LIST']->get($uid); return $tmp_val; }else{ return false; } } //根据用户的fd查用户的uid public function get_user_by_fd($fd){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); if($GLOBALS['USER_LIST_FD']->exist($fd)){ $tmp_val = $GLOBALS['USER_LIST_FD']->get($fd); return $tmp_val; }else{ return false; } } //设置用户在线 /* $data = array( "fd" => ,//"连接", "uid" => ,//用户ID, ); */ public function set_user_list($data){ //删掉这个uid之前绑定的连接 $tmp_uid = $GLOBALS['USER_LIST']->get($data['uid']); if($tmp_uid && $tmp_uid['fd']>0){ $GLOBALS['USER_LIST_FD']->del($tmp_uid['fd']); $GLOBALS['USER_LIST']->del($data['uid']); } $GLOBALS['USER_LIST']->set($data['uid'],array('fd'=>$data['fd'])); $GLOBALS['USER_LIST_FD']->set($data['fd'],array('uid'=>$data['uid'])); return true; } //给某个用户发信息 public function send_user($fd,$data,$opcode=1,$finish=true){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); if(empty($fd)){ return false; } if(is_array($data)){ $data = return_ajax($data); } //检测通道是否存在 if(!$this->server->exist($fd)){ $this->_echo_log("通道[ $fd ]的客户端已经断开,无法发送消息!\r\n",$data); unset($data); return false; }else{ $this->server->push($fd,$data,$opcode,$finish); unset($data); return $finish; } } //给指定用户发信息 public function send_all($arr,$data,$opcode=1,$finish=true){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); if(!empty($arr)){ foreach($arr as $uid){ if($GLOBALS['USER_LIST']->exist($uid)){ $tmp_fd = $GLOBALS['USER_LIST']->get($uid); if($this->send_user($tmp_fd['fd'],$data,$opcode,$finish)){ $this->_echo_log('用户['. $uid .']发送消息成功!\r\n'); }else{ $this->_echo_log('用户['. $uid .']的客户端已经断开,无法发送消息!\r\n'); } } } }else{ $this->_echo_log('没有指定要发信息的用户,无法发送消息!\r\n'); } return true; } //处理用户登入来的问题 /* $arr = array( "fd" =>'',//用户websocket连接 "token"=>'',//登陆token "ip"=>'',//客户端IP ); */ private function _user_open($arr){ if(empty($arr) || !isset($arr['token']) || $arr['token']==''){ $this->_echo_log('_user_open:消息不正确!\r\n'); return false; } $tmp_token = get_login_token($arr['token']); if(!empty($tmp_token) && isset($tmp_token['uid'])){ $data = array( "uid"=>$tmp_token['uid'], "fd"=>$arr['fd'] ); $this->set_user_list($data); return true; }else{ $this->_echo_log($tmp_token,$arr['token']."====>2"); return false; } } //根据玩家uid通知同房间的对手玩家掉线了,游戏结束 private function _user_close($uid){ $this->_echo_log(__CLASS__."->".__FUNCTION__."\r\n"); return true; } //打开调试 /* * $data=>要求输出的内容, * $data2=>要求输出的内容2 */ public function _echo_log($data,$data2=''){ //如果打开了调试 if($this->is_debug==true) { if(is_array($data) || is_object($data)) { var_dump($data); } else { echo(date('Y-m-d H:i:s')." | ".$data); } echo("\r\n"); if (!empty($data2)) { if(is_array($data2) || is_object($data2)) { var_dump($data2); } else { echo($data2); } echo("\r\n"); } } } } //redis连接池 class RedisPool{ protected $pool; function __construct(){ $this->pool = new SplQueue; } function put($redis){ $this->pool->push($redis); } function get(){ //有空闲连接 if (count($this->pool) > 0){ return $this->pool->pop(); } //无空闲连接,创建新连接 $redis = new myredis(); if ($redis == false) { return false; } else { return $redis; } } } //Mysql连接池 class MysqlPool{ protected $pool; function __construct(){ $this->pool = new SplQueue; } function put($conn){ $this->pool->push($conn); } function get(){ //有空闲连接 if (count($this->pool) > 0){ $conn = $this->pool->pop(); if($conn->myping()){ return $conn; }else{ $conn->Close(); } } //无空闲连接,创建新连接 $conn = new DB_ZDE(); if ($conn == false) { return false; } else { return $conn; } } }