#getcwd函数

函数原型

1
char *getcwd( char *buffer, int maxlen );

头文件

1
include <unistd.h>

功能

获取当前工作目录

参数说明

getcwd()会将当前工作目录的绝对路径复制到参数buffer所指的内存空间中,参数maxlen为buffer的空间大小。

返回值

成功则返回当前工作目录,失败返回 FALSE。

使用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_LEN 1024
int main(int argc,char* argv[]){
char buff[MAX_LEN];
char *cwd = getcwd(buff,MAX_LEN);
if(NULL == cwd){
perror("get current working director fail\n");
exit(-1);
}
printf("%s\n",buff);
return 0;
}

perror函数

头文件

1
include<stdlib.h>

函数原型

1
void perror(const char *str)

函数说明

perror()用来将上一个函数发生错误的原因输出到标准设备(stderr)。参数所指的字符串会先打印出,后面再加上错误原因字符串。此错误原因依照全局变量error 的值来决定要输出的字符串。

在库函数中有个error变量,每个error值对应着以字符串表示的错误类型。当你调用”某些”函数出错时,该函数已经重新设置了error的值。perror函数只是将你输入的一些信息和现在的error所对应的错误一起输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
int main ()
{
FILE *fp;
rename("file.txt", "newfile.txt");
fp = fopen("file.txt", "r");
if( fp == NULL )
{
perror("Error: ");
return -1;
}
fclose(fp);
return 0;
}

malloc函数

函数原型

1
void* malloc (size_t size);

头文件

1
#include <stdlib.h>

##作用:
函数用来动态地分配内存空间

##参数说明
size 为需要分配的内存空间的大小

##函数说明
malloc() 在堆区分配一块指定大小的内存空间,用来存放数据。这块内存空间在函数执行完成后不会被初始化,它们的值是未知的。如果希望在分配内存的同时进行初始化,请使用 calloc() 函数。

##注意
函数的返回值类型是 void *,void 并不是说没有返回值或者返回空指针,而是返回的指针类型未知。所以在使用 malloc() 时通常需要进行强制类型转换,将 void 指针转换成我们希望的类型,
例如:

1
int *ptr = (int *)malloc(10*sizeof(int)); // 分配10个int的的内存空间,用来存放int数组

申请了内存空间后,必须检查是否分配成功。
当不需要再使用申请的内存时,记得释放.释放后应该把指向这块内存的指针指向NULL,防止程序后面不小心使用了它.这个函数和free应该配对使用。如果申请后不释放就是内存泄露;如果无故释放那就是什么也没有做。释放只能一次,如果释放两次及两次以上会
出现错误(释放空指针例外,释放空指针其实也等于啥也没做,所以释放空指针释放多少次都没有问题)。

百度 模拟登陆

