Gowhich

Durban's Blog

使用curl登录很简单,关键是破解它的加密方式

这个加密方式是如何破解的,我才用的是nodejs的方式,直接执行里面的js的函数,让他自己进行加密解密,除掉了自己浪费时间去解密的方法

搭建nodejs,然后自己调用接口去传递参数解密,这个过程我写在了我的一篇文章里面 nodejs搭建web服务器 ,其实就是我的一个解密的接口,可以按照此方法进行搭建。

下面给出,最简单的部分,就是使用curl获取新浪微博的cookie

这里给出我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<?php
class Control
{

/**
* 获取新浪微博的登录的加密数据
* */
public static function getEntryData($servertime, $nonce, $password)
{
return self::curlRequest('http://xxx.xxx.xxx.xxx:8006/weibo?servertime=' . $servertime . '&nonce=' . $nonce . '&password=' . $password, '', '');
// return self::curlRequest('http://10.211.55.5:8006/weibo?servertime='.$servertime.'&nonce='.$nonce.'&password='.$password,'','');
}

/**
* CURL请求 辅助登录的
* @param String $url 请求地址
* @param Array $data 请求数据
*/
public static function curlRequest($url, $data = '', $cookieFile = '')
{
$ch = curl_init();
$option = [
CURLOPT_URL => $url,
CURLOPT_HEADER => 0,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_HTTPHEADER => ['Expect:'],
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4',
];
if ($cookieFile) {
$option[CURLOPT_COOKIEJAR] = $cookieFile;
$option[CURLOPT_COOKIEFILE] = $cookieFile;
}
if ($data) {
$option[CURLOPT_POST] = 1;
$option[CURLOPT_POSTFIELDS] = $data;
}

curl_setopt_array($ch, $option);
$response = curl_exec($ch);
if (curl_errno($ch) > 0) {
//echo "CURL ERROR:$url ".curl_error($ch);
}
curl_close($ch);
return $response;
}

/**
*
* 微博登录
* @param string $username
* @param string $password
* @param string $cookie_file
*/
public static function loginWeibo($username, $password, $cookie_file)
{

if (!empty($username) && !empty($password)) {
$preLoginData = self::curlRequest('http://login.sina.com.cn/sso/prelogin.php?entry=account&callback=sinaSSOController.preloginCallBack&su=' . base64_encode($username) . '&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.5)', '', $cookie_file);

preg_match('/sinaSSOController.preloginCallBack\((.*)\)/', $preLoginData, $preArr);
$jsonArr = json_decode($preArr[1], true);

if (is_array($jsonArr)) {
$postArr = [
'entry' => 'weibo',
'gateway' => 1,
'from' => '',
'savestate' => 7,
'useticket' => 1,
'pagerefer' => '',
'vsnf' => 1,
'su' => base64_encode(urlencode($username)),
'service' => 'sso',
'servertime' => $jsonArr['servertime'],
'nonce' => $jsonArr['nonce'],
'pwencode' => 'rsa2',
'rsakv' => $jsonArr['rsakv'],
'prelt' => 0,
'sp' => self::getEntryData($jsonArr['servertime'], $jsonArr['nonce'], $password),
'encoding' => 'UTF-8',
'url' => 'http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack',
'returntype' => 'META',
];

$loginData = self::curlRequest('http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.5)', $postArr, $cookie_file);

if ($loginData) {
$matchs = [];
preg_match('/replace\([\'|\"](.*?)[\'|\"]\)/', $loginData, $matchs);

$loginResult = self::curlRequest($matchs[1], '', $cookie_file);

$loginResultArr = [];
preg_match('/feedBackUrlCallBack\((.*?)\)/', $loginResult, $loginResultArr);

if (!empty($loginResultArr[1])) {
$userInfo = json_decode($loginResultArr[1]);

if ($userInfo->result) {
// echo "Login Success \n";
//throw new Exception('Login Success');
}
} else {
// echo "Login Failure \n";
//throw new Exception('Login Failure');
}
} else {
//echo "Login SinaWeibo Failure \n";
//throw new Exception('Login SinaWeibo Failure');
}
} else {
//echo "preLoginData \n";
//throw new Exception("$preLoginData");
}
} else {
//echo "Param Error. \n";
//throw new Exception('Param Error.');
}
}
}

获取加密的部分,其实我已经在我的上篇博文写过了,可以参考一下 nodejs搭建web服务器

nodejs的安装其实是很简单的,官方文档是有直接安装的文件的,之后linux下的稍微麻烦一点,不过只要解压一下,然后直接安装,也是比较简单的。

如果不懂的可以参考我的这篇文章nodejs的安装,测试,hello world

下面演示我的web服务器

第一个是引导文件:

直接看代码好了

index.js

1
2
3
4
5
6
7
8
9
10
var server = require('./server');
var router = require('./router');
var requestHandlers = require('./requestHandlers');
var handle = {};
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
handle["/weibo"] = requestHandlers.weibo;
handle["/baiduindex"] = requestHandlers.baiduindex;
server.start(router.route,handle);

看到上面的代码中,require了三个文件,下面我分别给出这三个文件

server.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var http = require('http');
var url = require('url');
var querystring = require("querystring");
function start(route,handle){
function onRequest(request,response){
var url_array = url.parse(request.url,true);
var pathname = url_array.pathname;
var query_param = url_array.query;

route(handle, pathname, response, query_param);
}
http.createServer(onRequest).listen(8006);
console.log("Server has started. listen:8006");
}
exports.start = start;

router.js

1
2
3
4
5
6
7
8
9
10
11
12
function route(handle,pathname, response, query_param){
// console.log("About to route a request for "+pathname);
if(typeof handle[pathname] === "function"){
return handle[pathname](response, query_param);
}else{
// console.log("No request handler found for " + pathname);
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not found");
response.end();
}
}
exports.route = route;

requestHandlers.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var querystring = require("querystring");
var sinaweibo = require("./sinaWeibo");
var baidu = require("./baiduIndex");

function start(response, query_param){
// console.log("Request handler 'start' was called.");
return "Hello Start";
}

function upload(response, query_param){
// console.log("Request handler 'upload' was called.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("You've sent: " + postData +" " + querystring.parse(postData).text);
response.end();
}

function weibo(response, query_param){
var content = sinaweibo.GetRSA(query_param.servertime,query_param.nonce,query_param.password);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(content);
response.end();
}
function baiduindex(response, query_param){
var content = baidu.execEval(query_param.input,query_param.key);
response.writeHead(200, {"Content-Type": "text/plain"});
response.write(content);
response.end();
}
exports.start = start;
exports.upload = upload;
exports.weibo = weibo;
exports.baiduindex = baiduindex;

在requestHandlers.js文件中,又引入了两个文件,分别是关于sinaWeibo和baiduIndex

在这两个文件中分别是关于对应的一个操作,如果你要针对于自己的操作,也可以自己加入到里面来。

sinaWeibo.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
var sinaSSOEncoder = sinaSSOEncoder || {};
(function(){
var hexcase = 0;
var chrsz = 8;
this.hex_sha1 = function(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));};
var core_sha1 = function(x, len) {
x[len >> 5] |= 0x80 << (24 - len % 32);
x[((len + 64 >> 9) << 4) + 15] = len;
var w = Array(80);
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
var e = -1009589776;

for(var i = 0; i < x.length; i += 16) {
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
var olde = e;

for(var j = 0; j < 80; j++) {
if(j < 16) w[j] = x[i + j];
else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
safe_add(safe_add(e, w[j]), sha1_kt(j)));
e = d;
d = c;
c = rol(b, 30);
b = a;
a = t;
}

a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
e = safe_add(e, olde);
}
return Array(a, b, c, d, e);

};
/*
* Perform the appropriate triplet combination function for the current
* iteration
*/
var sha1_ft = function(t, b, c, d) {
if(t < 20) return (b & c) | ((~b) & d);
if(t < 40) return b ^ c ^ d;
if(t < 60) return (b & c) | (b & d) | (c & d);
return b ^ c ^ d;
};

/*
* Determine the appropriate additive constant for the current iteration
*/
var sha1_kt = function(t) {
return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
(t < 60) ? -1894007588 : -899497514;
};
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
var safe_add = function(x, y) {
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
};

/*
* Bitwise rotate a 32-bit number to the left.
*/
var rol = function(num, cnt) {
return (num << cnt) | (num >>> (32 - cnt));
};

var str2binb = function(str) {
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz)
bin[i>>5] |= (str.charCodeAt(i /chrsz) & mask) << (24 - i%32);
return bin;
};
var binb2hex = function(binarray) {
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++){
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
}
return str;
};

this.base64 = {
encode:function(input) {
input = "" + input; // Convert to string for encode
if (input == "") return "";

var output = '';
var chr1, chr2, chr3 = '';
var enc1, enc2, enc3, enc4 = '';
var i = 0;
do {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)){
enc3 = enc4 = 64;
} else if (isNaN(chr3)){
enc4 = 64;
}
output = output+this._keys.charAt(enc1)+this._keys.charAt(enc2)+this._keys.charAt(enc3)+this._keys.charAt(enc4);
chr1 = chr2 = chr3 = '';
enc1 = enc2 = enc3 = enc4 = '';
} while (i < input.length);
return output;
},

