Ucenter 1.6和Discuz X2整合通信流程原理详细分析

Ucenter 1.6和Discuz X2整合通信流程原理详细分析
1. 当会员从登陆界面输入正确的用户名和密码单击登陆按钮后,我们看到打开源码 discuzx/source/class/class_member.php  找到第155行代码.
$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 之间:
function uc_user_synlogin($uid) {  
        $uid = intval($uid);  
        if(@include UC_ROOT.'./data/cache/apps.php') {  
                if(count($_CACHE['apps']) > 1) {  
                        $return = uc_api_post('user', 'synlogin', array('uid'=>$uid));  
                } else {  
                        $return = '';  
                }  
        }  
        return $return;  
}   
跟着这个方法流程,我们继续看 uc_api_post 方法签名, 仍然是在 discuzx/uc_client/client.php文件,找到54-74行
function uc_api_post($module, $action, $arg = array()) {  
        $s = $sep = '';  
        foreach($arg as $k => $v) {  
                $k = urlencode($k);  
                if(is_array($v)) {  
                        $s2 = $sep2 = '';  
                        foreach($v as $k2 => $v2) {  
                                $k2 = urlencode($k2);  
                                $s2 .= "$sep2{$k}[$k2]=".urlencode(uc_stripslashes($v2));  
                                $sep2 = '&';  
                        }  
                        $s .= $sep.$s2;  
                } else {  
                        $s .= "$sep$k=".urlencode(uc_stripslashes($v));  
                }  
                $sep = '&';  
        }  
        $postdata = uc_api_requestdata($module, $action, $s);  
        return uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20);  
}   
可以看到这个方法主要调用uc_api_requestdata 来组装一个post请求需要发送的数据,看这个方法的签名
function uc_api_requestdata($module, $action, $arg='', $extra='') {  
        $input = uc_api_input($arg);  
        $post = "m=$module&a=$action&inajax=2&release=".UC_CLIENT_RELEASE."&input=$input&appid=".UC_APPID.$extra;  
        return $post;  
}   
注意看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);