最近闲着无聊 没事写点东西玩
其实这种模拟登陆就是体力活,多次寻找,对比发送请求,找出’变量’和’常量’,然后代码中对’变量’进行填充,就可以了。难解决的问题是验证码,ip等,这里不做扩展。
百度在首次访问时会随机生成一个uid,这个uid的算法是js生成的,具体可以在js文件看到,不过js文件是通过多层js引用加载出来的
登陆时会使用到rsa算法,也是使用js做的加密,pubkey是通过异步动态获取的,逻辑不少,多抓几次包差不多就了解个大概了,细心点就可以了
直接贴代码吧

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
/**
* Created by PhpStorm.
* User: eric
* Date: 2017/4/22
* Time: 下午2:17
*/
use \GuzzleHttp\Client;
require "vendor/autoload.php";
class HttpClient extends Client{
private static $instance = null;
public static function getInstance(){
if(self::$instance ==null){
self::$instance = new HttpClient(['cookies' => true]);
}
return self::$instance;
}
}
function buildGid(){
$ss = 'xxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
$len = strlen($ss);
$result = '';
for ($i = 0; $i < $len; $i++) {
$result .= rep_sp($ss[$i]);
}
return $result;
}
function rep_sp($ns){
$t = rand(0, 16);
preg_match('/[xy]/',$ns,$result);
if(empty($result) ){
return $ns;
}
$n = $result ? $t:(3 & $t|8);
return strtoupper(dechex($n) );
}
function getToken($gid,$tt){
$callback = 'bd__cbs__3vybvq';
$api = "https://passport.baidu.com/v2/api/?getapi&tpl=pp&apiver=v3&tt=$tt&class=login&gid=$gid&logintype=basicLogin&callback=$callback";
$result = HttpClient::getInstance()->request('GET',$api);
$t = substr($result->getBody(),strlen($callback)+1,-1);
$t = str_replace("'","\"",$t);
$t = json_decode($t,true);
$token = $t['data']['token'];
return $token;
}
function getRsaKey($token,$gid,$tt){
$callback = 'bd__cbs__51lp3z';
$api = "https://passport.baidu.com/v2/getpublickey?token=$token&tpl=pp&apiver=v3&tt=$tt&gid=$gid&callback=$callback";
$result = HttpClient::getInstance()->request('GET',$api);
$t = substr($result->getBody(),strlen($callback)+1,-1);
$t = str_replace("'","\"",$t);
$t = json_decode($t,true);
return array($t['key'],$t['pubkey']);
}
function rsaSign($str,$pubKey) {
openssl_public_encrypt($str,$sign,$pubKey);
return base64_encode($sign);
}
function getMicroTime(){
return intval(microtime(true)*1000);
}
$passort_api = "https://passport.baidu.com/v2/api/?login";
$password = 'xxxx';
$username = 'xxxx';
HttpClient::getInstance()->request('get','https://passport.baidu.com/v2/?login');
$random_time = getMicroTime();
$gid = buildGid();
$token = getToken($gid,$random_time);
$random_time = $random_time+rand(1,1000);
list($key,$pubKey) = getRsaKey($token,$gid,$random_time);
$sign = rsaSign($password,$pubKey);
$postData = array(
'staticpage'=>'https://passport.baidu.com/static/passpc-account/html/v3Jump.html',
'charset'=>'UTF-8',
'token'=>$token,
'tpl'=>'pp',
'subpro'=>'',
'apiver'=>'v3',
'tt'=>time()*1000+123,
'codestring'=>'',
'safeflg'=>"0",
'u'=>"https://passport.baidu.com/",
'isPhone'=>0,
'detect'=>"1",
'gid'=>$gid,
'quick_user'=>0,
'logintype'=>'basicLogin',
'logLoginType'=>'pc_loginBasic',
'idc'=>'',
'loginmerge'=>"true",
'username'=>$username,
'password'=>$sign,
'crypttype'=>12,
'ppui_logintime'=>rand(1000,10000),
'countrycode'=>'',
'rsakey'=>$key,
'mem_pass'=>'on',
'callback'=>'parent.bd__pcbs__l3z9y3'
);
$headers = array(
'Accept'=>'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'User-Agent'=> 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0',
'Accept-Language'=>'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3',
'Accept-Encoding'=>' deflate, br',
'Referer'=> 'https://passport.baidu.com/v2/?login',
'Content-Type'=>'application/x-www-form-urlencoded',
'Host'=> 'passport.baidu.com',
'Connection'=>'keep-alive',
'Upgrade-Insecure-Requests'=>1,
);
$aaa = HttpClient::getInstance()->request("POST",$passort_api,['form_params'=>$postData,'headers'=>$headers]);
echo "<pre>";print_r($aaa->headers());exit;

获取到BDUSS cookie即表明登陆成功

rsa加密

rsa算法相关
RSA算法原理(一)
RSA算法原理(二)

最近研究百度登陆,发现百度登陆使用到了rsa加密,通过异步获取pubkey后,对password进行了rsa加密
具体php实现如下

1
2
3
4
5
function rsaSign($password,$pubKey) {
openssl_public_encrypt($password,$sign,$pubKey);
return base64_encode($sign);
}