_keys: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='

};
}).call(sinaSSOEncoder);

//RSA
;(function(){
/********************* jsbn.js start ************************/

// Copyright (c) 2005 Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.

// Basic JavaScript BN library - subset useful for RSA encryption.

// Bits per digit
var dbits;

// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);

// (public) Constructor
function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}

// return new, unset BigInteger
function nbi() { return new BigInteger(null); }

// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.

// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i,x,w,j,c,n) {
while(--n >= 0) {
var v = x*this[i++]+w[j]+c;
c = Math.floor(v/0x4000000);
w[j++] = v&0x3ffffff;
}
return c;
}
// am2 avoids a big mult-and-extract completely.
// Max digit bits should be <= 30 because we do bitwise ops
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
function am2(i,x,w,j,c,n) {
var xl = x&0x7fff, xh = x>>15;
while(--n >= 0) {
var l = this[i]&0x7fff;
var h = this[i++]>>15;
var m = xh*l+h*xl;
l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
w[j++] = l&0x3fffffff;
}
return c;
}
// Alternately, set max digit bits to 28 since some
// browsers slow down when dealing with 32-bit numbers.
function am3(i,x,w,j,c,n) {
var xl = x&0x3fff, xh = x>>14;
while(--n >= 0) {
var l = this[i]&0x3fff;
var h = this[i++]>>14;
var m = xh*l+h*xl;
l = xl*l+((m&0x3fff)<<14)+w[j]+c;
c = (l>>28)+(m>>14)+xh*h;
w[j++] = l&0xfffffff;
}
return c;
}
// Mozilla/Netscape seems to prefer am3
BigInteger.prototype.am = am3;
dbits = 28;


BigInteger.prototype.DB = dbits;
BigInteger.prototype.DM = ((1<<dbits)-1);
BigInteger.prototype.DV = (1<<dbits);

var BI_FP = 52;
BigInteger.prototype.FV = Math.pow(2,BI_FP);
BigInteger.prototype.F1 = BI_FP-dbits;
BigInteger.prototype.F2 = 2*dbits-BI_FP;

// Digit conversions
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
var BI_RC = new Array();
var rr,vv;
rr = "0".charCodeAt(0);
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
rr = "a".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
rr = "A".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;

function int2char(n) { return BI_RM.charAt(n); }
function intAt(s,i) {
var c = BI_RC[s.charCodeAt(i)];
return (c==null)?-1:c;
}

// (protected) copy this to r
function bnpCopyTo(r) {
for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
r.t = this.t;
r.s = this.s;
}

// (protected) set from integer value x, -DV <= x < DV
function bnpFromInt(x) {
this.t = 1;
this.s = (x<0)?-1:0;
if(x > 0) this[0] = x;
else if(x < -1) this[0] = x+DV;
else this.t = 0;
}

// return bigint initialized to value
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }

// (protected) set from string and radix
function bnpFromString(s,b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
continue;
}
mi = false;
if(sh == 0)
this[this.t++] = x;
else if(sh+k > this.DB) {
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
this[this.t++] = (x>>(this.DB-sh));
}
else
this[this.t-1] |= x<<sh;
sh += k;
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}

// (protected) clamp off excess high words
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}

// (public) return string representation in given radix
function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
var km = (1<<k)-1, d, m = false, r = "", i = this.t;
var p = this.DB-(i*this.DB)%k;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
while(i >= 0) {
if(p < k) {
d = (this[i]&((1<<p)-1))<<(k-p);
d |= this[--i]>>(p+=this.DB-k);
}
else {
d = (this[i]>>(p-=k))&km;
if(p <= 0) { p += this.DB; --i; }
}
if(d > 0) m = true;
if(m) r += int2char(d);
}
}
return m?r:"0";
}

// (public) -this
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }

// (public) |this|
function bnAbs() { return (this.s<0)?this.negate():this; }

// (public) return + if this > a, - if this < a, 0 if equal
function bnCompareTo(a) {
var r = this.s-a.s;
if(r != 0) return r;
var i = this.t;
r = i-a.t;
if(r != 0) return r;
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
return 0;
}

// returns bit length of the integer x
function nbits(x) {
var r = 1, t;
if((t=x>>>16) != 0) { x = t; r += 16; }
if((t=x>>8) != 0) { x = t; r += 8; }
if((t=x>>4) != 0) { x = t; r += 4; }
if((t=x>>2) != 0) { x = t; r += 2; }
if((t=x>>1) != 0) { x = t; r += 1; }
return r;
}

// (public) return the number of bits in "this"
function bnBitLength() {
if(this.t <= 0) return 0;
return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
}

// (protected) r = this << n*DB
function bnpDLShiftTo(n,r) {
var i;
for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
for(i = n-1; i >= 0; --i) r[i] = 0;
r.t = this.t+n;
r.s = this.s;
}

// (protected) r = this >> n*DB
function bnpDRShiftTo(n,r) {
for(var i = n; i < this.t; ++i) r[i-n] = this[i];
r.t = Math.max(this.t-n,0);
r.s = this.s;
}

// (protected) r = this << n
function bnpLShiftTo(n,r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<cbs)-1;
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
for(i = this.t-1; i >= 0; --i) {
r[i+ds+1] = (this[i]>>cbs)|c;
c = (this[i]&bm)<<bs;
}
for(i = ds-1; i >= 0; --i) r[i] = 0;
r[ds] = c;
r.t = this.t+ds+1;
r.s = this.s;
r.clamp();
}

// (protected) r = this >> n
function bnpRShiftTo(n,r) {
r.s = this.s;
var ds = Math.floor(n/this.DB);
if(ds >= this.t) { r.t = 0; return; }
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<bs)-1;
r[0] = this[ds]>>bs;
for(var i = ds+1; i < this.t; ++i) {
r[i-ds-1] |= (this[i]&bm)<<cbs;
r[i-ds] = this[i]>>bs;
}
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
r.t = this.t-ds;
r.clamp();
}

// (protected) r = this - a
function bnpSubTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]-a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c -= a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c -= a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c -= a.s;
}
r.s = (c<0)?-1:0;
if(c < -1) r[i++] = this.DV+c;
else if(c > 0) r[i++] = c;
r.t = i;
r.clamp();
}

// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}

// (protected) r = this^2, r != this (HAC 14.16)
function bnpSquareTo(r) {
var x = this.abs();
var i = r.t = 2*x.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < x.t-1; ++i) {
var c = x.am(i,x[i],r,2*i,0,1);
if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
r[i+x.t] -= x.DV;
r[i+x.t+1] = 1;
}
}
if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
r.s = 0;
r.clamp();
}

// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
// r != q, this != m. q or r may be null.
function bnpDivRemTo(m,q,r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
while(--j >= 0) {
// Estimate quotient digit
var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
y.dlShiftTo(j,t);
r.subTo(t,r);
while(r[i] < --qd) r.subTo(t,r);
}
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}

// (public) this mod a
function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}

// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }

Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;

// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
// justification:
// xy == 1 (mod m)
// xy = 1+km
// xy(2-xy) = (1+km)(1-km)
// x[y(2-xy)] = 1-k^2m^2
// x[y(2-xy)] == 1 (mod m^2)
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
// JS multiply "overflows" differently from C/C++, so care is needed here.
function bnpInvDigit() {
if(this.t < 1) return 0;
var x = this[0];
if((x&1) == 0) return 0;
var y = x&3; // y == 1/x mod 2^2
y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
// last step - calculate inverse mod DV directly;
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}

// Montgomery reduction
function Montgomery(m) {
this.m = m;
this.mp = m.invDigit();
this.mpl = this.mp&0x7fff;
this.mph = this.mp>>15;
this.um = (1<<(m.DB-15))-1;
this.mt2 = 2*m.t;
}

// xR mod m
function montConvert(x) {
var r = nbi();
x.abs().dlShiftTo(this.m.t,r);
r.divRemTo(this.m,null,r);
if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
return r;
}

// x/R mod m
function montRevert(x) {
var r = nbi();
x.copyTo(r);
this.reduce(r);
return r;
}

// x = x/R mod m (HAC 14.32)
function montReduce(x) {
while(x.t <= this.mt2) // pad x so am has enough room later
x[x.t++] = 0;
for(var i = 0; i < this.m.t; ++i) {
// faster way of calculating u0 = x[i]*mp mod DV
var j = x[i]&0x7fff;
var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
// use am to combine the multiply-shift-add into one call
j = i+this.m.t;
x[j] += this.m.am(0,u0,x,i,0,this.m.t);
// propagate carry
while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
}
x.clamp();
x.drShiftTo(this.m.t,x);
if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}

// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }

// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }

Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;

// (protected) true iff this is even
function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }

// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
function bnpExp(e,z) {
if(e > 0xffffffff || e < 1) return BigInteger.ONE;
var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
g.copyTo(r);
while(--i >= 0) {
z.sqrTo(r,r2);
if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
else { var t = r; r = r2; r2 = t; }
}
return z.revert(r);
}

// (public) this^e % m, 0 <= e < 2^32
function bnModPowInt(e,m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}

