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;
}
}
}