Spl中提供了两个接口来实现观察者模式
Subject需要实现3个具体方法,包括添加观察者,删除观察者,通知观察者
Observer需要实现一个接口,更新

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
class ConcertSubject implements SplSubject{
private $obeservers;
private $value;
public function __construct()
{
$this->obeservers = new SplObjectStorage();
}
public function attach (SplObserver $observer){
$this->obeservers->attach($observer);
}
public function detach (SplObserver $observer){
$this->obeservers->detach($observer);
}
public function notify (){
foreach ($this->obeservers as $obeserver){
$obeserver->update($this);
}
}
public function setValue($value){
$this->value = $value;
$this->notify();
}
public function getValue(){
return $this->value;
}
}
class ConcertObserver implements SplObserver{
public function update (SplSubject $subject){
var_dump($subject->getValue());
}
}
$subject = new ConcertSubject();
$observer = new ConcertObserver();
$subject->attach($observer);
$subject->setValue(666);

很多人喜欢用正则提取url中的domain,path及参数信息
实际上php已经提供了对url解析的方法:parse_url
一个url可以分解成如下几个部分

  • scheme 协议
  • host 主机
  • port 端口
  • user 用户名
  • pass 密码
  • path 路径
  • query 参数
  • fragment

直接调用parse_url
可以将url解析成一个数组,可以通过索引名称获取到对应的值

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
$url = 'http://username:password@hostname:9090/path?arg=value#anchor';
var_dump(parse_url($url));
var_dump(parse_url($url, PHP_URL_SCHEME));
var_dump(parse_url($url, PHP_URL_USER));
var_dump(parse_url($url, PHP_URL_PASS));
var_dump(parse_url($url, PHP_URL_HOST));
var_dump(parse_url($url, PHP_URL_PORT));
var_dump(parse_url($url, PHP_URL_PATH));
var_dump(parse_url($url, PHP_URL_QUERY));
var_dump(parse_url($url, PHP_URL_FRAGMENT));
?>

STOMP 概述

STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP客户端与任意STOMP消息代理(Broker)进行交互。STOMP协议由于设计简单,易于开发客户端,因此在多种语言和多种平台上得到广泛地应用。

STOMP协议的前身是TTMP协议(一个简单的基于文本的协议),专为消息中间件设计。

STOMP是一个非常简单和容易实现的协议,其设计灵感源自于HTTP的简单性。尽管STOMP协议在服务器端的实现可能有一定的难度,但客户端的实现却很容易。例如,可以使用Telnet登录到任何的STOMP代理,并与STOMP代理进行交互。

STOMP的实现

业界已经有很多优秀的STOMP的服务器/客户端的开源实现,下面就介绍一下这方面的情况。

STOMP服务器




















项目名 兼容STOMP的版本 描述
Apache Apollo 1.0 1.1 1.2 ActiveMQ的继承者
Apache ActiveMQ 1.0 1.1 流行的开源消息服务器
RabbitMQ 1.0 1.1 1.2 基于Erlang、支持多种协议的消息Broker,通过STOMP插件支持STOMP协议

STOMP客户端库

















项目名 兼容STOMP的版本 描述
Gozirra 1.0 Java客户端库]
stompest 1.0 1.1 1.2 Python客户端库,全功能实现,包括同步和异步

STOMP协议分析

STOMP协议与HTTP协议很相似,它基于TCP协议,使用了以下命令:

CONNECT
SEND
SUBSCRIBE
UNSUBSCRIBE
BEGIN
COMMIT
ABORT
ACK
NACK
DISCONNECT

STOMP的客户端和服务器之间的通信是通过“帧”(Frame)实现的,每个帧由多“行”(Line)组成。
第一行包含了命令,然后紧跟键值对形式的Header内容。
第二行必须是空行。
第三行开始就是Body内容,末尾都以空字符结尾。
STOMP的客户端和服务器之间的通信是通过MESSAGE帧、RECEIPT帧或ERROR帧实现的,它们的格式相似。

使用

ActiveMQ 安装

官网下载最新版本,解压
配置conf目录下的activemq.xml 打开stomp端口
具体可以看这里

1
2
./bin/activemq start

PHP STOMP

安装STOMP扩展

php连接