// protected
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.fromString = bnpFromString;
BigInteger.prototype.clamp = bnpClamp;
BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
BigInteger.prototype.drShiftTo = bnpDRShiftTo;
BigInteger.prototype.lShiftTo = bnpLShiftTo;
BigInteger.prototype.rShiftTo = bnpRShiftTo;
BigInteger.prototype.subTo = bnpSubTo;
BigInteger.prototype.multiplyTo = bnpMultiplyTo;
BigInteger.prototype.squareTo = bnpSquareTo;
BigInteger.prototype.divRemTo = bnpDivRemTo;
BigInteger.prototype.invDigit = bnpInvDigit;
BigInteger.prototype.isEven = bnpIsEven;
BigInteger.prototype.exp = bnpExp;

// public
BigInteger.prototype.toString = bnToString;
BigInteger.prototype.negate = bnNegate;
BigInteger.prototype.abs = bnAbs;
BigInteger.prototype.compareTo = bnCompareTo;
BigInteger.prototype.bitLength = bnBitLength;
BigInteger.prototype.mod = bnMod;
BigInteger.prototype.modPowInt = bnModPowInt;

// "constants"
BigInteger.ZERO = nbv(0);
BigInteger.ONE = nbv(1);

/********************* jsbn.js end ************************/

/********************* prng4.js start ************************/

// prng4.js - uses Arcfour as a PRNG

function Arcfour() {
this.i = 0;
this.j = 0;
this.S = new Array();
}

// Initialize arcfour context from key, an array of ints, each from [0..255]
function ARC4init(key) {
var i, j, t;
for(i = 0; i < 256; ++i)
this.S[i] = i;
j = 0;
for(i = 0; i < 256; ++i) {
j = (j + this.S[i] + key[i % key.length]) & 255;
t = this.S[i];
this.S[i] = this.S[j];
this.S[j] = t;
}
this.i = 0;
this.j = 0;
}

function ARC4next() {
var t;
this.i = (this.i + 1) & 255;
this.j = (this.j + this.S[this.i]) & 255;
t = this.S[this.i];
this.S[this.i] = this.S[this.j];
this.S[this.j] = t;
return this.S[(t + this.S[this.i]) & 255];
}

Arcfour.prototype.init = ARC4init;
Arcfour.prototype.next = ARC4next;

// Plug in your RNG constructor here
function prng_newstate() {
return new Arcfour();
}

// Pool size must be a multiple of 4 and greater than 32.
// An array of bytes the size of the pool will be passed to init()
var rng_psize = 256;

/********************* prng4.js end ************************/

/********************* rng.js start ************************/

// Random number generator - requires a PRNG backend, e.g. prng4.js

// For best results, put code like
// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
// in your main HTML document.

var rng_state;
var rng_pool;
var rng_pptr;

// Mix in a 32-bit integer into the pool
function rng_seed_int(x) {
rng_pool[rng_pptr++] ^= x & 255;
rng_pool[rng_pptr++] ^= (x >> 8) & 255;
rng_pool[rng_pptr++] ^= (x >> 16) & 255;
rng_pool[rng_pptr++] ^= (x >> 24) & 255;
if(rng_pptr >= rng_psize) rng_pptr -= rng_psize;
}

// Mix in the current time (w/milliseconds) into the pool
function rng_seed_time() {
rng_seed_int(new Date().getTime());
}

// Initialize the pool with junk if needed.
if(rng_pool == null) {
rng_pool = new Array();
rng_pptr = 0;
var t;

while(rng_pptr < rng_psize) { // extract some randomness from Math.random()
t = Math.floor(65536 * Math.random());
rng_pool[rng_pptr++] = t >>> 8;
rng_pool[rng_pptr++] = t & 255;
}
rng_pptr = 0;
rng_seed_time();
//rng_seed_int(window.screenX);
//rng_seed_int(window.screenY);
}

function rng_get_byte() {
if(rng_state == null) {
rng_seed_time();
rng_state = prng_newstate();
rng_state.init(rng_pool);
for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
rng_pool[rng_pptr] = 0;
rng_pptr = 0;
//rng_pool = null;
}
// TODO: allow reseeding after first request
return rng_state.next();
}

function rng_get_bytes(ba) {
var i;
for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
}

function SecureRandom() {}

SecureRandom.prototype.nextBytes = rng_get_bytes;

/********************* rng.js end ************************/

/********************* rsa.js start ************************/

// Depends on jsbn.js and rng.js

// Version 1.1: support utf-8 encoding in pkcs1pad2

// convert a (hex) string to a bignum object
function parseBigInt(str,r) {
return new BigInteger(str,r);
}

function linebrk(s,n) {
var ret = "";
var i = 0;
while(i + n < s.length) {
ret += s.substring(i,i+n) + "\n";
i += n;
}
return ret + s.substring(i,s.length);
}

function byte2Hex(b) {
if(b < 0x10)
return "0" + b.toString(16);
else
return b.toString(16);
}

// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
function pkcs1pad2(s,n) {
if(n < s.length + 11) { // TODO: fix for utf-8
alert("Message too long for RSA");
return null;
}
var ba = new Array();
var i = s.length - 1;
while(i >= 0 && n > 0) {
var c = s.charCodeAt(i--);
if(c < 128) { // encode using utf-8
ba[--n] = c;
}
else if((c > 127) && (c < 2048)) {
ba[--n] = (c & 63) | 128;
ba[--n] = (c >> 6) | 192;
}
else {
ba[--n] = (c & 63) | 128;
ba[--n] = ((c >> 6) & 63) | 128;
ba[--n] = (c >> 12) | 224;
}
}
ba[--n] = 0;
var rng = new SecureRandom();
var x = new Array();
while(n > 2) { // random non-zero pad
x[0] = 0;
while(x[0] == 0) rng.nextBytes(x);
ba[--n] = x[0];
}
ba[--n] = 2;
ba[--n] = 0;
return new BigInteger(ba);
}

// "empty" RSA key constructor
function RSAKey() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null;
}

// Set the public key fields N and e from hex strings
function RSASetPublic(N,E) {
if(N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N,16);
this.e = parseInt(E,16);
}
else
alert("Invalid RSA public key");
}

// Perform raw public operation on "x": return x^e (mod n)
function RSADoPublic(x) {
return x.modPowInt(this.e, this.n);
}

// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
function RSAEncrypt(text) {
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) return h; else return "0" + h;
}

// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
//function RSAEncryptB64(text) {
// var h = this.encrypt(text);
// if(h) return hex2b64(h); else return null;
//}

// protected
RSAKey.prototype.doPublic = RSADoPublic;

// public
RSAKey.prototype.setPublic = RSASetPublic;
RSAKey.prototype.encrypt = RSAEncrypt;
//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;

//暴露RSAKey
this.RSAKey = RSAKey;

//example:
// var rsa = new RSAKey();
// rsa.setPublic(encode_key, key_plus);
// password = rsa.encrypt(password);

}).call(sinaSSOEncoder);
// function getpass(pwd,servicetime,nonce,rsaPubkey){
// var RSAKey=new sinaSSOEncoder.RSAKey();
// RSAKey.setPublic(rsaPubkey,'10001');
// var password=RSAKey.encrypt([servicetime,nonce].join('\t')+'\n'+pwd);
// return password;
// }

function GetRSA(servertime,nonce,password){
var key="EB2A38568661887FA180BDDB5CABD5F21C7BFD59C090CB2D245A87AC253062882729293E5506350508E7F9AA3BB77F4333231490F915F6D63C55FE2F08A49B353F444AD3993CACC02DB784ABBB8E42A9B1BBFFFB38BE18D78E87A0E41B9B8F73A928EE0CCEE1F6739884B9777E4FE9E88A1BBE495927AC4A799B3181D6442443";
var RSAKey = new sinaSSOEncoder.RSAKey();
RSAKey.setPublic(key, '10001');
return(RSAKey.encrypt([servertime,nonce].join("\t") + "\n" + password));
}

exports.GetRSA = GetRSA;

baiduIndex.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
function Dec(input, key)
{
input = escape(input);
var int_key = "";
for(var I=0;I<key.length;I++)
{
int_key += key.charCodeAt(I).toString();
}

var app = input.substr(input.length - 13,input.length);
var app1 = app;

app = app ^ 99998999;

var str = input.substr(0,input.length - 13);
input = str;

int_key = int_key + app1;
var ret = "";

for(var I=0;I<input.length;I+=2)
{
var sig = input.substr(I,2);
sig = parseInt(sig,16);

var i = (I/2) % int_key.length;
var xor_key = int_key.substr(i,1);
sig = sig ^ xor_key;

ret += String.fromCharCode(sig);
}
return unescape(ret);
}

function execEval(input, key){
return Dec(input,key);
}
exports.execEval = execEval;

大家认真看的话,除了index.js文件,会发现每个文件最后都会有个exports的调用,这个按照我自己的理解是,使用exports可以使得引用他的文件直接调用里面的方法。

到这里我的web服务就基本上搭建完毕了。

在index.js文件中的

1
2
3
4
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers.upload;
handle["/weibo"] = requestHandlers.weibo;
handle["/baiduindex"] = requestHandlers.baiduindex;

这里其实就是给出了,url的访问地址

比如我如果要访问的话,我会这样写

