ucenter+discuz通信原理!写的不错,转载保留

ucenter+discuz通信原理!

   

1. 当会员从登陆界面输入正确的用户名和密码单击登陆按钮后,我们看到打开源码discuzx/source/class/class_member.php  找到第155行代码.

 


 
  1. $ucsynlogin = $this->setting['allowsynlogin'] ? uc_user_synlogin($_G['uid']) : '';  

从上面的代码可以看到,当会员登陆验证通过后, 当你在ucenter管理面板里面设置开启同步登陆后,X2将会调用uc_user_synlogin($_G['uid']) 方法发出同步登陆的通知。

2. 调用uc_user_synlogin($_G['uid']) 方法后,实际调用的是discuzx/uc_client/client.php  中的 uc_user_synlogin函数,我们打开源码看方法签名,在310 - 320 之间:


 
  1. function uc_user_synlogin($uid) { 
  2.             $uid = intval($uid); 
  3.             if(@include UC_ROOT.'./data/cache/apps.php') { 
  4.                     if(count($_CACHE['apps']) > 1) { 
  5.                             $return = uc_api_post('user', 'synlogin', array('uid'=>$uid)); 
  6.                     } else { 
  7.                             $return = ''; 
  8.                     } 
  9.             } 
  10.             return $return; 
  11.     } 

跟着这个方法流程,我们继续看 uc_api_post 方法签名, 仍然是在discuzx/uc_client/client.php文件,找到54-74行

 


 
  1. function uc_api_post($module, $action, $arg = array()) { 
  2.             $s = $sep = ''; 
  3.             foreach($arg as $k => $v) { 
  4.                     $k = urlencode($k); 
  5.                     if(is_array($v)) { 
  6.                             $s2 = $sep2 = ''; 
  7.                             foreach($v as $k2 => $v2) { 
  8.                                     $k2 = urlencode($k2); 
  9.                                     $s2 .= "$sep2{$k}[$k2]=".urlencode(uc_stripslashes($v2)); 
  10.                                     $sep2 = '&'; 
  11.                             } 
  12.                             $s .= $sep.$s2; 
  13.                     } else { 
  14.                             $s .= "$sep$k=".urlencode(uc_stripslashes($v)); 
  15.                     } 
  16.                     $sep = '&'; 
  17.             } 
  18.             $postdata = uc_api_requestdata($module, $action, $s); 
  19.             return uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20); 
  20.     }  

可以看到这个方法主要调用

uc_api_requestdata

来组装一个post请求需要发送的数据,看这个方法的签名

 


 
  1. function uc_api_requestdata($module, $action, $arg='', $extra='') { 
  2.            $input = uc_api_input($arg); 
  3.            $post = "m=$module&a=$action&inajax=2&release=".UC_CLIENT_RELEASE."&input=$input&appid=".UC_APPID.$extra; 
  4.            return $post; 
  5.    }  

注意看uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20)函数中的 UC_API和 UC_APPID常量实际就是我们在discuzx/config/config_ucenter.php 文件中配置的预定的常量,相信到这里大家已经明白了这几个常量的用意,UC_API就是你定义的uc_server的URL.

return uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20);

 


 
  1. define('UC_API', 'http://www.itkuaixun.com/xxxx');  //uc_server地址 
  2.    define('UC_APPID', '1');  

 

3. 接着看uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20) 方法签名和调用原理

 


 
  1. function uc_fopen2($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE) { 
  2.             $__times__ = isset($_GET['__times__']) ? intval($_GET['__times__']) + 1 : 1; 
  3.             if($__times__ > 2) { 
  4.                     return ''; 
  5.             } 
  6.             $url .= (strpos($url, '?') === FALSE ? '?' : '&')."__times__=$__times__"; 
  7.             return uc_fopen($url, $limit, $post, $cookie, $bysocket, $ip, $timeout, $block); 
  8.     }  