贴个官网的代码

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
<?php
$queue = '/queue/foo';
$msg = 'bar';
/* connection */
try {
$stomp = new Stomp('tcp://localhost:61613');
} catch(StompException $e) {
die('Connection failed: ' . $e->getMessage());
}
/* send a message to the queue 'foo' */
$stomp->send($queue, $msg);
/* subscribe to messages from the queue 'foo' */
$stomp->subscribe($queue);
/* read a frame */
$frame = $stomp->readFrame();
if ($frame->body === $msg) {
var_dump($frame);
/* acknowledge that the frame was received */
$stomp->ack($frame);
}
/* close connection */
unset($stomp);
?>

使用telnet

这类基于tcp的简单协议 都可以使用telnet模拟,甚至是查找问题
CONNECT

1
2
3
4
5
6
7
8
9
10
11
12
13
➜ ~ telnet 10.207.26.112 61613
Trying 10.207.26.112...
Connected to ericwang.com.
Escape character is '^]'.
CONNECT
^@
CONNECTED
server:ActiveMQ/5.14.4
heart-beat:0,0
session:ID:ericwang-37951-1491805812122-3:11
version:1.0

SEND

1
2
3
4
5
6
7
8
SEND
destination:testSTOMP
receipt:123
hello
^@
RECEIPT
receipt-id:123

DISCONNECT

1
2
3
4
5
6
DISCONNECT
receipt:123
^@
RECEIPT
receipt-id:123

其余的可以参考这里

几种方式

.ini

parse_ini_file() 载入一个由 filename 指定的 ini 文件,并将其中的设置作为一个联合数组返回。

ini 文件的结构和 php.ini 的相似。
一下是一个名为 db.ini的配置文件

1
2
3
4
5
[db]
host=0.0.0.0
port=1234
username=username
password=pass

那么使用parse_ini_file读取也很简单

1
2
$config = parse_ini_file('db.ini',true);
print_r($config);exit;

会返回一个key为db的二维数组

.env

利用php的环境变量 $_ENV

1
2
3
4
5
6
7
通过环境方式传递给当前脚本的变量的数组。
这些变量被从 PHP 解析器的运行环境导入到 PHP 的全局命名空间。很多是由支持 PHP 运行的 Shell 提供的,并且不同的系统很可能运行着不同种类的 Shell,所以不可能有一份确定的列表。请查看你的 Shell 文档来获取定义的环境变量列表。
其他环境变量包含了 CGI 变量,而不管 PHP 是以服务器模块还是 CGI 处理器的方式运行。
$HTTP_ENV_VARS 包含相同的信息,但它不是一个超全局变量。 (注意 $HTTP_ENV_VARS 和 $_ENV 是不同的变量,PHP 处理它们的方式不同)

推荐使用php的phpdotenv
使用起来非常简单,laravel里就是使用dotenv作为配置的

yaml

这个没怎么用过,不太喜欢靠缩进风格的代码。可读性太差
这里有很详尽的说明PHP下处理YAML以及
symfony/yaml

xml

php中很少见到有使用xml的,不过spring基本上是以这种方式加载大多数配置的(后续版本中也有大量使用annotition来声明配置),一个主要原因是可读性很强,另一个原因也是规范化

array & json

很多公司自己实现的框架中都是依靠数组去实现配置的。这就导致配置文件的解析耗费了很大的性能

yaconf

最后推荐鸟哥的yaconf,由于php的生命周期,导致每次读取配置时都需要解析配置文件,而yaconf是常驻内存的,而且支持动态更新等等,优势非常明显,缺点也很突出,仅支持php7,但是php7的项目推荐使用yaconf

rabbitmq cluster

queue 的信息只在一台node上存储,其他节点不会存储,所有节点只知道queue的信息在这个node上,当这个node 挂掉之后,message将进入黑洞。
单节点的rabbitmq是disk模式的,集群模式下,可以设置至少一个disk节点,其他节点设置为RAM模式,这样可以提高路由的性能,
如果只有一个disk节点并且disk节点挂掉,创建queue,exchange,bings,添加user,修改permission,添加删除节点将会失败,直到disk节点恢复

rabbitmq reply_to

rabbitmq 屏蔽了生产者和消费者,生产者不知道消费者是谁,同样消费者也不知道生产者是谁
但是如果生产者如果需要一个回复该如何解决?
在header中设置reply_to