http://10.211.55.5:8006/start,

http://10.211.55.5:8006/upload,

http://10.211.55.5:8006/weibo,

http://10.211.55.5:8006/baiduindex。

后面也是可以加上参数的,然后对应的处理文件上,加上自己的处理函数,来接受不同的参数,进行自己的一些逻辑操作。

下面可以测试一下了。

在根目录下,执行

1
node ./index.js

然后再浏览器上输入url地址,就可以访问了

出现的问题如标题:

问题原因是:

xib 中, 没有对File’s Owner 的Outlets view 进行绑定, 导致在父视图中插入子视图时出错, 在IB中拖拽Files’ Owner到view, 添加绑定后, 运行成功!
总结一下创建视图和绑定的步骤:

  • 创建控制器. File->New File->Iphone OS->Cocoa Touch Class->UIViewController subclass;

  • 创建xib. File->New File->Iphone OS->User Interface->View XIB

  • 绑定controller和view. 用Interface Builder打开xxx.xib, 点击Files’ Owner, 在Identity Inspector里面的Class Identity, 选择Step 1创建的控制器类, 接着拖拽File’s Owner到View中, 选择Outlets->view.先选中file’s owner(这个很重要)

资源来自:http://blog.csdn.net/thebesttome/article/details/7799893

属性 作用
UIBarStyle barStyle 控件的样式
id delegate 设置控件的委托
NSString *text 控件上面的显示的文字
NSString *prompt 显示在顶部的单行文字,通常作为一个提示行
NSString *placeholder 半透明的提示文字,输入搜索内容消失
BOOL showsBookmarkButton 是否在控件的右端显示一个书的按钮(没有文字的时候)
BOOL showsCancelButton 是否显示cancel按钮
BOOL showsSearchResultsButton 是否在控件的右端显示搜索结果按钮(没有文字的时候)
BOOL searchResultsButtonSelected 搜索结果按钮是否被选中
UIColor *tintColor bar的颜色(具有渐变效果)
BOOL translucent 指定控件是否会有透视效果
UITextAutocapitalizationType
autocapitalizationType
设置在什么的情况下自动大写
UITextAutocorrectionType
autocorrectionType
对于文本对象自动校正风格
UIKeyboardType
keyboardType
键盘的样式
NSArray *scopeButtonTitles 搜索栏下部的选择栏,数组里面的内容是按钮的标题
NSInteger selectedScopeButtonIndex 搜索栏下部的选择栏按钮的个数
BOOL showsScopeBar 控制搜索栏下部的选择栏是否显示出来

代理列表:

编辑代理
– searchBar:textDidChange:
– searchBar:shouldChangeTextInRange:replacementText:
– searchBarShouldBeginEditing:
– searchBarTextDidBeginEditing:
– searchBarShouldEndEditing:
– searchBarTextDidEndEditing:

点击按钮
– searchBarBookmarkButtonClicked:
– searchBarCancelButtonClicked:
– searchBarSearchButtonClicked:
– searchBarResultsListButtonClicked:

范围代理
– searchBar:selectedScopeButtonIndexDidChange:

searchBar使用小技巧

searchBar的范围控件showsScopeBar,官方学名叫Scope Buttons。
首先就要设置这个属性:
self.searchBar.showsScopeBar = YES;
然后要给他添加按钮。比如说,这样:self.searchBar.scopeButtonTitles = [NSArray arrayWithObjects:@"BOY",@"GIRL",@"ALL",nil];
还有一个很重要的事情就是我们要实现这个代理UISearchBarDelegate里的这个方法searchBar:selectedScopeButtonIndexDidChange:。告诉表格,你选择的范围是啥。
还有要是设置默认选择哪个按钮的话,要设置这个属性,像这样就是默认选中第1个啦。
self.searchBar.selectedScopeButtonIndex = 0;

在实现搜索功能时,界面使用UISearchBar比较好,它实现了很多搜索时使用到的东西,但是默认的风格可能和现有的风格不一致,所以需要我们想办法去修改一下默认的外观。

修改UISearchBar的背景颜色

UISearchBar是由两个subView组成的,一个是UISearchBarBackGround,另一个是UITextField. 要IB中没有直接操作背景的属性。
方法是直接将 UISearchBarBackGround移去
第一种解决方案:

1
2
3
4
5
6
7
8
9
10
seachBar=[[UISearchBar alloc] init];  
seachBar.backgroundColor=[UIColor clearColor];
for (UIView *subview in seachBar.subviews)
{
if ([subview isKindOfClass:NSClassFromString(@"UISearchBarBackground")])
{
[subview removeFromSuperview];
break;
}
}

第二种解决的方法:

1
[[searchbar.subviews objectAtIndex:0]removeFromSuperview];

为UISearchBar添加背景图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
UISearchBar* m_searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0, 44, 320, 41)];  
m_searchBar.delegate = self;
m_searchBar.barStyle = UIBarStyleBlackTranslucent;
m_searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
m_searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
m_searchBar.placeholder = _(@"Search");
m_searchBar.keyboardType = UIKeyboardTypeDefault;
//为UISearchBar添加背景图片
UIView *segment = [m_searchBar.subviews objectAtIndex:0];
UIImageView *bgImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Images/search_bar_bg.png"]];
[segment addSubview: bgImage];
//<---背景图片
[self.view addSubview:m_searchBar];
[m_searchBar release];

取消UISearchBar调用的键盘

1
[searchBar resignFirstResponder];

第一种方法

1
2
3
4
5
6
7
8
9
UISearchBar *mySearchBar = [[UISearchBar alloc] 
initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, 45)];
mySearchBar.delegate = self;
mySearchBar.showsCancelButton = NO;
mySearchBar.barStyle=UIBarStyleDefault;
mySearchBar.placeholder=@"Enter Name or Categary";
mySearchBar.keyboardType=UIKeyboardTypeNamePhonePad;
[self.view addSubview:mySearchBar];
[mySearchBar release];

第二种方法,在tableview上添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//add Table  
UITableView *myBeaconsTableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height-40) style:UITableViewStylePlain];
myBeaconsTableView.backgroundColor = [UIColor whiteColor];
myBeaconsTableView.delegate=self;
myBeaconsTableView.dataSource=self;
[myBeaconsTableView setRowHeight:40];
// Add searchbar
searchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, 40)];
searchBar.placeholder=@"Enter Name";
searchBar.delegate = self;
myBeaconsTableView.tableHeaderView = searchBar;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
[searchBar release];
[self.view addSubview:myBeaconsTableView];
[myBeaconsTableView release];

1
void NSLog(NSString *format, …);

基本上,NSLog很像printf,同样会在console中输出显示结果。不同的是,传递进去的格式化字符是NSString的对象,而不是char *这种字符串指针。

实例:

NSLog可以如下面的方法使用:

1
2
3
NSLog (@"this is a test");
NSLog (@"string is :%@", string);
NSLog (@"x=%d, y=%d", 10, 20);

但是下面的写法是不行的:

1
2
int i = 12345;
NSLog( @"%@", i );

原因是, %@需要显示对象,而int i明显不是一个对象,要想正确显示,要写成:

1
2
int i = 12345;
NSLog( @"%d", i );

格式:

NSLog的格式如下所示:

  • %@ 对象

  • %d, %i 整数

  • %u 无符整形

  • %f 浮点/双字

  • %x, %X 二进制整数

  • %o 八进制整数

  • %zu size_t%p 指针

  • %e 浮点/双字 (科学计算)

  • %g 浮点/双字

  • %s C 字符串

  • %.*s Pascal字符串

  • %c 字符

  • %C unicha

  • r%lld 64位长整数

  • (long long)%llu 无符64位长整数

  • %Lf 64位双字

下面举例子中空格我用【X】来表示

请看这条sql语句

1
2
3
INSERT INTO teleplay_tieba_daily
(`t_t_id`,`sum`,`post_sum`,`subject_sum`,`member_sum`,`check_sum`,`yesterday_fans_num`, `yesterday_post_num`, `yesterday_subject_num`,`yesterday_member_num`, `yesterday_check_num`,`spider_date`)
VALUES (236,1790,24728,1837,1790,17,0,0,0,0,0,'2013-04-22'),(240,239,6824,1082,239,5,0,0,0,0,0,'2013-04-22'),(5109,171,300,90,171,4,0,0,0,0,0,'2013-04-22'), (5139,97,2462,294,97,1,0,0,0,0,0,'2013-04-22'),(5153,36,1215,181,36,0,0,0,0,0,0,'2013-04-22'),(5276,26,759,115,26,0,0,0,0,0,0,'2013-04-22'),(5358,53,320,93,53,0,0,0,0,0,0,'2013-04-22'),(5387,3156,85687,4389,3156,54,0,0,0,0,0,'2013-04-22'),(5429,1181,13811,2430,1181,14,0,0,0,0,0,'2013-04-22'),(5480,46,749,117,46,0,0,0,0,0,0,'2013-04-22')

按道理说是没有问题的,但是给出的错误是:

Unknown column ‘yesterday_check_num’ in ‘field list’
注意,是字段没有

反复看了一下,通过借助phpmyadmin的选择字段的方式添加数据,执行后,居然成功了