最终由uc_fopen函数调用PHP函数fsockopen 或者 pfsockopen 打开一个socket 连接将数据用流的形式发送通知数据到uc_server 

 


 
  1. function uc_fopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE) { 
  2.             $return = ''; 
  3.             $matches = parse_url($url); 
  4.             !isset($matches['host']) && $matches['host'] = ''; 
  5.             !isset($matches['path']) && $matches['path'] = ''; 
  6.             !isset($matches['query']) && $matches['query'] = ''; 
  7.             !isset($matches['port']) && $matches['port'] = ''; 
  8.             $host = $matches['host']; 
  9.             $path = $matches['path'] ? $matches['path'].($matches['query'] ? '?'.$matches['query'] : '') : '/'; 
  10.             $port = !emptyempty($matches['port']) ? $matches['port'] : 80; 
  11.             if($post) { 
  12.                     $out = "POST $path HTTP/1.0\r\n"; 
  13.                     $out .= "Accept: **\r\n"; 
  14.                     //$out .= "Referer: $boardurl\r\n"; 
  15.                     $out .= "Accept-Language: zh-cn\r\n"; 
  16.                     $out .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n"; 
  17.                     $out .= "Host: $host\r\n"; 
  18.                     $out .= "Connection: Close\r\n"; 
  19.                     $out .= "Cookie: $cookie\r\n\r\n"; 
  20.             } 
  21.             if(function_exists('fsockopen')) { 
  22.                     $fp = @fsockopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout); 
  23.             } elseif (function_exists('pfsockopen')) { 
  24.                     $fp = @pfsockopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout); 
  25.             } else { 
  26.                     $fp = false; 
  27.             } 
  28.             if(!$fp) { 
  29.                     return ''; 
  30.             } else { 
  31.                     stream_set_blocking($fp, $block); 
  32.                     stream_set_timeout($fp, $timeout); 
  33.                     @fwrite($fp, $out); 
  34.                     $status = stream_get_meta_data($fp); 
  35.                     if(!$status['timed_out']) { 
  36.                             while (!feof($fp)) { 
  37.                                     if(($header = @fgets($fp)) && ($header == "\r\n" ||  $header == "\n")) { 
  38.                                             break; 
  39.                                     } 
  40.                             } 
  41.                             $stop = false; 
  42.                             while(!feof($fp) && !$stop) { 
  43.                                     $data = fread($fp, ($limit == 0 || $limit > 8192 ? 8192 : $limit)); 
  44.                                     $return .= $data; 
  45.                                     if($limit) { 
  46.                                             $limit -= strlen($data); 
  47.                                             $stop = $limit <= 0; 
  48.                                     } 
  49.                             } 
  50.                     } 
  51.                     @fclose($fp); 
  52.                     return $return; 
  53.             } 
  54.     }  

至此,uc_client的调用流程结束,转入uc_server部份.

 

4. 继续打开

discuzx/uc_server/index.php

  部份,找到52行,我们就可以完全理解ucenter的内部通知机制,当接收到

uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20)  方法发送过来的请求后,

 


 
  1. if(in_array($m, array('app', 'frame', 'user', 'pm', 'pm_client', 'tag', 'feed', 'friend', 'domain', 'credit', 'mail', 'version'))) { 
  2.             if(file_exists(UC_ROOT.RELEASE_ROOT."control/$m.php")) { 
  3.                     include UC_ROOT.RELEASE_ROOT."control/$m.php"; 
  4.             } else { 
  5.                     include UC_ROOT."control/$m.php"; 
  6.             } 
  7.             $classname = $m.'control'; 
  8.             $control = new $classname(); 
  9.             $method = 'on'.$a; 
  10.             if(method_exists($control, $method) && $a{0} != '_') { 
  11.                     $data = $control->$method(); 
  12.                     echo is_array($data) ? $control->serialize($data, 1) : $data; 
  13.                     exit; 
  14.             } elseif(method_exists($control, '_call')) { 
  15.                     $data = $control->_call('on'.$a, ''); 
  16.                     echo is_array($data) ? $control->serialize($data, 1) : $data; 
  17.                     exit; 
  18.             } else { 
  19.                     exit('Action not found!'); 
  20.             } 
  21.     } else { 
  22.             exit('Module not found!'); 
  23.     }  

在这个方法里面查找对应的通知模块,这里我们以用户同步登陆为例,其它原理都是一样的,所以这里实际调用的是user我们接着打开 discuzx/uc_server/control/user.php 文件源码, 在第32行开始,可以看到最终它调用onsynlogin方法,查询缓存的所有开启同步通知的应用,

 


 
  1. // -1 未开启 
  2.             function onsynlogin() { 
  3.                     $this->init_input(); 
  4.                     $uid = $this->input('uid'); 
  5.                     if($this->app['synlogin']) { 
  6.                             if($this->user = $_ENV['user']->get_user_by_uid($uid)) { 
  7.                                     $synstr = ''; 
  8.     //这里循环从缓存中读取所有需要发送通知的应用 
  9.                                     foreach($this->cache['apps'] as $appid => $app) { 
  10.                                             if($app['synlogin']) { 
  11.                                                     $synstr .= '$app['url'].'/api/'.$app['apifilename'].'?time='.$this->time.'&code='.urlencode($this->authcode('action=synlogin&username='.$this->user['username'].'&uid='.$this->user['uid'].'&password='.$this->user['password']."&time=".$this->time, 'ENCODE', $app['authkey'])).'" reload="1">'; 
  12.                                                     if(is_array($app['extra']['extraurl'])) foreach($app['extra']['extraurl'] as $extraurl) { 
  13.                                                             $synstr .= '$extraurl.'/api/'.$app['apifilename'].'?time='.$this->time.'&code='.urlencode($this->authcode('action=synlogin&username='.$this->user['username'].'&uid='.$this->user['uid'].'&password='.$this->user['password']."&time=".$this->time, 'ENCODE', $app['authkey'])).'" reload="1">'; 
  14.                                                     } 
  15.                                             } 
  16.                                     } 
  17.                                     return $synstr; 
  18.                             } 
  19.                     } 
  20.                     return ''; 
  21.             }  

使用Firefox安装好firebug可以看到,这就是为什么返回我们看到返回的一段javascript代码,如果不成功将返回-1(false) ,

 
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读