wfrest

底层包装了一下workflow,让他更易于使用。原来的workflow仍然可以使用

好处

简化了服务端的代码,具体简化了:

  1. 简化了server的写法

    process实际还要做路由的工作,所以导致开发过程中就要写一个很大的if ,else if,但使用wfrest直接提供了一个默认构造函数,也就是说,process不需要自己提供了,框架以及把process已经写好了

    wfrest写的process干了什么? 路由(来get请求,执行get逻辑,来post请求,执行post逻辑),会增加路由的目的地

    1
    2
    3
    4
    5
    6
    if(server.track().start(12345) == 0){
    //track每次客户端请求都显示客户端的信息
    server.list_routes();
    server.stop();

    }
  2. wfrest帮助解析数据

    192.168.72.125:12345/login?username=hu&password=123,数据再url的查询参数里面,wfrest可以帮助把参数切割为map

    1
    2
    3
    4
    5
    6
    7
    wfrest::Httpserver server;
    server.POST("/login",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp){
    map<string,string>querylist = req->query_ist();//提取所有的查询参数
    for(auto & pair:quertList){
    cout << pair.first << pair.second;
    }
    })
  3. wfrest帮我们解析urlencoded类型的报文体

    该报文体就是 k1=v1&k2=v2&k=v3…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    server.GET("/login",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp){
    resp->File("xxx.html");//部署静态资源服务器
    })
    server.POST("/login",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp){
    map<string,string> fromKV = req->form_kv();//提取所有的查询参数
    for(auto & pair:fromKV){
    cout << pair.first << pair.second;
    }
    })
  4. wfrest帮助解析form-data格式的报文体

    该报文体就是key:(child_key:child_value),一个键对应两个值

    1
    2
    3
    4
    5
    6
    server.POST("/login",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp){
    map<string,pair<string,string>> from = req->form();//提取所有的查询参数
    for(auto & pair:from){
    cout << pair.first << pair.second.first << pair.second.second;
    }
    })

    同时form-data适合传输大数据,也就是可以实现文件上传功能

在wfrest当中使用workflow的任务和序列机制

目前是wfrest写好了process ,用户实现process后面的handler,wfrest又会包装handler成为一个任务,但是目前的问题是,用户写的handler还没交给框架,还没成为一个任务,也就是无法找到自己所在的序列(因为相当于还没加入序列呢)

提供了三参数的版本

1
2
3
4
server.POST("/login",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp,SeriesWork *series){
...
}
})

增加了一个序列的参数

示例,在登录之后跟一个redis任务,来判断是否登录成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server.POST("/login",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp,SeriesWork *series){
map<string,string>querylist = req->query_ist();
string password = queryList["password"];
WFRedisTask *redisTask = WFTaskFacory::create_redis_task("redis://127.0.0.1:6379",10,[quertList](WFRedisTask * redistask){
protocl::RedisResponse * repRedis = redisTask->get_resp();
protocol::RedisValue result;
respRedis->get_result(result);
if(result.is_string()&&result.string_value() == queryList["password"]){
cout << "success"
}
else{
cout<<"fail";
}
});
redisTask->get_req->set_request("HGET",{"57user",queryList["username"]});
series->push_backredisTask);
})

不仅要对比账号密码是否正确 还要回响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server.POST("/login",[](const wfrest::HttpReq *req,wfrest::HttpResp *resp,SeriesWork *series){
map<string,string>querylist = req->query_ist();
string password = queryList["password"];
WFRedisTask *redisTask = WFTaskFacory::create_redis_task("redis://127.0.0.1:6379",10,[resp,quertList](WFRedisTask * redistask){//捕获列表增加一resp用于回复响应
protocl::RedisResponse * repRedis = redisTask>get_resp();
protocol::RedisValue result;
respRedis-get_result(result);
if(result.is_string()&&result.string_value() == queryList["password"]){
resp->String("success");//resp是wfrest继承过的,用法更简单
}
else{
resp->String("fail");
}
});
redisTask->get_req->set_request("HGET",{"57user",queryList["username"]});
series->push_backredisTask);
})