define('UC_API', 'http://www.itkuaixun.com/xxxx');  //uc_server地址  
define('UC_APPID', '1');   
3. 接着看uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20) 方法签名和调用原理
function uc_fopen2($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE) {  
        $__times__ = isset($_GET['__times__']) ? intval($_GET['__times__']) + 1 : 1;  
        if($__times__ > 2) {  
                return '';  
        }  
        $url .= (strpos($url, '?') === FALSE ? '?' : '&')."__times__=$__times__";  
        return uc_fopen($url, $limit, $post, $cookie, $bysocket, $ip, $timeout, $block);  
}   
最终由uc_fopen函数调用PHP函数fsockopen 或者 pfsockopen 打开一个socket 连接将数据用流的形式发送通知数据到uc_server 
function uc_fopen($url, $limit = 0, $post = '', $cookie = '', $bysocket = FALSE, $ip = '', $timeout = 15, $block = TRUE) {  
        $return = '';  
        $matches = parse_url($url);  
        !isset($matches['host']) && $matches['host'] = '';  
        !isset($matches['path']) && $matches['path'] = '';  
        !isset($matches['query']) && $matches['query'] = '';  
        !isset($matches['port']) && $matches['port'] = '';  
        $host = $matches['host'];  
        $path = $matches['path'] ? $matches['path'].($matches['query'] ? '?'.$matches['query'] : '') : '/';  
        $port = !emptyempty($matches['port']) ? $matches['port'] : 80;  
        if($post) {  
                $out = "POST $path HTTP/1.0\r\n";  
                $out .= "Accept: */*\r\n";  
                //$out .= "Referer: $boardurl\r\n";  
                $out .= "Accept-Language: zh-cn\r\n";  
                $out .= "Content-Type: application/x-www-form-urlencoded\r\n";  
                $out .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";  
                $out .= "Host: $host\r\n";  
                $out .= 'Content-Length: '.strlen($post)."\r\n";  
                $out .= "Connection: Close\r\n";  
                $out .= "Cache-Control: no-cache\r\n";  
                $out .= "Cookie: $cookie\r\n\r\n";  
                $out .= $post;  
        } else {  
                $out = "GET $path HTTP/1.0\r\n";  
                $out .= "Accept: */*\r\n";  
                //$out .= "Referer: $boardurl\r\n";  
                $out .= "Accept-Language: zh-cn\r\n";  
                $out .= "User-Agent: $_SERVER[HTTP_USER_AGENT]\r\n";  
                $out .= "Host: $host\r\n";  
                $out .= "Connection: Close\r\n";  
                $out .= "Cookie: $cookie\r\n\r\n";  
        }  
        if(function_exists('fsockopen')) {  
                $fp = @fsockopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout); 
        } elseif (function_exists('pfsockopen')) {  
                $fp = @pfsockopen(($ip ? $ip : $host), $port, $errno, $errstr, $timeout);
        } else {  
                $fp = false;  
        }  
        if(!$fp) {  
                return '';  
        } else {  
                stream_set_blocking($fp, $block);  
                stream_set_timeout($fp, $timeout);  
                @fwrite($fp, $out);  
                $status = stream_get_meta_data($fp);  
                if(!$status['timed_out']) {  
                        while (!feof($fp)) {  
                                if(($header = @fgets($fp)) && ($header == "\r\n" ||  $header == "\n")) {  
                                        break;  
                                }  
                        }  
                        $stop = false;  
                        while(!feof($fp) && !$stop) {  
                                $data = fread($fp, ($limit == 0 || $limit > 8192 ? 8192 : $limit));  
                                $return .= $data;  
                                if($limit) {  
                                        $limit -= strlen($data);  
                                        $stop = $limit <= 0;  
                                }  
                        }  
                }  
                @fclose($fp);  
                return $return;  
        }  
}   
至此,uc_client的调用流程结束,转入uc_server部份.
4. 继续打开 discuzx/uc_server/index.php  部份,找到52行,我们就可以完全理解ucenter的内部通知机制,当接收到uc_fopen2(UC_API.'/index.php', 500000, $postdata, '', TRUE, UC_IP, 20)  方法发送过来的请求后,
在这个方法里面查找对应的通知模块,这里我们以用户同步登陆为例,其它原理都是一样的,所以这里实际调用的是user
if(in_array($m, array('app', 'frame', 'user', 'pm', 'pm_client', 'tag', 'feed', 'friend', 'domain', 'credit', 'mail', 'version'))) {  
        if(file_exists(UC_ROOT.RELEASE_ROOT."control/$m.php")) {  
                include UC_ROOT.RELEASE_ROOT."control/$m.php";  
        } else {  
                include UC_ROOT."control/$m.php";  
        }  
        $classname = $m.'control';  
        $control = new $classname();  
        $method = 'on'.$a;  
        if(method_exists($control, $method) && $a{0} != '_') {  
                $data = $control->$method();  
                echo is_array($data) ? $control->serialize($data, 1) : $data;  
                exit;  
        } elseif(method_exists($control, '_call')) {  
                $data = $control->_call('on'.$a, '');  
                echo is_array($data) ? $control->serialize($data, 1) : $data;  
                exit;  
        } else {  
                exit('Action not found!');  
        }  
} else {  
        exit('Module not found!');  
}   
我们接着打开  discuzx/uc_server/control/user.php 文件源码, 在第32行开始,可以看到最终它调用onsynlogin方法,查询缓存的所有开启同步通知的应用,
// -1 未开启  
        function onsynlogin() {  
                $this->init_input();  
                $uid = $this->input('uid');  
                if($this->app['synlogin']) {  
                        if($this->user = $_ENV['user']->get_user_by_uid($uid)) {  
                                $synstr = '';  
//这里循环从缓存中读取所有需要发送通知的应用  
                                foreach($this->cache['apps'] as $appid => $app) {  
                                        if($app['synlogin']) {  
                                                $synstr .= '<script type="text/javascript" src="'.$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"></script>';  
                                                if(is_array($app['extra']['extraurl'])) foreach($app['extra']['extraurl'] as $extraurl) {  
                                                        $synstr .= '<script type="text/javascript" src="'.$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"></script>';  
                                                }  
                                        }  
                                }  
                                return $synstr;  
                        }  
                }  
                return '';  
        }   
使用Firefox安装好firebug可以看到,这就是为什么返回我们看到返回的一段javascript代码,如果不成功将返回-1(false) ,
$app['url'].'/api   看到这段代码,这也是为什么ucenter API文档中定义我们必须在根目录下面创建一个api文件夹, 使用P3P协议来解决cookie发送的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值