1
2
3
INSERT INTO teleplay_tieba_daily
(`t_t_id`,`sum`,`post_sum`,`subject_sum`,`member_sum`,`check_sum`,`yesterday_fans_num`, `yesterday_post_num`,`yesterday_subject_num`, `yesterday_member_num`,`【x】yesterday_check_num`,`spider_date`)
VALUES (236,1790,24728,1837,1790,17,0,0,0,0,0,'2013-04-22'),(240,239,6824,1082,239,5,0,0,0,0,0,'2013-04-22'),(5109,171,300,90,171,4,0,0,0,0,0,'2013-04-22'),(5139,97,2462,294,97,1,0,0,0,0,0,'2013-04-22'), (5153,36,1215,181,36,0,0,0,0,0,0,'2013-04-22'),(5276,26,759,115,26,0,0,0,0,0,0,'2013-04-22'),(5358,53,320,93,53,0,0,0,0,0,0,'2013-04-22'),(5387,3156,85687,4389,3156,54,0,0,0,0,0,'2013-04-22'),(5429,1181,13811,2430,1181,14,0,0,0,0,0,'2013-04-22'),(5480,46,749,117,46,0,0,0,0,0,0,'2013-04-22')

发现字段的真相是’【x】yesterday_check_num’,但是我想,我最初在创建字段的时候,为什么mysql没有把左右两边的空格过滤掉,这是漏洞还是mysql故意要这样设计的呢?如果有一天一个像我一样的程序员,也做了类似的事情,后面的程序员做sql插入的时候,估计会很困惑。因为这个问题,不去仔仔细细的查找,根本找不出原因。

另外程序员在处理输入的字符的时候都会将两边的空格去掉才对

表中的每一行代表一个数据项的数据,和一个列通常代表一个属性的物品(一些列可能对应于复杂的表达式的属性或静态文本)。  CGridView既支持排 序和分页的数据项。排序和分页可以在AJAX模式或正常的页面请求。使用CGridView的一个好处是,当用户浏览器禁用JavaScript,排序和 分页自动退化普通页面请求和仍然正常运行。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<?php
$this->widget('zii.widgets.grid.CGridView', [
'id' => 'chapter-grid',
'dataProvider' => $model->search(), //数据结果集
'filter' => $model,
'columns' => [
'id',
//锚点<a href="http://www.gulianqiang.com/"></a>
[
'name' => 'name',
'type' => 'raw',
'value' => 'CHtml::link($data->name,"/book/$data->id")',
],
//图片
[
'name' => 'image',
'type' => 'image',
'value' => 'LImages::getPath("book").$data->image', //图片相对路径
],
//下拉列表
[
'name' => 'type',
'value' => 'Lookup::item("chapterType",$data->type)',
'filter' => Lookup::items('chapterType'),
],
//内容截取
[
'name' => 'content',
'type' => 'html',
'value' => 'mb_substr(htmlspecialchars_decode($data->content),0,100,"utf-8")',
],
//时间
[
'name' => 'create_time',
'type' => 'datetime',
],
// 根据相关信息读数据库
[
'name' => 'user_id',
'value' => 'User::model()->findbyPk($data->user_id)->username',
'filter' => false,
],
[
'class' => 'CButtonColumn',
],
],
]);

我的使用代码:

其实也是可以加入自己的方法的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
$this->widget('zii.widgets.grid.CGridView', [
'id' => 'link-grid',
'dataProvider' => $model->search(),
'summaryCssClass' => 'hide',
'itemsCssClass' => 'table table-bordered',
'emptyText' => '搜索结果为空',
'pagerCssClass' => 'pagination',
'filterCssClass' => '',
'pager' => [
'class' => 'CLinkPager',
'nextPageLabel' => '下一页',
'prevPageLabel' => '上一页',
'firstPageLabel' => '最前',
'lastPageLabel' => '最后',
'htmlOptions' => ['class' => ''],
'header' => '',
'hiddenPageCssClass' => '',
'selectedPageCssClass' => 'active',
'previousPageCssClass' => '',
'nextPageCssClass' => '',
],
'filter' => $model,
'columns' => [
'title' => [
'name' => 'title',
'type' => 'raw',
'value' => 'Helper::truncate_utf8_string($data->title, 20,\'......\')',
],
'url' => [
'name' => 'url',
'type' => 'raw',
'value' => 'Helper::truncate_utf8_string($data->url, 20,\'......\')',
],
'create_date',
[
'class' => 'CButtonColumn',
],
],
]);

以上已经提供常用的数据显示类型。基本可以将使用Yii框架开发web应用使用CGridView的情况都列出。

参考资料:http://www.gulianqiang.com/yii/158.html

简介

memcache模块是一个高效的守护进程,提供用于内存缓存的过程式程序和面向对象的方便的接口,特别是对于设计动态web程序时减少对数据库的访问。

memcache也提供用于通信对话(session_handler)的处理。

更多Memcache 模块相关信息可以到 http://www.danga.com/memcached/ 查阅。

memcache在php.ini中的配置项列表

memcache在php.ini中的配置项列表

名称 默认值 是否可变 改变日志
memcache.allow_failover “1” PHP_INI_ALL Available since memcache 2.0.2.
memcache.max_failover_attempts “20” PHP_INI_ALL Available since memcache 2.1.0.
memcache.chunk_size “8192” PHP_INI_ALL Available since memcache 2.0.2.
memcache.default_port “11211” PHP_INI_ALL Available since memcache 2.0.2.
memcache.hash_strategy “standard” PHP_INI_ALL Available since memcache 2.2.0.
memcache.hash_function “crc32” PHP_INI_ALL Available since memcache 2.2.0.
session.save_handler “files” PHP_INI_ALL Supported since memcache 2.1.2
session.save_path “” PHP_INI_ALL Supported since memcache 2.1.2
有关 PHP_INI_* 常量进一步的细节与定义参见PHP手册php.ini 配置选项。

以下是配置项的简要解释

memcache.allow_failover Boolean

在错误时是否透明的故障转移到其他服务器上处理(注:故障转移是动词)。

memcache.max_failover_attempts integer

定义服务器的数量类设置和获取数据,只联合 memcache.allow_failover 一同使用。

memcache.chunk_size integer

数据将会被分成指定大小(chunk_size)的块来传输,这个值(chunk_size)越小,写操作的请求就越多,如果发现其他的无法解释的减速,请试着将这个值增大到32768.

memcache.default_port string

当连接memcache服务器的时候,如果没有指定端口这个默认的tcp端口将被用。

memcache.hash_strategy string

控制在映射 key 到服务器时使用哪种策略。设置这个值一致能使hash 算法始终如一的使用于服务器接受添加或者删除池中变量时将不会被重新映射。设置这个值以标准的结果在旧的策略被使用时。

memcache.hash_function string

控制哪种 hsah 函数被应用于 key映射 到服务器过程中,默认值“crc32”使用 CRC32 算法,而“fnv”则表示使用 FNV-1a 算法。

session.save_handler string

通过设置这个值为memcache来确定使用 memcache 用于通信对话的处理(session handler)。

session.save_path string

定义用于通话存储的各服务器链接的分隔符号,例如:“tcp://host1:11211, tcp://host2:11211”。
每 服务器个链接可以包含被接受于该服务器的参数,比较类似使用 Memcache::addServer() 来添加的服务器,例如:“tcp://host1:11211?persistent=1&weight=1&timeout=1& amp; amp;retry_interval=15”。

memcache常量列表

memcache常量列表

名称 类型 描述
MEMCACHE_COMPRESSED integer 用于调整在使用 Memcache::set(), Memcache::add() 和 Memcache::replace() 几个函数时的压缩比率。
MEMCACHE_HAVE_SESSION integer 如果通信对话的处理(session handler)被允许使用其值为 1,其他情况值为 0。

Memcache Functions 函数列表

Memcache::connect

说明

bool Memcache::connect ( string $host [, int $port [, int $timeout ]] )

连接memcache服务器

参数
- -
$host(string) 服务器域名或ip
$port(int) 服务器tcp端口号,默认值是11211
$timeout 连接memcache进程的失效时间,在修改它的默认值1的时候要三思,以免失去所有memcache缓存的优势导致连接变得很慢。
返回值

如果成功则返回true,失败则返回false

范例
1
2
3
4
5
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
/* OO API */
$memcache = new Memcache;
$memcache->connect(‘memcache_host‘, 11211);

Memcache::pconnect

说明

bool Memcache::pconnect ( string $host [, int $port [, int $timeout ]] )以长连接方式连接服务器

参数
- -
$host(string) 服务器域名或ip
$port(int) 服务器tcp端口号,默认值是11211
$timeout 连接memcache进程的失效时间,在修改它的默认值1的时候要三思,以免失去所有memcache缓存的优势导致连接变得很慢。
返回值

如果成功则返回true,失败则返回false

范例
1
2
3
4
5
/* procedural API */
$memcache_obj = memcache_pconnect(‘memcache_host‘, 11211);
/* OO API */
$memcache_obj = new Memcache;
$memcache_obj->pconnect(‘memcache_host‘, 11211);

Memcache::close

说明

bool Memcache::close ( void )

关闭对象 (对常连接不起作用)

返回值