生产者声明临时匿名队列为exclusive,同时设置reply_to为接受队列名称。当然有时候计算比较大,消费者还需要确定生产者是否还在监听,direct-reply-to中给出了一些建议

rabbitmq 三种分布式方案

规约:
exchange 交换机
queue 队列
broker 中间服务器/服务器
cluster 集群
node 节点
route 路由

RabbitMQ可以通过三种方法部署分布式系统:集群、联盟(federation)和shovel。

集群通过连接多个机器组成单个逻辑中间服务器。机器之间通信要借助于Erlang的消息传输,要求集群中所有节点必须有相同的Erlang cookie;节点之间的网络必须是可靠的,且运行相同版本的RabbitMQ和Erlang。
虚拟主机、交换机、用户信息和权限会自动镜像到集群中各个节点。队列可能位于单个节点或镜像到多个节点。连接到任意节点的客户端能够看到集群中所有队列,即使该队列不位于连接节点上。
通常可以使用集群来提高可靠性和吞吐量,前提是在分布同一个区域内的机器,不支持网络分段。

联盟允许单台服务器上的交换机或队列接收发布到另一台服务器上交换机或队列的消息,可以是单独机器或集群。服务器之间通过AMQP协议通信,因此两个联盟交换机或联盟队列要求设置相应的用户权限。
联盟交换机之间由单向点对点链接关联,默认消息只会由联盟链接转发一次,但允许有更复杂的路由拓扑来提高转发次数。消息也可以不进行转发;如果消息到达联盟交换机之后不会路由到队列,那么它再也不会被转发。
联盟队列类似于单向点对点连接,消息会在联盟队列之间转发任意次,直到被消费者接受。
通常使用联盟来连接internet上的中间服务器,用作订阅分发消息或工作队列。

shovel连接方式与联盟的连接方式类似,但它工作在更低层次。shovel接受队列上的消息,转发到另一台服务器上的交换机。
shovel和联盟类似,但它比联盟提供更多控制。

联盟/shovel vs 集群:
1)前者中间服务器逻辑分离,后者组成一个逻辑中间服务器;
2)前者可以运行不同版本RabbitMQ和Erlang,后者要求RabbitMQ和Erlang的版本保持一致;
3)前者可以分布在WAN上,采用AMQP协议通信,要求设置权限,后者必须分布在LAN上,结合Erlang内部节点通信,要求有相同的Erlang cookie;
4)前者的拓扑结构可自行设计,链接可以单向或双向,后者要求节点之间必须保持双向链接;
5)前者遵循CAP理论的可用性和分区容错性,后者遵循CAP理论的一致性和可用性(可选择一致性和分布容错性);
6)前者服务器中的交换机可以选择联盟或本地,后者孤注一掷;
7)前者客户端只能看到所连接服务器上的队列,后者客户端可以看到所有节点的队列。

Federation / Shovel 属于AP 系统(高可用性) cluster属于CP系统(强一致性)

参考:https://www.rabbitmq.com/distributed.html

rabbitmq 一些参数说明

为队列设置唯一消费者,设置exclusive参数,如果想设置临时队列,可以加入auto_delete参数
声明队列时,如果声明参数相同,rabbit不会做任何动作,如果参数不同,则会报错
生产者投递消息到exchange中后,如果没有被路由将会被抛弃(black holed)
消费者无法在订阅一个queue的时在同一个channel中声明其他queue
exchange type :fanout,direct,topic,header(性能问题,不常用)
重启后queue和exchange都会消失,原因是durable默认时false
message需要persitant参数,queue和exchange durable参数设置为true才能保证重启后不丢失
可以通过basic.ack做好事务,auto_ack会在消息到达后自动删除,所以消费者无法做事务

其他

amqp connection 是建立在tcp之上的,由于tcp建立握手需要耗费过多的资源,所以引入了channel的概念,一个amqp connection可以建立无数个channels。
从queue中消费有两种方式,一种是basic.consume 一种是basic.get,consume会一直消费消息直到停止订阅,而get一次只消费一个消息,直到再次调用get才会消费下一个消息,
不要再循环中使用get,因为它每次都是订阅队列->消费一个消息->取消订阅
一个队列有多个消费者时,采用的是round robin方式分配消息