NetUtil.php 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063
  1. <?php
  2. // vim: set expandtab cindent tabstop=4 shiftwidth=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | The Function Inc |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 2014, Function Inc. All rights reserved. |
  7. // +----------------------------------------------------------------------+
  8. // | Authors: The PHP Dev Team, ISRD, Function Inc. |
  9. // | sniferyuan <sniferyuan@gmail.com> |
  10. // +----------------------------------------------------------------------+
  11. /**
  12. * 封装一些常用的网络操作函数
  13. */
  14. abstract class NetUtil
  15. {
  16. /**
  17. * 错误编码
  18. */
  19. public static $errCode = 0;
  20. /**
  21. * 错误信息,无错误为''
  22. */
  23. public static $errMsg = '';
  24. /**
  25. * 清除错误信息,在每个函数的开始调用
  26. */
  27. private static function clearError()
  28. {
  29. self::$errCode = 0;
  30. self::$errMsg = '';
  31. }
  32. /**
  33. * 对socket_read的封装,支持多个包的传播,此函数针对TcpServer的
  34. * 前8个字节为消息的长度
  35. * 接下来的4个字节为错误编码
  36. * 接下来的是正文
  37. * @param socket socket句柄
  38. * @param int maxLength 能接收数据的字符串长度
  39. *
  40. * @return string 正确返回读取的数据,错误返回false
  41. */
  42. public static function tcpSocketRead(&$socket, $maxLength)
  43. {
  44. self::clearError();
  45. $str = @socket_read($socket, 10240);
  46. if ($str === false){
  47. self::$errCode = 10102;
  48. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  49. return false;
  50. }
  51. if (strlen($str) < 8) {
  52. self::$errCode = 10102;
  53. self::$errMsg = 'bad tcp bag';
  54. return false;
  55. }
  56. $len = trim(substr($str, 0, 8));
  57. if(!is_numeric($len)){
  58. self::$errCode = 10104;
  59. self::$errMsg = 'bad tcp bag';
  60. return false;
  61. }
  62. if ($len > $maxLength) {
  63. self::$errCode = 10105;
  64. self::$errMsg = 'tcp bag too big';
  65. return false;
  66. }
  67. $message = substr($str, 8);
  68. $n = strlen($message);
  69. if ($n == $len) {
  70. return $message;
  71. }
  72. if ($n > $len) {
  73. self::$errCode = 10104;
  74. self::$errMsg = 'bad tcp bag';
  75. return false;
  76. }
  77. while ($len > $n)
  78. {
  79. $tmp = @socket_read($socket, 10240);
  80. if ($tmp === false) {
  81. self::$errCode = 10102;
  82. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  83. return false;
  84. }
  85. $message .= $tmp;
  86. $n = strlen($message);
  87. unset($tmp);
  88. }
  89. if ($n != $len) {
  90. self::$errCode = 10105;
  91. self::$errMsg = 'bad tcp bag';
  92. return false;
  93. }
  94. return $message;
  95. }
  96. /**
  97. * 对socket_write的封装,支持多个包的传播,此函数针对TcpServer的
  98. *
  99. * @param socket socket句柄
  100. * @param string message 需要发送的消息
  101. *
  102. * @return bool 正确返回true,错误返回false
  103. */
  104. public static function tcpSocketWrite(&$socket, $message)
  105. {
  106. self::clearError();
  107. $len = strlen($message);
  108. $padStr = str_pad($len, 8, ' ', STR_PAD_RIGHT);
  109. $message = $padStr.$message;
  110. $len = $len + 8;
  111. $n = @socket_write($socket, $message, $len);
  112. if($n === false) {
  113. self::$errCode = 10103;
  114. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  115. return false;
  116. }
  117. while ($n < $len) {
  118. $tmp = substr($message, $n);
  119. $tmp_n = @socket_write($socket, $tmp, ($len - $n));
  120. if ($tmp_n === false) {
  121. self::$errCode = 10103;
  122. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  123. return false;
  124. }
  125. $n += $tmp_n;
  126. }
  127. if ($n == $len) {
  128. return true;
  129. }
  130. return false;
  131. }
  132. /**
  133. * 处理简单的tcp发包收包,只适合短包的操作,最大10k
  134. * 实际上受MTU[Maximum Transmission Unit]限制, 大部分网络设备的MTU都是1500, 故每个包不会超过1500 bytes
  135. *
  136. * @param string ip IP地址
  137. * @param int port 端口
  138. * @param string cmd 向server发送命令
  139. * @param int n 错误重试次数
  140. * @param int timeout_sec 超时秒
  141. * @param int timeout_usec 超时u秒
  142. *
  143. * @return string 错误返回false,正确返回收到的信息
  144. */
  145. public static function tcpCmd($ip, $port, $cmd, $n = 2, $timeout_sec = 2, $timeout_usec = 0)
  146. {
  147. self::clearError();
  148. $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  149. if(!$socket) {
  150. self::$errCode = 10101;
  151. self::$errMsg = @socket_strerror(@socket_last_error());
  152. oo::logs()->debug3(10101, 'tcpcmd.log');
  153. return false;
  154. }
  155. if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
  156. self::$errCode = 10106;
  157. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  158. @socket_close($socket);
  159. oo::logs()->debug3(10106, 'tcpcmd.log');
  160. return false;
  161. }
  162. if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
  163. self::$errCode = 10106;
  164. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  165. @socket_close($socket);
  166. oo::logs()->debug3(10106, 'tcpcmd.log');
  167. return false;
  168. }
  169. $ret = false;
  170. for ($i = 0; $i < $n; $i++){
  171. $ret = @socket_connect($socket, $ip, $port);
  172. if ($ret == true) break;
  173. }
  174. if ($ret === false) {
  175. self::$errCode = 10107;
  176. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  177. @socket_close($socket);
  178. oo::logs()->debug3(10107, 'tcpcmd.log');
  179. return false;
  180. }
  181. $len = strlen($cmd);
  182. $n = 0;
  183. $tmp = $cmd;
  184. while ($n < $len){
  185. $ret = @socket_write($socket, $tmp, $len);
  186. if ($ret == false) {
  187. self::$errCode = 10103;
  188. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  189. @socket_close($socket);
  190. oo::logs()->debug3(10103, 'tcpcmd.log');
  191. return false;
  192. }
  193. $n += $ret;
  194. if ($n < $len) {
  195. $tmp = substr($tmp, $ret);
  196. }
  197. }
  198. $rev = @socket_read($socket, 10240);
  199. if ($rev == false) {
  200. self::$errCode = 10102;
  201. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  202. oo::logs()->debug3(10102, 'tcpcmd.log');
  203. }
  204. @socket_close($socket);
  205. return $rev;
  206. }
  207. /**
  208. * 处理简单的tcp发包,不收包
  209. * 实际上受MTU[Maximum Transmission Unit]限制, 大部分网络设备的MTU都是1500, 故每个包不会超过1500 bytes
  210. *
  211. * @param string ip IP地址
  212. * @param int port 端口
  213. * @param string cmd 向server发送命令
  214. * @param int n 错误重试次数
  215. * @param int timeout_sec 超时秒
  216. * @param int timeout_usec 超时u秒
  217. *
  218. * @return string 错误返回false,正确返回收到的信息
  219. */
  220. public static function tcpCmdNotRecv($ip, $port, $cmd, $n = 2, $timeout_sec = 2, $timeout_usec = 0)
  221. {
  222. self::clearError();
  223. $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  224. if(!$socket) {
  225. self::$errCode = 10101;
  226. self::$errMsg = @socket_strerror(@socket_last_error());
  227. return false;
  228. }
  229. if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
  230. self::$errCode = 10106;
  231. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  232. @socket_close($socket);
  233. return false;
  234. }
  235. if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
  236. self::$errCode = 10106;
  237. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  238. @socket_close($socket);
  239. return false;
  240. }
  241. $ret = false;
  242. for ($i = 0; $i < $n; $i++){
  243. $ret = @socket_connect($socket, $ip, $port);
  244. if ($ret == true) break;
  245. }
  246. if ($ret === false) {
  247. self::$errCode = 10107;
  248. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  249. @socket_close($socket);
  250. return false;
  251. }
  252. $len = strlen($cmd);
  253. $n = 0;
  254. $tmp = $cmd;
  255. while ($n < $len){
  256. $ret = @socket_write($socket, $tmp, $len);
  257. if ($ret == false) {
  258. self::$errCode = 10103;
  259. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  260. @socket_close($socket);
  261. return false;
  262. }
  263. $n += $ret;
  264. if ($n < $len) {
  265. $tmp = substr($tmp, $ret);
  266. }
  267. }
  268. @socket_close($socket);
  269. return true;
  270. }
  271. /**
  272. * 处理简单的tcp发包收包,只适合短包的操作,最大10k
  273. * 实际上受MTU[Maximum Transmission Unit]限制, 大部分网络设备的MTU都是1500, 故每个包不会超过1500 bytes
  274. *
  275. * @param string ip IP地址
  276. * @param int port 端口
  277. * @param string cmd 向server发送命令
  278. * @param int n 错误重试次数
  279. * @param int timeout_sec 超时秒
  280. * @param int timeout_usec 超时u秒
  281. * @param string end 结束符
  282. * @param bool flag true表示还要收返回包,false不用收
  283. *
  284. * @return string 错误返回false,正确返回收到的信息
  285. */
  286. public static function tcpLongCmd($ip, $port, $cmd, $n = 2, $timeout_sec = 2, $timeout_usec = 0, $end = "\0", $flag = true )
  287. {
  288. self::clearError();
  289. $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  290. if(!$socket) {
  291. self::$errCode = 10101;
  292. self::$errMsg = @socket_strerror(@socket_last_error());
  293. return false;
  294. }
  295. if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
  296. self::$errCode = 10106;
  297. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  298. @socket_close($socket);
  299. return false;
  300. }
  301. if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
  302. self::$errCode = 10106;
  303. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  304. @socket_close($socket);
  305. return false;
  306. }
  307. $ret = false;
  308. for ($i = 0; $i < $n; $i++){
  309. $ret = @socket_connect($socket, $ip, $port);
  310. if ($ret == true) break;
  311. }
  312. if ($ret === false) {
  313. self::$errCode = 10107;
  314. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  315. @socket_close($socket);
  316. return false;
  317. }
  318. $len = strlen($cmd);
  319. $n = 0;
  320. $tmp = $cmd;
  321. while ($n < $len){
  322. $ret = @socket_write($socket, $tmp, $len);
  323. if ($ret == false) {
  324. self::$errCode = 10103;
  325. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  326. @socket_close($socket);
  327. return false;
  328. }
  329. $n += $ret;
  330. if ($n < $len) {
  331. $tmp = substr($tmp, $ret);
  332. }
  333. }
  334. $tmp = true;
  335. $rev = "";
  336. if ( $flag )
  337. {
  338. while ( !empty( $tmp ) )
  339. {
  340. $tmp = socket_read($socket, 1024);
  341. if ( empty( $tmp ) )
  342. {
  343. break;
  344. }
  345. $rev = $rev.$tmp;
  346. if ( $tmp[strlen($tmp) - 1] == $end )
  347. {
  348. break;
  349. }
  350. }
  351. }
  352. @socket_close($socket);
  353. return $rev;
  354. }
  355. /**
  356. * 对socket_recvfrom的封装,支持对包的校验,此函数针对udpServer的
  357. *
  358. * @param socket socket句柄
  359. * @param string message 需要发送的消息
  360. * @param string ip ip地址
  361. * @param int port 端口
  362. *
  363. * @return bool 正确返回true,错误返回false
  364. */
  365. public static function udpSocketRecvFrom(&$socket, $maxLength, &$ip, &$port)
  366. {
  367. self::clearError();
  368. $message = '';
  369. $n = @socket_recvfrom($socket, $message, $maxLength, 0, $ip, $port);
  370. if ($n === false){
  371. self::$errCode = 10104;
  372. self::$errMsg = @socket_strerror(@socket_last_error());
  373. return false;
  374. }
  375. if (strlen($message) < 8 || $n < 8) {
  376. self::$errCode = 10104;
  377. self::$errMsg = 'bad udp bag';
  378. return false;
  379. }
  380. $len = trim(substr($message, 0, 8));
  381. if(!is_numeric($len))
  382. {
  383. self::$errCode = 10104;
  384. self::$errMsg = 'bad udp bag';
  385. return false;
  386. }
  387. if ($len > $maxLength) {
  388. self::$errCode = 10105;
  389. self::$errMsg = 'udp bag too big';
  390. return false;
  391. }
  392. $message = substr($message, 8);
  393. $n = strlen($message);
  394. if ($n == $len) {
  395. return $message;
  396. }
  397. self::$errCode = 10104;
  398. self::$errMsg = 'bad udp bag';
  399. return false;
  400. }
  401. /**
  402. * 对socket_sendto的封装,支持对包的校验,此函数针对udpServer的,加8个字符的长度
  403. *
  404. * @param socket socket句柄
  405. * @param string message 需要发送的消息
  406. * @param string ip ip地址
  407. * @param int port 端口
  408. *
  409. * @return bool 正确返回true,错误返回false
  410. */
  411. public static function udpSocketSendTo(&$socket, $message, $ip, $port)
  412. {
  413. self::clearError();
  414. $len = strlen($message);
  415. $padStr = str_pad($len, 8, ' ', STR_PAD_RIGHT);
  416. $message = $padStr.$message;
  417. $len += 8;
  418. $n = @socket_sendto($socket, $message, $len, 0, $ip, $port);
  419. if ($n === $len) {
  420. self::$errCode = 10103;
  421. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  422. return true;
  423. }
  424. self::$errCode = 10103;
  425. self::$errMsg ='udp send error';
  426. return false;
  427. }
  428. /**
  429. * 正确返回接受到的数据,错误返回false
  430. *
  431. * @param string ip ip地址
  432. * @param int port 端口
  433. * @param string cmd 命令字符串
  434. * @param boolean isResponse 是否需要回复
  435. * @param int timeout 超时时间
  436. */
  437. public static function udpCmd($ip, $port, $cmd, $isResponse=true, $timeout = 2, $utmo = 0)
  438. {
  439. self::clearError();
  440. $socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
  441. if (!$socket) {
  442. self::$errCode = 10101;
  443. self::$errMsg = @socket_strerror(@socket_last_error());
  444. return false;
  445. }
  446. if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout, "usec"=>$utmo))){
  447. self::$errCode = 10106;
  448. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  449. @socket_close($socket);
  450. return false;
  451. }
  452. if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout, "usec"=>$utmo))){
  453. self::$errCode = 10106;
  454. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  455. @socket_close($socket);
  456. return false;
  457. }
  458. $n = @socket_sendto($socket, $cmd, 10240, 0, $ip, $port);
  459. if ($n == -1) {
  460. self::$errCode = 10103;
  461. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  462. @socket_close($socket);
  463. return false;
  464. }
  465. if ($isResponse === false) {
  466. return true;
  467. }
  468. $revBuf = '';
  469. $ret = @socket_recvfrom($socket, $revBuf, 10240, 0, $ip, $port);
  470. if ($ret == -1) {
  471. self::$errCode = 10102;
  472. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  473. @socket_close($socket);
  474. return false;
  475. }
  476. return $revBuf;
  477. }
  478. /**
  479. * 正确返回接受到的数据,错误返回false
  480. *
  481. * @param string ip ip地址
  482. * @param int port 端口
  483. * @param string cmd 命令字符串
  484. * @param boolean isResponse 是否需要回复
  485. * @param int timeout 超时时间
  486. */
  487. public static function udpPHPCmd($ip, $port, $cmd, $isResponse=true, $timeout = 2, $utmo = 0)
  488. {
  489. self::clearError();
  490. $socket = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
  491. if (!$socket) {
  492. self::$errCode = 10101;
  493. self::$errMsg = @socket_strerror(@socket_last_error());
  494. return false;
  495. }
  496. if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout, "usec"=>$utmo))){
  497. self::$errCode = 10106;
  498. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  499. @socket_close($socket);
  500. return false;
  501. }
  502. if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout, "usec"=>$utmo))){
  503. self::$errCode = 10106;
  504. self::$errMsg = @socket_strerror(@socket_last_error($socket));
  505. @socket_close($socket);
  506. return false;
  507. }
  508. $n = self::udpSocketSendTo($socket, $cmd, $ip, $port);
  509. if ($n == false) {
  510. @socket_close($socket);
  511. return false;
  512. }
  513. if ($isResponse === false) {
  514. return true;
  515. }
  516. return self::udpSocketRecvFrom($socket, 10240, $ip, $port);
  517. }
  518. /**
  519. * TcpServer的客户端
  520. *
  521. * @param string ip ip地址
  522. * @param int port 端口
  523. * @param string cmd 命令字符串
  524. * @param int timeout_sec 超时秒
  525. * @param int timeout_usec 超时u秒
  526. *
  527. * @return string 正确返回接受到的数据,错误返回false
  528. */
  529. public static function tcpPHPCmd($ip, $port, $cmd, $n = 2, $timeout_sec = 2, $timeout_usec = 0)
  530. {
  531. self::clearError();
  532. $hostInfo = " to {$ip}:{$port} "; // 连接的 ip:port 信息
  533. $socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  534. if(!$socket) {
  535. self::$errCode = 10101;
  536. self::$errMsg = @socket_strerror(@socket_last_error()) . $hostInfo;
  537. return false;
  538. }
  539. if(!@socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
  540. self::$errCode = 10106;
  541. self::$errMsg = @socket_strerror(@socket_last_error($socket)) . $hostInfo;
  542. @socket_close($socket);
  543. return false;
  544. }
  545. if(!@socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout_sec, "usec"=>$timeout_usec))){
  546. self::$errCode = 10106;
  547. self::$errMsg = @socket_strerror(@socket_last_error($socket)) . $hostInfo;
  548. @socket_close($socket);
  549. return false;
  550. }
  551. $ret = false;
  552. for ($i = 0; $i < $n; $i++){
  553. $ret = @socket_connect($socket, $ip, $port);
  554. if ($ret == true) break;
  555. }
  556. if ($ret === false) {
  557. self::$errCode = 10107;
  558. self::$errMsg = @socket_strerror(@socket_last_error($socket)) . $hostInfo;
  559. @socket_close($socket);
  560. return false;
  561. }
  562. $ret = self::tcpSocketWrite($socket, $cmd);
  563. if ( $ret===false ) {
  564. @socket_close($socket);
  565. return false;
  566. }
  567. $rev = self::tcpSocketRead($socket, 1024000);
  568. @socket_close($socket);
  569. return $rev;
  570. }
  571. /**
  572. * 常用打包函数
  573. *
  574. * @author hickwu
  575. * @param mix $data 需要打包的数据
  576. * @return string
  577. */
  578. public static function wrap($data) {
  579. $str = serialize($data);
  580. return $str;
  581. }
  582. /**
  583. * 常用解包函数
  584. *
  585. * @author hickwu
  586. * @param string $str 需要解包的数据
  587. * @return mix 解包失败返回 false,成功返回打包数据
  588. */
  589. public static function unwrap($str)
  590. {
  591. self::clearError();
  592. $arr = unserialize($str);
  593. if ($arr === false) {
  594. self::$errCode = 10613;
  595. self::$errMsg = 'unserialize-err-' . serialize($str);
  596. }
  597. return $arr;
  598. }
  599. // ##################### Qzone 通用协议相关函数 ####################
  600. /**
  601. * 求二进制串校验和
  602. *
  603. * @author peterdu
  604. *
  605. * @param string $bytes, 二进制串
  606. *
  607. * @return short $sum, 校验和值
  608. */
  609. public static function checkSum($bytes) {
  610. self::clearError();
  611. $len = strlen($bytes);
  612. $sum = 0;
  613. $tail = 0;
  614. if ( $len % 2 )
  615. {
  616. $tail = ord( substr($bytes, -1) );
  617. }
  618. $len = (int)($len / 2);
  619. for ($i = 0; $i < $len; $i++)
  620. {
  621. $chunk = substr($bytes, $i * 2, 2);
  622. $sum += array_pop( unpack('S', $chunk) );
  623. }
  624. $sum += $tail;
  625. $sum = ($sum >> 16) + ($sum & 0xffff);
  626. $sum += ($sum >> 16);
  627. return hexdec( substr( dechex(~$sum), 4 ) );
  628. }
  629. /**
  630. * 构建 Qzone 通用协议包
  631. *
  632. * @author peterdu
  633. *
  634. * @param int $ver, 协议版本(注意: 通用协议源文件中, 此处传入为字符类型, php 中调用需传入其 ascii 码)
  635. * @param int $cmd, 协议命令字
  636. * @param string $body, 协议包体
  637. * @param bool $chk, 是否需要校验和, 可选参数, 默认为 false
  638. * @param int $sn, 协议序列号, 可选参数, 默认为0
  639. * @param int $color, 染色信息, 可选信息, 默认为0
  640. *
  641. * @return string $pack_str, 构建好的协议包
  642. */
  643. public static function structQzoneCmmPrtcl($ver, $cmd, $body, $chk = false, $sn = 0, $color = 0) {
  644. self::clearError();
  645. // 协议头标识字符
  646. $pre_ch = chr(4);
  647. // 协议版本号
  648. $ver = intval($ver);
  649. $ver = chr($ver);
  650. // 协议命令字
  651. $cmd = intval($cmd);
  652. $cmd = pack('N', $cmd);
  653. // 初始化校验和
  654. $init_checksum = pack('n', 0);
  655. // 协议序列号
  656. $sn = intval($sn);
  657. $sn = pack('N', $sn);
  658. // 协议染色信息
  659. $color = intval($color);
  660. $color = pack('N', $color);
  661. // 回应标识
  662. $respflag = chr(100);
  663. // 回应信息
  664. $respinfo = pack('n', 0);
  665. // 保留字段
  666. $reserve = chr(0);
  667. // 协议包长度
  668. $head_len = 25;
  669. $body_len = strlen($body);
  670. $pack_len = pack('N', $head_len + $body_len);
  671. // 协议尾标识字符
  672. $tail_ch = chr(5);
  673. $pack_str_1 = '';
  674. $pack_str_2 = '';
  675. $pack_str_1 .= $pre_ch;
  676. $pack_str_1 .= $ver;
  677. $pack_str_1 .= $cmd;
  678. $pack_str_2 .= $sn;
  679. $pack_str_2 .= $color;
  680. $pack_str_2 .= $respflag;
  681. $pack_str_2 .= $respinfo;
  682. $pack_str_2 .= $reserve;
  683. $pack_str_2 .= $pack_len;
  684. $pack_str_2 .= $body;
  685. $pack_str_2 .= $tail_ch;
  686. $pack_str = $pack_str_1 . $init_checksum . $pack_str_2;
  687. if ( $chk ) {
  688. // 计算实际校验和
  689. $act_checksum = self::checkSum($pack_str);
  690. $act_checksum = pack('s', $act_checksum);
  691. $pack_str = $pack_str_1 . $act_checksum . $pack_str_2;
  692. }
  693. return $pack_str;
  694. }
  695. /**
  696. * 解析 Qzone 通用协议包
  697. *
  698. * @author peterdu
  699. *
  700. * @param string $pack_str, 协议包(二进制串)
  701. * @param bool $chk, 是否检测校验和, 可选参数, 默认为 false
  702. *
  703. * @return array/bool $pack, 为协议包解析后的数组, 元素包括协议版本, 协议命令字, 回应标识, 协议体等, 错误返回 false
  704. */
  705. public static function parseQzoneCmmPrtcl($pack_str, $chk = false) {
  706. self::clearError();
  707. $data = array();
  708. $data['errcode'] = 0;
  709. $data['errmsg'] = '';
  710. $pack_len = strlen($pack_str);
  711. if ( !$pack_len ) {
  712. self::$errCode = 10612;
  713. self::$errMsg = 'empty package';
  714. return false;
  715. }
  716. // 若检测校验和
  717. if ( $chk ) {
  718. $chksum = self::checkSum($pack_str);
  719. if ( $chksum != 0x0 && $chksum != 0xffff )
  720. {
  721. self::$errCode = 10613;
  722. self::$errMsg = 'validate checksum failed';
  723. return false;
  724. }
  725. }
  726. // 均转换为10进制, 避免使用 unpack 引起符号问题
  727. $ver = hexdec( bin2hex( substr($pack_str, 1, 1) ) );
  728. $cmd = hexdec( bin2hex( substr($pack_str, 2, 4) ) );
  729. $checksum = hexdec( bin2hex( substr($pack_str, 6, 2) ) );
  730. $sn = hexdec( bin2hex( substr($pack_str, 8, 4) ) );
  731. $color = hexdec( bin2hex( substr($pack_str, 12, 4) ) );
  732. $respflag = hexdec( bin2hex( substr($pack_str, 16, 1) ) );
  733. $respinfo = hexdec( bin2hex( substr($pack_str, 17, 2) ) );
  734. $packlen = hexdec( bin2hex( substr($pack_str, 20, 4) ) );
  735. if ( $packlen != $pack_len ) {
  736. self::$errCode = 10614;
  737. self::$errMsg = 'package length is not match';
  738. return false;
  739. }
  740. // 截取包体
  741. $body = substr($pack_str, 24, -1);
  742. $pack = array();
  743. $pack['ver'] = $ver;
  744. $pack['cmd'] = $cmd;
  745. $pack['checksum'] = $checksum;
  746. $pack['sn'] = $sn;
  747. $pack['color'] = $color;
  748. $pack['respflag'] = $respflag;
  749. $pack['respinfo'] = $respinfo;
  750. $pack['packlen'] = $packlen;
  751. $pack['body'] = $body;
  752. return $pack;
  753. }
  754. // ##################### cURL 请求相关函数 ####################
  755. /**
  756. * 使用 cURL 实现 HTTP GET 请求
  757. *
  758. * @param string $url, 请求地址
  759. * @param string $host, 服务器 host 名, 默认为空(当一台机器有多个虚拟主机时需要指定 host)
  760. * @param int $timeout, 连接超时时间, 默认为2
  761. *
  762. * @return sting/bool $data, 为返回数据, 失败返回 false
  763. */
  764. public static function cURLHTTPGet($url, $timeout = 2, $host = '', $cookie = '', $exectime = 3) {
  765. self::clearError();
  766. $header = array('Content-transfer-encoding: text');
  767. if ( !empty($host) ) {
  768. $header[] = 'Host: ' . $host;
  769. }
  770. if ( defined( 'SITE_DOMAIN' ) && in_array(SITE_DOMAIN, array('renren') ) ) {
  771. $real_user_ip = $_SERVER['REMOTE_ADDR'] ;
  772. } else {
  773. $real_user_ip = defined( 'USER_REAL_IP' ) ? USER_REAL_IP : $_SERVER['REMOTE_ADDR'] ;
  774. }
  775. $header[] = 'X-FORWARDED-FOR: '.$real_user_ip ;
  776. $curl_handle = curl_init();
  777. // 连接超时
  778. curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, $timeout);
  779. // 执行超时
  780. curl_setopt($curl_handle, CURLOPT_TIMEOUT, $exectime);
  781. // HTTP返回错误时, 函数直接返回错误
  782. curl_setopt($curl_handle, CURLOPT_FAILONERROR, true);
  783. // 允许重定向
  784. curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
  785. // 允许重定向的最大次数
  786. curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 2);
  787. // 返回为字符串
  788. curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
  789. // 设置HTTP头
  790. curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $header);
  791. // 指定请求地址
  792. curl_setopt($curl_handle, CURLOPT_URL, $url);
  793. // 设置cookie
  794. curl_setopt($curl_handle, CURLOPT_COOKIE, $cookie) ;
  795. // 执行请求
  796. $response = curl_exec($curl_handle);
  797. if ( $response === false ) {
  798. self::$errCode = 10615;
  799. self::$errMsg = 'cURL errno: ' . curl_errno($curl_handle) . '; error: ' . curl_error($curl_handle);
  800. // 关闭连接
  801. curl_close($curl_handle);
  802. return false;
  803. }
  804. // 关闭连接
  805. curl_close($curl_handle);
  806. return $response;
  807. }
  808. /**
  809. * 使用 cURL 实现 服务器HTTP GET 请求 无header
  810. *
  811. * @param string $url, 请求地址
  812. * @param string $host, 服务器 host 名, 默认为空(当一台机器有多个虚拟主机时需要指定 host)
  813. * @param int $timeout, 连接超时时间, 默认为2
  814. * @return sting/bool $data, 为返回数据, 失败返回 false
  815. */
  816. public static function cURLHTTPGetNormal($url, $timeout = 2, $exectime = 3) {
  817. self::clearError();
  818. $curl_handle = curl_init();
  819. // 连接超时
  820. curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, $timeout);
  821. // 执行超时
  822. curl_setopt($curl_handle, CURLOPT_TIMEOUT, $exectime);
  823. // HTTP返回错误时, 函数直接返回错误
  824. curl_setopt($curl_handle, CURLOPT_FAILONERROR, true);
  825. // 允许重定向
  826. curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
  827. // 允许重定向的最大次数
  828. curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 2);
  829. // 返回为字符串
  830. curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
  831. // 指定请求地址
  832. curl_setopt($curl_handle, CURLOPT_URL, $url);
  833. // 执行请求
  834. $response = curl_exec($curl_handle);
  835. if ( $response === false ) {
  836. self::$errCode = 10615;
  837. self::$errMsg = 'cURL errno: ' . curl_errno($curl_handle) . '; error: ' . curl_error($curl_handle);
  838. // 关闭连接
  839. curl_close($curl_handle);
  840. return false;
  841. }
  842. // 关闭连接
  843. curl_close($curl_handle);
  844. return $response;
  845. }
  846. /**
  847. * 使用 cURL 实现 HTTP POST 请求
  848. *
  849. * @param string $url, 请求地址
  850. * @param string $post_data, 请求的post数据,一般为经过urlencode 和用&处理后的字符串
  851. * @param string $host, 服务器 host 名, 默认为空(当一台机器有多个虚拟主机时需要指定 host)
  852. * @param int $timeout, 连接超时时间, 默认为2
  853. *
  854. * @return sting/bool $data, 为返回数据, 失败返回 false
  855. */
  856. public static function cURLHTTPPost($url, $post_data, $timeout = 2, $host = '', $cookie = '', $exectime = 3) {
  857. self::clearError();
  858. $header = array('Content-transfer-encoding: text');
  859. if (is_string($post_data)) {
  860. $data_len = strlen($post_data);
  861. $header[] = 'Content-Length: ' . $data_len;
  862. }
  863. if ( !empty($host) ) {
  864. $header[] = 'Host: ' . $host;
  865. }
  866. $real_user_ip = defined( 'USER_REAL_IP' ) ? USER_REAL_IP : (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '');
  867. $header[] = 'X-FORWARDED-FOR: '.$real_user_ip ;
  868. $curl_handle = curl_init();
  869. // 连接超时
  870. curl_setopt($curl_handle, CURLOPT_CONNECTTIMEOUT, $timeout);
  871. // 执行超时
  872. curl_setopt($curl_handle, CURLOPT_TIMEOUT, $exectime);
  873. // HTTP返回错误时, 函数直接返回错误
  874. curl_setopt($curl_handle, CURLOPT_FAILONERROR, true);
  875. // 允许重定向
  876. curl_setopt($curl_handle, CURLOPT_FOLLOWLOCATION, true);
  877. // 允许重定向的最大次数
  878. curl_setopt($curl_handle, CURLOPT_MAXREDIRS, 2);
  879. // 返回为字符串
  880. curl_setopt($curl_handle, CURLOPT_RETURNTRANSFER, true);
  881. // 设置HTTP头
  882. curl_setopt($curl_handle, CURLOPT_HTTPHEADER, $header);
  883. // 指定请求地址
  884. curl_setopt($curl_handle, CURLOPT_URL, $url);
  885. //设置为post方式
  886. curl_setopt($curl_handle, CURLOPT_POST, TRUE);
  887. //post 参数
  888. curl_setopt($curl_handle, CURLOPT_POSTFIELDS, $post_data);
  889. // 设置cookie
  890. curl_setopt($curl_handle, CURLOPT_COOKIE, $cookie) ;
  891. // 执行请求
  892. $response = curl_exec($curl_handle);
  893. if ( $response === false ) {
  894. self::$errCode = 10616;
  895. self::$errMsg = 'cURL errno: ' . curl_errno($curl_handle) . '; error: ' . curl_error($curl_handle);
  896. // 关闭连接
  897. curl_close($curl_handle);
  898. return false;
  899. }
  900. // 关闭连接
  901. curl_close($curl_handle);
  902. return $response;
  903. }
  904. /**
  905. * 维护分表统一自增字段
  906. *
  907. * @param Mixed $code
  908. * @return 获取的可插入DB的ID
  909. */
  910. public static function getAutoId($code){
  911. self::clearError();
  912. if(empty($code) || !is_numeric($code)){
  913. self::$errCode = 5001;
  914. self::$errMsg = 'autoid code err';
  915. return false;
  916. }
  917. $autoIdSvr = Config::getIP('autoId');
  918. if($autoIdSvr === false){
  919. self::$errCode = Config::$errCode;
  920. self::$errMsg = Config::$errMsg;
  921. return false;
  922. }
  923. $bag = array(
  924. 'code'=>intval($code)
  925. );
  926. $bag = self::wrap($bag);
  927. $rev = self::tcpPHPCmd($autoIdSvr['IP'], $autoIdSvr['PORT'], $bag);
  928. if($rev === false){
  929. self::$errCode = self::$errCode;
  930. self::$errMsg = self::$errMsg;
  931. return false;
  932. }
  933. $rev = self::unwrap($rev);
  934. if(!is_array($rev)){
  935. self::$errCode = 5002;
  936. self::$errMsg = 'server returns errno ' . $rev;
  937. return false;
  938. }
  939. if(!isset($rev['value'])){
  940. self::$errCode = 5003;
  941. self::$errMsg = 'server returns no value';
  942. return false;
  943. }
  944. return intval($rev['value']);
  945. }
  946. }
  947. //End of script
  948. ?>