如果成功则返回true,失败则返回false

范例
1
2
3
4
5
6
7
8
9
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
/* do something here .. */
memcache_close($memcache_obj);
/* OO API */
$memcache_obj = new Memcache;
$memcache_obj->connect(‘memcache_host‘, 11211);
/* do something here .. */
$memcache_obj->close();

Memcache::addServer(#2-4)

说明

bool Memcache::addServer ( string $host [, int $port [, bool $persistent [, int $weight [, int $timeout [, int $retry_interval [, bool $status [, callback $failure_callback ]]]]]]] )
向对象添加一个服务器(注:addServer没有连接到服务器的动作,所以在memcache进程没有启动的时候,执行addServer成功也会返回true)

参数
- -
host 服务器域名或 IP
port 端口号,默认为 11211
persistent 是否使用常连接,默认为 TRUE
weight 权重,在多个服务器设置中占的比重
timeout 连接服务器失效的秒数,修改默认值 1 时要三思,有可能失去所有缓存方面的优势导致连接变得很慢
retry_interval 服务器连接失败时的重试频率,默认是 15 秒一次,如果设置为 -1 将禁止自动重试,当扩展中加载了 dynamically via dl() 时,无论本参数还是常连接设置参数都会失效。
                      每一个失败的服务器在失效前都有独自的生存期,选择后端请求时会被跳过而不服务于请求。一个过期的连接将成功的重新连接或者被标记为失败的连接等待下一次 重试。这种效果就是说每一个 web server 的子进程在服务于页面时的重试连接都跟他们自己的重试频率有关。

|status | 控制服务器是否被标记为 online,设置这个参数为 FALSE 并设置 retry_interval 为 -1 可以使连接失败的服务器被放到一个描述不响应请求的服务器池子中,对这个服务器的请求将失败,接受设置为失败服务器的设置,默认参数为 TRUE,代表该服务器可以被定义为 online。
|failure_callback| 失败时的回调函数,函数的两个参数为失败服务器的 hostname 和 port

返回值

成功返回 TRUE,失败返回 FALSE。
注:在测试addServer函数的时候我们主要测试了其参数retry_interval和status

范例
retry_interval参数的测试
1
2
3
$mem = new Memcache;
$is_add = $mem->addServer(‘localhost‘, 11211, true, 1, 1, 15, true); // retrt_interval=15
$is_set = $mem->set(‘key1‘, ‘中华人民共和国‘);

上面的例子中如果localhost服务器down掉或是memcache守护进程当掉,执行请求的时候连接服务器失败时算起15秒后会自动重试连 接服务器,但是在这15秒内不会去连接这个服务器,就是只要有请求,没15秒就会尝试连接服务器,但是每个服务器连接重试是独立的。比如说我一次添加了两 个服务器一个是localhost,一个是172.16.100.60,它们分别是从各自连接失败那个时间算起,只要对各自服务器有请求就会每隔15秒去 连接各自的服务器的。

retry_interval和status结合使用的情况
1
2
3
4
5
<?php
$mem = new Memcache;
$is_add = $mem->addServer(‘localhost‘, 11211, true, 1, 1, -1, false); // retrt_interval=-1, status=false
$is_set = $mem->set(‘key1‘, ‘中华人民共和国‘);
?>

在上面的retrt_interval=-1, status=false这种情况下,将连接失败的服务器放到一个不响应请求的一个池子中,因此对key分配的算法也就没有影响了,而他是立即返回错误失败还是故障转移还要看memcache.allow_failover的设置,执行set, add, replace,get等请求的时候都会失败返回false,即使memcache进程运行正常。

status参数的测试

除了与retry_interval结合使用,status单独使用的情况会对函数memcache::getServerStatu获得的结果产生影响
无论memcache进程的正常运行还是当掉,status为true的时候getServerStatus的结果都是true,反之则为false
但是在memcache进程正常运行的情况下,对set,add,replace,get等函数都没有影响。

Memcache::add

说明

bool Memcache::add ( string $key , mixed $var [, int $flag [, int $expire ]] )
添加一个要缓存的数据如果作为这个缓存的数据的键在服务器上还不存在的情况下,

参数
- -
key 缓存数据的键 其长度不能超过250个字符
var 值,整型将直接存储,其他类型将被序列化存储 ,其值最大为1M
flag 是否使用 zlib 压缩 ,当flag=MEMCACHE_COMPRESSED的时侯,数据很小的时候不会采用zlib压缩,只有数据达到一定大小才对数据进行zlib压缩。(没有具体的测试数据进行压缩的最小值是多少)
expire 过期时间,0 为永不过期,可使用 unix 时间戳格式或距离当前时间的秒数,设为秒数时不能大于 2592000(30 天)
返回值

成功返回 TRUE,失败返回 FALSE,如果这个键已经存在,其他方面memcache:;add()的行为与memcache::set相似

范例
1
2
3
4
5
6
7
<?php
$memcache_obj = memcache_connect("localhost", 11211);
/* procedural API */
memcache_add($memcache_obj, ‘var_key‘, ‘test variable‘, FALSE, 30);
/* OO API */
$memcache_obj->add(‘var_key‘, ‘test variable‘, FALSE, 30);
?>

Memcache::replace

说明

bool Memcache::replace ( string $key , mixed $var [, int $flag [, int $expire ]] )
替换一个指定 已存在key 的的缓存变量内容

参数
- -
key 缓存数据的键, 其长度不能超过250个字符
var 值,整型将直接存储,其他类型将被序列化存储,其值最大为1M
flag 是否使用 zlib 压缩 ,当flag=MEMCACHE_COMPRESSED的时侯,数据很小的时候不会采用zlib压缩,只有数据达到一定大小才对数据进行zlib压缩。(没有具体的测试数据进行压缩的最小值是多少)
expire 过期时间,0 为永不过期,可使用 unix 时间戳格式或距离当前时间的秒数,设为秒数时不能大于 2592000(30 天)
返回值

成功返回 TRUE,失败返回 FALSE。

范例
1
2
3
4
5
6
7
<?php
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
/* procedural API */
memcache_replace($memcache_obj, "test_key", "some variable", FALSE, 30);
/* OO API */
$memcache_obj->replace("test_key", "some variable", FALSE, 30);
?>

Memcache::set

说明

bool Memcache::set ( string $key , mixed $var [, int $flag [, int $expire ]] )
设置一个指定 key 的缓存变量内容

参数
- -
key 缓存数据的键, 其长度不能超过250个字符
var 值,整型将直接存储,其他类型将被序列化存储,其值最大为1M
flag 是否使用 zlib 压缩 ,当flag=MEMCACHE_COMPRESSED的时侯,数据很小的时候不会采用zlib压缩,只有数据达到一定大小才对数据进行zlib压缩。(没有具体的测试数据进行压缩的最小值是多少)
expire 过期时间,0 为永不过期,可使用 unix 时间戳格式或距离当前时间的秒数,设为秒数时不能大于 2592000(30 天)
返回值

成功返回 TRUE,失败返回 FALSE。

范例
1
2
3
4
5
6
7
8
9
10
11
12
<?php
/* procedural API */
/* connect to memcached server */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
/*
set value of item with key ‘var_key‘
using 0 as flag value, compression is not used
expire time is 30 second
*/
memcache_set($memcache_obj, ‘var_key‘, ‘some variable‘, 0, 30);
echo memcache_get($memcache_obj, ‘var_key‘);
?>
1
2
3
4
5
6
7
8
9
10
11
12
<?php
/* OO API */
$memcache_obj = new Memcache;
/* connect to memcached server */
$memcache_obj->connect(‘memcache_host‘, 11211);
/*
set value of item with key ‘var_key‘, using on-the-fly compression
expire time is 50 seconds
*/
$memcache_obj->set(‘var_key‘, ‘some really big variable‘, MEMCACHE_COMPRESSED, 50);
echo $memcache_obj->get(‘var_key‘);
?>

Memcache::get

说明

string Memcache::get ( string $key [, int &$flags ] )
array Memcache::get ( array $keys [, array &$flags ] )获取某个 key 的变量缓存值

参数
- -
key 缓存值的键
flags 如果是传址某个变量,获取缓存值被set或是add的flag结果将被存于该变量
返回值

返回缓存的指定 key 的变量内容或者是在失败或该变量的值不存在时返回 FALSE
如果传出的key的数组中的key都不存在,返回的结果是一个空数组,反之则返回key与缓存值相关联的关联数组

范例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
$var = memcache_get($memcache_obj, ‘some_key‘);
/* OO API */
$memcache_obj = new Memcache;
$memcache_obj->connect(‘memcache_host‘, 11211);
$var = $memcache_obj->get(‘some_key‘);
/*
You also can use array of keys as a parameter.
If such item wasn‘t found at the server, the result
array simply will not include such key.
*/
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
$var = memcache_get($memcache_obj, Array(‘some_key‘, ‘another_key‘));
//如果some_key,another_key不存在 $var = array();
//如果some_key,another_key存在 $var = array(‘some_key‘=>‘缓存值‘, ‘another_key‘=>‘缓存值‘);
/* OO API */
$memcache_obj = new Memcache;
$memcache_obj->connect(‘memcache_host‘, 11211);
$var = $memcache_obj->get(Array(‘some_key‘, ‘second_key‘));
?>

Memcache::delete

说明

bool Memcache::delete ( string $key [, int $timeout ] )

删除某一个变量的缓存

参数
- -
key 缓存的键 键值不能为null和‘’,当它等于前面两个值的时候php会有警告错误。
timeout 删除这项的时间,如果它等于0,这项将被立刻删除反之如果它等于30秒,那么这项被删除在30秒内
返回值

成功返回 TRUE,失败返回 FALSE。

范例
1
2
3
4
5
6
7
8
9
10
<?php
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
/* after 10 seconds item will be deleted by the server */
memcache_delete($memcache_obj, ‘key_to_delete‘, 10);
/* OO API */
$memcache_obj = new Memcache;
$memcache_obj->connect(‘memcache_host‘, 11211);
$memcache_obj->delete(‘key_to_delete‘, 10);
?>

Memcache::flush

说明

bool Memcache::flush ( void )

清空所有缓存内容,不是真的删除缓存的内容,只是使所有变量的缓存过期,使内存中的内容被重写

返回值

成功返回 TRUE,失败返回 FALSE。

范例
1
2
3
4
5
6
7
8
9
10

<?php
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
memcache_flush($memcache_obj);
/* OO API */
$memcache_obj = new Memcache;
$memcache_obj->connect(‘memcache_host‘, 11211);
$memcache_obj->flush();
?>

Memcache::getExtendedStats

说明

array Memcache::getExtendedStats ([ string $type [, int $slabid [, int $limit ]]] )

获取所有服务器扩展静态信息

参数
- -
type 静态信息类型,有效值包括{reset, malloc, maps, cachedump, slabs, items, sizes},依照一定规则协议这个可选参数是为了方便开发人员查看不同类别的信息而输入的标题
slabid 用于按指定类型联合设置 cache 堆为有效的片到堆中。缓存堆被被命令绑定到服务器上并被严格的用于调试用途
limit 用于按指定类型联合设置 cache 堆为输入的数字所限制的大小到堆,默认值为 100
返回值

返回一个由服务器扩展静态信息二维数组,失败时返回 FALSE

范例
1
2
3
4
5
6
7
<?php
$memcache_obj = new Memcache;
$memcache_obj->addServer(‘memcache_host‘, 11211);
$memcache_obj->addServer(‘failed_host‘, 11211);
$stats = $memcache_obj->getExtendedStats();
print_r($stats);
?>

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Array(
[memcache_host:11211] => Array(
[pid] => 3756
[uptime] => 603011
[time] => 1133810435
[version] => 1.1.12
[rusage_user] => 0.451931
[rusage_system] => 0.634903
[curr_items] => 2483
[total_items] => 3079
[bytes] => 2718136
[curr_connections] => 2
[total_connections] => 807
[connection_structures] => 13
[cmd_get] => 9748
[cmd_set] => 3096
[get_hits] => 5976
[get_misses] => 3772
[bytes_read] => 3448968
[bytes_written] => 2318883
[limit_maxbytes] => 33554432
),
[failed_host:11211] =>
)

Memcache::getStats

说明

array Memcache::getStats ([ string $type [, int $slabid [, int $limit ]]] )

获取最后添加服务器静态信息

参数
- -
type 静态信息类型,有效值包括{reset, malloc, maps, cachedump, slabs, items, sizes},依照一定规则协议这个可选参数是为了方便开发人员查看不同类别的信息而输入的标题
slabid 用于按指定类型联合设置 cache 堆为有效的片到堆中。缓存堆被被命令绑定到服务器上并被严格的用于调试用途
limit 用于按指定类型联合设置 cache 堆为输入的数字所限制的大小到堆,默认值为 100
返回值

返回一个服务器静态信息数组,失败时返回 FALSE

Memcache::getServerStatus

说明

int Memcache::getServerStatus ( string $host [, int $port ] )

通过输入的 host 及 port 来获取相应的服务器信息

参数
- -
host 服务器域名或 IP
port 端口号,默认为 11211
返回值

返回服务器状态,0 为失败,其他情况返回非 0 数字

范例
1
2
3
4
5
6
7
8
9
<?php
/* OO API */
$memcache = new Memcache;
$memcache->addServer(‘memcache_host‘, 11211);
echo $memcache->getServerStatus(‘memcache_host‘, 11211);
/* procedural API */
$memcache = memcache_connect(‘memcache_host‘, 11211);
echo memcache_get_server_status($memcache, ‘memcache_host‘, 11211);
?>

Memcache::getVersion

说明

string Memcache::getVersion ( void )

获取服务器的版本号信息

返回值

成功返回服务器的版本号字符串,失败返回 FALSE

范例
1
2
3
4
5
6
7
8
9
<?php
/* OO API */
$memcache = new Memcache;
$memcache->connect(‘memcache_host‘, 11211);
echo $memcache->getVersion();
/* procedural API */
$memcache = memcache_connect(‘memcache_host‘, 11211);
echo memcache_get_version($memcache);
?>

Memcache::setCompressThreshold

bool Memcache::setCompressThreshold ( int $threshold [, float $min_savings ] )设置压缩极限

参数
- -
threshold 设置控制自动压缩的变量长度的最小值
min_saving 指定的最低压缩比率,值必须介于 0 - 1 之间,默认为 0.2 代表 20% 的压缩比率
返回值

成功返回 TRUE,失败返回 FALSE。

范例
1
2
3
4
5
6
7
8
9
<?php
/* OO API */
$memcache_obj = new Memcache;
$memcache_obj->addServer(‘memcache_host‘, 11211);
$memcache_obj->setCompressThreshold(20000, 0.2);
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
memcache_set_compress_threshold($memcache_obj, 20000, 0.2);
?>

Memcache::setServerParams

说明

bool Memcache::setServerParams ( string $host [, int $port [, int $timeout [, int $retry_interval [, bool $status [, callback $failure_callback ]]]]] )
Memcache version 2.1.0 后增加的函数,运行时设置服务器参数

参数
- -
host 服务器域名或 IP
port 端口号,默认为 11211
timeout 超时连接失效的秒数,修改默认值 1 时要三思,有可能失去所有缓存方面的优势导致连接变得很慢
retry_interval 服务器连接失败时的重试频率,默认是 15 秒一次,如果设置为 -1 将禁止自动重试,当扩展中加载了 dynamically via dl() 时,无论本参数还是常连接设置参数都会失效。 每一个失败的服务器在失效前都有独自的生存期,选择后端请求时会被跳过而不服务于请求。一个过期的连接将成功的重新连接或者被标记为失败的连接等待下一次 重试。这种效果就是说每一个 web server 的子进程在服务于页面时的重试连接都跟他们自己的重试频率有关。
status 控制服务器是否被标记为 online,设置这个参数为 FALSE 并设置 retry_interval 为 -1 可以使连接失败的服务器被放到一个描述不响应请求的服务器池子中,对这个服务器的请求将失败,接受设置为失败服务器的设置,默认参数为 TRUE,代表该服务器可以被定义为 online。
failure_callback 失败时的回调函数,函数的两个参数为失败服务器的 hostname 和 port
返回值

成功返回 TRUE,失败返回 FALSE。

范例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
function _callback_memcache_failure($host, $port){
print "memcache ‘$host:$port‘ failed";
}
/* OO API */
$memcache = new Memcache;
// Add the server in offline mode
$memcache->addServer(‘memcache_host‘, 11211, FALSE, 1, 1, -1, FALSE);
// Bring the server back online
$memcache->setServerParams(‘memcache_host‘, 11211, 1, 15, TRUE, ‘_callback_memcache_failure‘);
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
memcache_set_server_params($memcache_obj, ‘memcache_host‘, 11211, 1, 15, TRUE, ‘_callback_memcache_failure‘);
?>

Memcache::increment

说明

int Memcache::increment ( string $key [, int $value ] )
给指定 key 的缓存变量一个增值,如果该变量不是数字时不会被转化为数字,这个增值将会加到该变量原有的数字之上,变量不存在不会新增变量,对于压缩存储的变量不要使用本函数因为相应的取值方法会失败。

参数
- -
key 缓存值的键
value 值,整型将直接存储,其他类型将被序列化存储
返回值

成功返回新的变量值,失败返回 FALSE。

范例
1
2
3
4
5
6
7
8
9
10
11
<?php
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
/* increment counter by 2 */
$current_value = memcache_increment($memcache_obj, ‘counter‘, 2);
/* OO API */
$memcache_obj = new Memcache;
$memcache_obj->connect(‘memcache_host‘, 11211);
/* increment counter by 3 */
$current_value = $memcache_obj->increment(‘counter‘, 3);
?>

Memcache::decrement

说明

int Memcache::decrement ( string $key [, int $value ] )
给指定 key 的缓存变量一个递减值,与 increment 操作类似,将在原有变量基础上减去这个值,该项的值将会在转化为数字后减去,新项的值不会小于 0,对于压缩存储的变量不要使用本函数因为相应的取值方法会失败。

参数
- -
key 缓存值的键
value 值,整型将直接存储,其他类型将被序列化存储
返回值

成功返回新的变量值,失败返回 FALSE。

范例
1
2
3
4
5
6
7
8
9
10
11
<?php
/* procedural API */
$memcache_obj = memcache_connect(‘memcache_host‘, 11211);
/* decrement item by 2 */
$new_value = memcache_decrement($memcache_obj, ‘test_item‘, 2);
/* OO API */
$memcache_obj = new Memcache;
$memcache_obj->connect(‘memcache_host‘, 11211);
/* decrement item by 3 */
$new_value = $memcache_obj->decrement(‘test_item‘, 3);
?>

memcache_debug

说明

bool memcache_debug ( bool $on_off )

设置 memcache 的调试器是否开启,值为 TRUE 或 FALSE。 受影响于 php 安装时是否使用了 –enable-debug 选项,如果使用了该函数才会返回 TRUE,其他情况将始终返回 FALSE。

参数
- -
on_off 设置调试模式是否开启,TRUE 为开启,FALSE 为关闭
返回值

php 安装时如果使使用了 –enable-debug 选项返回 TRUE,否则将返回 FALSE。

参考资料来源:http://www.cnblogs.com/qiantuwuliang/archive/2011/03/07/1974499.html

许多语言都实现了连接memcached的客户端,其中以Perl、PHP为主。 仅仅memcached网站上列出的语言就有

  • Perl
  • PHP
  • Python
  • Ruby
  • C#
  • C/C++
  • Lua

等等。

memcached客户端API:http://www.danga.com/memcached/apis.bml
这里介绍通过mixi正在使用的Perl库链接memcached的方法。

使用Cache::Memcached

Perl的memcached客户端有

  • Cache::Memcached
  • Cache::Memcached::Fast
  • Cache::Memcached::libmemcached

等几个CPAN模块。这里介绍的Cache::Memcached是memcached的作者Brad Fitzpatric的作品, 应该算是memcached的客户端中应用最为广泛的模块了。

Cache::Memcached – search.cpan.org: http://search.cpan.org/dist/Cache-Memcached/

使用Cache::Memcached连接memcached

下面的源代码为通过Cache::Memcached连接刚才启动的memcached的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/perl

use strict;
use warnings;
use Cache::Memcached;

my $key = "foo";
my $value = "bar";
my $expires = 3600; # 1 hour
my $memcached = Cache::Memcached->new({
servers => ["127.0.0.1:11211"],
compress_threshold => 10_000
});

$memcached->add($key, $value, $expires);
my $ret = $memcached->get($key);
print "$ret\n";

在这里,为Cache::Memcached指定了memcached服务器的IP地址和一个选项,以生成实例。 Cache::Memcached常用的选项如下所示。

选项 说明
servers 用数组指定memcached服务器和端口
compress_threshold 数据压缩时使用的值
namespace 指定添加到键的前缀

另外,Cache::Memcached通过Storable模块可以将Perl的复杂数据序列化之后再保存, 因此散列、数组、对象等都可以直接保存到memcached中。

保存数据

向memcached保存数据的方法有

  • add
  • replace
  • set

它们的使用方法都相同:

1
2
3
4
5
my $add = $memcached->add( '键', '值', '期限' );
my $replace = $memcached->replace( '键', '值', '期限' );
my $set = $memcached->set( '键', '值', '期限' );
my $val = $memcached->get('键');
my $val = $memcached->get_multi('键1', '键2', '键3', '键4', '键5');

一次取得多条数据时使用getmulti。getmulti可以非同步地同时取得多个键值, 其速度要比循环调用get快数十倍。

删除数据

删除数据使用delete方法,不过它有个独特的功能。

1
$memcached->delete('键', '阻塞时间(秒)');

删除第一个参数指定的键的数据。第二个参数指定一个时间值,可以禁止使用同样的键保存新数据。 此功能可以用于防止缓存数据的不完整。但是要注意,set函数忽视该阻塞,照常保存数据

增一和减一操作

可以将memcached上特定的键值作为计数器使用。

1
2
my $ret = $memcached->incr('键');
$memcached->add('键', 0) unless defined $ret;

增一和减一是原子操作,但未设置初始值时,不会自动赋成0。因此, 应当进行错误检查,必要时加入初始化操作。而且,服务器端也不会对 超过2 SUP(32)时的行为进行检查。

memcached 是以LiveJournal 旗下Danga Interactive 公司的Brad Fitzpatric 为首开发的一款软件。现在已成为 mixi、 hatena、 Facebook、 Vox、LiveJournal等众多服务中 提高Web应用扩展性的重要因素。

许多Web应用都将数据保存到RDBMS中,应用服务器从中读取数据并在浏览器中显示。 但随着数据量的增大、访问的集中,就会出现RDBMS的负担加重、数据库响应恶化、 网站显示延迟等重大影响。

这时就该memcached大显身手了。memcached是高性能的分布式内存缓存服务器。 一般的使用目的是,通过缓存数据库查询结果,减少数据库访问次数,以提高动态Web应用的速度、 提高可扩展性。

memcached的特征

memcached作为高速运行的分布式缓存服务器,具有以下的特点。

  1. 协议简单
  2. 基于libevent的事件处理
  3. 内置内存存储方式
  4. memcached不互相通信的分布式

协议简单

memcached的服务器客户端通信并不使用复杂的XML等格式, 而使用简单的基于文本行的协议。因此,通过telnet 也能在memcached上保存数据、取得数据。下面是例子。

1
2
3
4
5
6
7
8
9
10
$ telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
set foo 0 0 3 (保存命令)
bar (数据)
STORED (结果)
get foo (取得命令)
VALUE foo 0 3 (数据)
bar (数据)

协议文档位于memcached的源代码内,也可以参考以下的URL。

http://code.sixapart.com/svn/memcached/trunk/server/doc/protocol.txt

基于libevent的事件处理

libevent是个程序库,它将Linux的epoll、BSD类操作系统的kqueue等事件处理功能 封装成统一的接口。即使对服务器的连接数增加,也能发挥O(1)的性能。 memcached使用这个libevent库,因此能在Linux、BSD、Solaris等操作系统上发挥其高性能。 可以参考Dan Kegel的The C10K Problem。

libevent: http://www.monkey.org/~provos/libevent/
The C10K Problem: http://www.kegel.com/c10k.html

内置内存存储方式

为了提高性能,memcached中保存的数据都存储在memcached内置的内存存储空间中。 由于数据仅存在于内存中,因此重启memcached、重启操作系统会导致全部数据消失。 另外,内容容量达到指定值之后,就基于LRU(Least Recently Used)算法自动删除不使用的缓存。 memcached本身是为缓存而设计的服务器,因此并没有过多考虑数据的永久性问题。

memcached不互相通信的分布式

memcached尽管是“分布式”缓存服务器,但服务器端并没有分布式功能。 各个memcached不会互相通信以共享信息。那么,怎样进行分布式呢? 这完全取决于客户端的实现。

接下来简单介绍一下memcached的使用方法。

安装memcached

memcached的安装比较简单,这里稍加说明。

memcached支持许多平台。 * Linux * FreeBSD * Solaris (memcached 1.2.5以上版本) * Mac OS X

另外也能安装在Windows上。这里使用Fedora Core 8进行说明。

memcached的安装
运行memcached需要本文开头介绍的libevent库。Fedora 8中有现成的rpm包, 通过yum命令安装即可。

$ sudo yum install libevent libevent-devel

memcached的源代码可以从memcached网站上下载。本文执笔时的最新版本为1.2.5。 Fedora 8虽然也包含了memcached的rpm,但版本比较老。因为源代码安装并不困难, 这里就不使用rpm了。

下载memcached:http://www.danga.com/memcached/download.bml

memcached安装与一般应用程序相同,configure、make、make install就行了。

1
2
3
4
5
6
$ wget http://www.danga.com/memcached/dist/memcached-1.2.5.tar.gz
$ tar zxf memcached-1.2.5.tar.gz
$ cd memcached-1.2.5
$ ./configure
$ make
$ sudo make install

默认情况下memcached安装到/usr/local/bin下。

memcached的启动

从终端输入以下命令,启动memcached。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ /usr/local/bin/memcached -p 11211 -m 64m -vv
slab class 1: chunk size 88 perslab 11915
slab class 2: chunk size 112 perslab 9362
slab class 3: chunk size 144 perslab 7281

中间省略...

slab class 38: chunk size 391224 perslab 2
slab class 39: chunk size 489032 perslab 2
<23 server listening
<24 send buffer was 110592, now 268435456
<24 server listening (udp)
<24 server listening (udp)
<24 server listening (udp)
<24 server listening (udp)

这里显示了调试信息。这样就在前台启动了memcached,监听TCP端口11211 最大内存使用量为64M。

作为daemon后台启动时,只需

1
$ /usr/local/bin/memcached -p 11211 -m 64m -d

这里使用的memcached启动选项的内容如下。

选项 说明
-p 使用的TCP端口。默认为11211
-m 最大内存大小。默认为64M
-vv 用very vrebose模式启动,调试信息和错误输出到控制台
-d 作为daemon在后台启动

上面四个是常用的启动选项,其他还有很多,通过

1
$ /usr/local/bin/memcached -h

命令可以显示。许多选项可以改变memcached的各种行为, 推荐读一读。

0%