一、UC通信原理
我们用火狐可以看到,UCENTER跟应用通信的地址如下http://bbs.zhiyin.cn/ucenter/admin.php?m=app&a=ping&inajax=1&url=http%3A%2F%2Fapp.zhiyin.cn&ip=192.168.193.64&appid=33&random=1789609938&sid=d6c6MilHehc1GRqHPB14s6eyWNVp84nE3wbbjmf9MBnGQjsqRRnzY%2Fh2w9y1ZXMxvyeT7cSMsJRUaw
我们调试的时候用火狐可以在“应用管理”菜单中右侧右键点击,然后此框架在新标签页打开,这样在火狐firebug中可以清晰看到get过来的东西,其实也就是html中看到的testlink的地址,只不过加了个随机码而已。
其实请求的是uc服务端的/control/admin/app.php的onping方法(一般情况下会进入如下红色的那段的,而不是上面那个IF),代码如下
function onping() {
$ip = getgpc('ip');
$url = getgpc('url');
$appid = intval(getgpc('appid'));
$app = $_ENV['app']->get_app_by_appid($appid);
$status = '';
if($app['extra']['apppath'] && @include $app['extra']['apppath'].'./api/'.$app['apifilename']) {
$uc_note = new uc_note();
$status = $uc_note->test($note['getdata'], $note['postdata']);
} else {
$this->load('note');
$url = $_ENV['note']->get_url_code('test', '', $appid);
//$this->writelog('ping_url', $url);//如果需要看实际的URL为什么,可以在这里写日志后然后去data/logs里面去看url,其实是url/api/uc.php?code=XXXX,code解密后就是action跟time数组,也是后面提到的东西。
$status = $_ENV['app']->test_api($url, $ip);
}
if($status == 1) {
echo 'document.getElementById(\'status_'.$appid.'\').innerHTML = "'.$this->lang['app_connent_ok'].'";testlink();';
} else {
echo 'document.getElementById(\'status_'.$appid.'\').innerHTML = "'.$this->lang['app_connent_false'].'";testlink();';
}
}
请求的是model/app.php的test_api方法
function test_api($url, $ip = '') {
$this->base->load('misc');
if(!$ip) {
$ip = $_ENV['misc']->get_host_by_url($url);
}
if($ip < 0) {
return FALSE;
}
return $_ENV['misc']->dfopen($url, 0, '', '', 1, $ip);
}
请求的是model/misc.php的dfopen方法,这个方法就不贴了,其实就是一个跟远程端通信,看返回的是不是字符串1,是1就通信成功,0就是通信失败,通信的url是什么呢,
其实地址是用get_url_code方法获取的(可以在前面的ONPING方法里面看到是通过$_ENV['note']->get_url_code('test', '', $appid);获取的),在model/note里面
function get_url_code($operation, $getdata, $appid) {
$app = $this->apps[$appid];
$authkey = $app['authkey'];
$url = $app['url'];
$apifilename = isset($app['apifilename']) && $app['apifilename'] ? $app['apifilename'] : 'uc.php';
$action = $this->operations[$operation][1];
$code = urlencode($this->base->authcode("$action&".($getdata ? "$getdata&" : '')."time=".$this->base->time, 'ENCODE', $authkey));
return $url."/api/$apifilename?code=$code";
}
可以看出来是与在应用中填写的地址的api目录的appfilename通信,不修改的话将是uc.php
其实如上只是叙述逻辑,真正的UC通信失败大多是因为数据库链接没写正确,服务器时间没有设置对,客户端没有权限链接服务端数据库,而都可以通过如下几个方式来排查错误。
所以
1、UCENTER是否支持内网通信(可以在model/misc.php中的dfopen方法中//$this->writelog('fp', $fp);中看结果,返回资源id则表示本身跟客户端通信成功,是客户端返回错误)
绝对支持的,我们平常的通信失败一般都是客户端这块写错了,因此我们可以在客户端的api目录里面写一个uc2.php,内容为,同时在uc应用中将接口文件名换成uc2.php,如果能通信成功则表示UC服务端跟客户端通信没问题,是你的uc.php写错了,问题排查要在这里排查。如果不能通信,则是UC服务端无法通过fsockopen访问客户端,可能是域名解析问题,也可能是你填写的IP问题,有IP的话服务端会优先用IP跟客户端通信的。
2、UCENTER中填不填IP以及IP的作用
其实填写IP后的通信速度要比填域名快,这个应用地址其实只是起个域名解析的作用的,最终依然会与客户端的这个应用的IP进行通信,也就是api里面的uc.php通信。如果你的应用没上线之前是可以填写个局域网的IP用于测试的,不过要保证UC的服务端能跟客户端的这个IPfsckopen,否则也是会失败的,检测方法见第一条。
3、UCENTER通信是否需要客户端的数据库填写正确以及UC的参数填写正确(其实UC应用管理里面生成的数据库链接参数并不一定在客户端有权限链接UC的数据库服务器,这也是导致UC通信失败的主要原因。具体排查方式如下。)
uc.php一般会通过数据库连接的方式跟uc的数据库表通信,UC在跟客户端通信的时候会发送一个code参数给客户端,
$code = @$_GET['code'];
parse_str(_authcode($code, 'DECODE', UC_KEY), $get);
if(MAGIC_QUOTES_GPC) {
$get = _stripslashes($get);
}
这个code包含了两部分的参数,一部分是time,一部分是action,都会放到$get数组里面,因此我们在调试uc.php的时候,需要自己写类似如下代码模拟从uc发过来的数据
$timestamp = time();
$get['time']=time();//新增行
$get['action']='test';//新增行
if($timestamp - $get['time'] > 3600) {
exit('Authracation has expiried');
}
这样浏览器直接访问http://app.zhiyin.cn/api/uc.php就可以准确反映ucenter跟uc.php的通信结果
4、终极杀招,服务器时间没有设置对导致通信失败,TMD搞了我一上午。
我一步步调试最终到了model/misc.php的dfopen方法中,我在最后一行@fclose($fp);中加入一行$this->writelog('return', $return);因为misc.php中没有writelog方法,这个方法直接从model/admin.php中提取即可,然后将客户端的uc.php中加入一行
$timestamp = time();
/*$get['time']=time();
$get['action']='test';
*/
exit($get['time'].'|'.$timestamp.$get['action']);
if($timestamp - $get['time'] > 3600) {
exit('Authracation has expiried');
}
其实是因为我看/bbs/ucenter/data/logs/中写的日志中有Authracation has expiried我才想到可能是UC的服务器跟客户端的服务器不是一台服务器,两台服务器环境不同,取到的时间不同导致超时造成“假冒”的通信失败,这个通信失败是人为的访问客户端http://app.zhiyin.cn/api/uc.php发现不了的,因为你肯定是给time以及action赋值了来看是否是客户端uc.php书写错误导致通信失败。但UC服务端实际发送过程中的时间跟客户端取到的时间不一致直接报超时。这个通信失败我告诉你,一般人是绝对发现不了这个问题的。