使用回调嵌回调的方法(但要避免回调地狱:回调函数中嵌套回调函数的情况就叫做回调地狱。总结一下,回调地狱就是为是实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护。)

使用Linux抓包

首先进入root用户

然后使用指令

1
2
tcpdump -i port 1234 -w /home/hy/xxx.cap
# -i表示选择网口 -w 表示保存

工作碰到的严重问题有如何解决的?

碰到一个严重的网络问题,给了框架,但没有文档,所以我首先找文档,没找到的情况下,我选择先启动程序,然后执行netstate -an 命令列出所有的网络,(netstate -nt列出tcp的)发现端口号被占用了,然后使用tcpdump抓包,然后我把抓包结果进行保存,然后使用wireshark进行分析

如果是用md5出现碰撞,换sha1,如果仍然有,可以调整表的结构,例如添加文件的前十个字节,设置文件哈希值和文件的前十个字节为联合唯一键,

阿里云oss

Bucket列表

先创建一个bucket,自定义创建,bucket相当于购买了一个网盘

登陆凭证

点击accessKey管理,点击创建accessKey,完成后会生成id 和secret

oss使用

点击bucket列表,选择刚才创建的那个bucket,会发现跟网盘很像,把要上传的文件拖进来,再点击上传,就可以把文件上传上去,也可以下载。

使用代码实现oss

选择文档和工具找到api和SDK。

api是接口—>RESTful规范的http接口集合

​ |

​ |官方直接使用编程语言封装一下

​ √

形成SDK

安装SDK

解压

1
2
tar xf ...
cd ...

安装

1
2
3
4
5
6
7
makedir build
cd build
cmake ..
make
#可能会缺库 需要在下载库
sudo apt install libcur14-gnutls-dev#不缺库不需要执行这一
sudo make install

测试安装是否成功

1
g++ test.cpp -std=c++11 -fno-rtti -lalibabacloud-oss-cpp-sdk -lcurl -lcrypto -lpthread -o test.bin

代码使用

上传文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

struct OSSInfo{

}
int main(void){
OSSInfo ossinfo;
//初始化网络等资源
InitializeSdk();
//创建一个配置对象
ClientConfiguation conf;//默认构造表示客户端选择默认参数
OssClient client(ossinfo.EndPoint,ossino.AcesseyId,ossinfo.AcessKeySecret,conf);

//上传文件
PutObjectOutcome out come = client.PutObject(ossinfo.Bucket,"dir1/2.txt","2.txt");//第一个参数是bucket的名字,第二个参数是文件在oss中的路径,第三个参数是文件在本地的路径
if(!outcome.isSuccess()){
//上传失败
cerr << "Put Object fail , code = "<<outcome.eror().Code() << "messge = " << outcome.error().Message() << "\n";
}
//释放网络资源
Shutdowndk();
return 0;
}

下载文件

只需要生成下载链接即可

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
#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

struct OSSInfo{

}
int main(void){
OSSInfo ossinfo;
//初始化网络等资源
InitializeSdk();
//创建一个配置对象
ClientConfiguation conf;//默认构造表示客户端选择默认参数
OssClient client(ossinfo.EndPoint,ossino.AcesseyId,ossinfo.AcessKeySecret,conf);

//生成下载链接
time_t expires = time(nullptr) + 600;//设置过期的时间,用于超时验证
strinOutcome outcome = client.GeeratePresignedUrl(ossinfo.Bucket,"dir1/2.txt",expires);
if(outcome.isSucess()){
cout <<" success url= "<<outcome.result();
}else{
out << "fail messgae" << outcome.error();
}

//释放网络资源
Shutdowndk();
return 0;
}

对比直接使用oss的好处1.便宜。2.可以实现复杂的功能例如搜索,3.存在本地可以方便的进行数据挖掘,人工智能等算法