热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Ucenter1.6和DiscuzX2整合通信流程原理详细分析

本文将以本站IT快讯网整合DiscuzX2结合DiscuzX2用户登陆流程源码来分析整个Ucenter的通信流程原理步骤。1.当会员从登陆界面输入正确的用户名和密码单击登陆按钮后,我们看到打

本文将以本站IT快讯网整合Discuz X2结合Discuz X2用户登陆流程源码来分析整个Ucenter的通信流程原理步骤。

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 = !empty($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 .= '';
                                                if(is_array($app['extra']['extraurl'])) foreach($app['extra']['extraurl'] as $extraurl) {
                                                        $synstr .= '';
                                                }
                                        }
                                }
                                return $synstr;
                        }
                }
                return '';
        }

使用Firefox安装好firebug可以看到,这就是为什么返回我们看到返回的一段Javascript代码,如果不成功将返回-1(false) ,
$app['url'].'/api   看到这段代码,这也是为什么ucenter API文档中定义我们必须在根目录下面创建一个api文件夹, 使用P3P协议来解决COOKIE发送的问题.


到这里相信你已经明白整个UCenter的运行流程和原理,赶紧动手去整合一个。

Yii 和 Discuz X2完美整合演式地址:

IT快讯网


有问题欢迎回复,时间允许范围内将会第一时间回复。

转载请说明出自:   IT快讯网  |  原文地址: Ucenter 1.6和Discuz X2整合通信流程原理详细分析


关注我的微博:  http://t.qq.com/itkuaixun


推荐阅读
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • 本文介绍了在Linux下安装和配置Kafka的方法,包括安装JDK、下载和解压Kafka、配置Kafka的参数,以及配置Kafka的日志目录、服务器IP和日志存放路径等。同时还提供了单机配置部署的方法和zookeeper地址和端口的配置。通过实操成功的案例,帮助读者快速完成Kafka的安装和配置。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文介绍了django中视图函数的使用方法,包括如何接收Web请求并返回Web响应,以及如何处理GET请求和POST请求。同时还介绍了urls.py和views.py文件的配置方式。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
author-avatar
孙亦然5277
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有