什么是 异步 请求
发起请求和 不需要等待 请求返回结果,直接去发起其他请求。
以DNS 请求为例 进行解析
向 DNS 服务器发起请求,解析并打印返回结果。
DNS 接口说明:
struct dns_header header 结构体并通过函数 dns_create_header(&header) 获得 header
struct dns_question question 结构体并通过函数 dns_create_question(&question,domain) 获得 question
然后传参 dns_build_request(&headr,&question,request) 获得 request
通过 snedto 发送 request 并获得 response
recvfrom(sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&addr, (socklen_t*)&addr_len);
dns_parse_response(buffer,&domains); //解析结果
例子: 同步DNS请求
char *domain[] = {
// "www.ntytcp.com",
// "bojing.wang",
"www.baidu.com",
"tieba.baidu.com",
"news.baidu.com",
"zhidao.baidu.com",
"music.baidu.com",
"image.baidu.com",
"v.baidu.com",
"map.baidu.com",
"baijiahao.baidu.com",
"xueshu.baidu.com",
"cloud.baidu.com",
"www.163.com",
"open.163.com",
"auto.163.com",
"gov.163.com",
"money.163.com",
"sports.163.com",
"tech.163.com",
"edu.163.com",
"www.taobao.com",
"q.taobao.com",
"sf.taobao.com",
"yun.taobao.com",
"baoxian.taobao.com",
"www.tmall.com",
"suning.tmall.com",
"www.tencent.com",
"www.qq.com",
"www.aliyun.com",
"www.ctrip.com",
"hotels.ctrip.com",
"hotels.ctrip.com",
"vacations.ctrip.com",
"flights.ctrip.com",
"trains.ctrip.com",
"bus.ctrip.com",
"car.ctrip.com",
"piao.ctrip.com",
"tuan.ctrip.com",
"you.ctrip.com",
"g.ctrip.com",
"lipin.ctrip.com",
"ct.ctrip.com"
};
void dns_client_commit(const char *domain )
{
// 创建 socket
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0){
perror("create socket failed\n");
exit(-1);
}
printf("url:%s\n",domain);
struct sockaddr_in dest;
bzero(&dest,sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(53);
dest.sin_addr.s_addr = inet_addr(DNS_SVR);
// 连接 服务器
int ret = connect(sockfd, (struct sockaddr *)&dest,sizeof(dest));
printf("connect :%d\n",ret);
// 准备 dns 相关数据 -- 头
struct dns_header header = {0};
dns_create_header(&header);
// 准备 dns 相关数据 -- question
struct dns_question question = {0};
dns_create_question(&question,domain);
char request[1024] = {0};
// 把 Header 与 question build 成 request
int req_len = dns_build_request(&header,&question,request);
// 发送
int slen = (int)sendto(sockfd, request, req_len, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr));
char buffer[1024] = {0};
struct sockaddr_in addr;
size_t addr_len = sizeof(struct sockaddr_in);
// 需要等待----- 接收
// 打印时间
int n = (int)recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
// 打印时间
printf("recvfrom n : %d\n", n);
struct dns_item *domains = NULL;
// 解析接收
dns_parse_response(buffer, &domains);
}
qifei@QF:~/0Voice/aysnc_dns$ ./sync_dns_client
url:www.baidu.com
connect :0
---usedtime=[14] milliseconds.
recvfrom n : 63
www.baidu.com has address 36.152.44.96
Time to live: 0 minutes , 0 seconds
www.baidu.com has address 36.152.44.95
Time to live: 0 minutes , 0 seconds
url:tieba.baidu.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 49
tieba.baidu.com has address 183.232.231.118
Time to live: 0 minutes , 0 seconds
url:news.baidu.com
connect :0
---usedtime=[15] milliseconds.
recvfrom n : 80
news.baidu.com has address 39.156.68.124
Time to live: 0 minutes , 0 seconds
news.baidu.com has address 112.34.111.125
Time to live: 0 minutes , 0 seconds
news.baidu.com has address 183.232.232.207
Time to live: 0 minutes , 0 seconds
url:zhidao.baidu.com
connect :0
---usedtime=[7] milliseconds.
recvfrom n : 50
zhidao.baidu.com has address 112.34.111.123
Time to live: 0 minutes , 0 seconds
url:music.baidu.com
connect :0
---usedtime=[7] milliseconds.
recvfrom n : 49
music.baidu.com has address 120.53.205.35
Time to live: 0 minutes , 0 seconds
url:image.baidu.com
connect :0
---usedtime=[15] milliseconds.
recvfrom n : 65
image.baidu.com has address 112.34.112.11
Time to live: 0 minutes , 0 seconds
image.baidu.com has address 112.34.113.35
Time to live: 0 minutes , 0 seconds
url:v.baidu.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 77
v.baidu.com has address 180.76.54.189
Time to live: 0 minutes , 0 seconds
v.baidu.com has address 180.76.139.133
Time to live: 0 minutes , 0 seconds
v.baidu.com has address 180.76.236.81
Time to live: 0 minutes , 0 seconds
url:map.baidu.com
connect :0
---usedtime=[7] milliseconds.
recvfrom n : 63
map.baidu.com has address 223.109.81.129
Time to live: 0 minutes , 0 seconds
map.baidu.com has address 223.109.81.130
Time to live: 0 minutes , 0 seconds
url:baijiahao.baidu.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 53
baijiahao.baidu.com has address 223.109.81.200
Time to live: 0 minutes , 0 seconds
url:xueshu.baidu.com
connect :0
---usedtime=[8] milliseconds.
recvfrom n : 66
xueshu.baidu.com has address 36.152.44.96
Time to live: 0 minutes , 0 seconds
xueshu.baidu.com has address 36.152.44.95
Time to live: 0 minutes , 0 seconds
url:cloud.baidu.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 81
cloud.baidu.com has address 112.34.111.166
Time to live: 0 minutes , 0 seconds
cloud.baidu.com has address 183.232.231.71
Time to live: 0 minutes , 0 seconds
cloud.baidu.com has address 112.34.111.165
Time to live: 0 minutes , 0 seconds
url:www.163.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 77
www.163.com has address 36.150.145.247
Time to live: 0 minutes , 0 seconds
www.163.com has address 36.150.145.244
Time to live: 0 minutes , 0 seconds
www.163.com has address 36.150.145.245
Time to live: 0 minutes , 0 seconds
url:open.163.com
connect :0
---usedtime=[7] milliseconds.
recvfrom n : 46
open.163.com has address 112.13.119.33
Time to live: 0 minutes , 0 seconds
url:auto.163.com
connect :0
---usedtime=[15] milliseconds.
recvfrom n : 78
auto.163.com has address 36.156.5.248
Time to live: 0 minutes , 0 seconds
auto.163.com has address 36.156.5.242
Time to live: 0 minutes , 0 seconds
auto.163.com has address 36.156.5.240
Time to live: 0 minutes , 0 seconds
url:gov.163.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 77
gov.163.com has address 36.156.5.243
Time to live: 0 minutes , 0 seconds
gov.163.com has address 36.156.5.249
Time to live: 0 minutes , 0 seconds
gov.163.com has address 36.156.5.244
Time to live: 0 minutes , 0 seconds
url:money.163.com
connect :0
---usedtime=[14] milliseconds.
recvfrom n : 79
money.163.com has address 36.156.5.240
Time to live: 0 minutes , 0 seconds
money.163.com has address 36.156.5.241
Time to live: 0 minutes , 0 seconds
money.163.com has address 36.156.5.249
Time to live: 0 minutes , 0 seconds
url:sports.163.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 80
sports.163.com has address 36.156.5.249
Time to live: 0 minutes , 0 seconds
sports.163.com has address 36.156.5.240
Time to live: 0 minutes , 0 seconds
sports.163.com has address 36.156.5.244
Time to live: 0 minutes , 0 seconds
url:tech.163.com
connect :0
---usedtime=[7] milliseconds.
recvfrom n : 78
tech.163.com has address 36.156.5.243
Time to live: 0 minutes , 0 seconds
tech.163.com has address 36.156.5.249
Time to live: 0 minutes , 0 seconds
tech.163.com has address 36.156.5.250
Time to live: 0 minutes , 0 seconds
url:edu.163.com
connect :0
---usedtime=[14] milliseconds.
recvfrom n : 77
edu.163.com has address 36.156.5.242
Time to live: 0 minutes , 0 seconds
edu.163.com has address 36.156.5.241
Time to live: 0 minutes , 0 seconds
edu.163.com has address 36.156.5.248
Time to live: 0 minutes , 0 seconds
url:www.taobao.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 64
www.taobao.com has address 36.156.208.231
Time to live: 0 minutes , 0 seconds
www.taobao.com has address 36.156.208.230
Time to live: 0 minutes , 0 seconds
url:q.taobao.com
connect :0
---usedtime=[14] milliseconds.
recvfrom n : 62
q.taobao.com has address 36.156.208.231
Time to live: 0 minutes , 0 seconds
q.taobao.com has address 36.156.208.230
Time to live: 0 minutes , 0 seconds
url:sf.taobao.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 47
sf.taobao.com has address 203.119.144.26
Time to live: 0 minutes , 0 seconds
url:yun.taobao.com
connect :0
---usedtime=[7] milliseconds.
recvfrom n : 48
yun.taobao.com has address 59.82.9.90
Time to live: 0 minutes , 0 seconds
url:baoxian.taobao.com
connect :0
---usedtime=[14] milliseconds.
recvfrom n : 52
baoxian.taobao.com has address 59.82.31.182
Time to live: 0 minutes , 0 seconds
url:www.tmall.com
connect :0
---usedtime=[15] milliseconds.
recvfrom n : 63
www.tmall.com has address 36.156.208.230
Time to live: 0 minutes , 0 seconds
www.tmall.com has address 36.156.208.231
Time to live: 0 minutes , 0 seconds
url:suning.tmall.com
connect :0
---usedtime=[9] milliseconds.
recvfrom n : 50
suning.tmall.com has address 203.119.169.156
Time to live: 0 minutes , 0 seconds
url:www.tencent.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 81
www.tencent.com has address 117.147.189.28
Time to live: 0 minutes , 0 seconds
www.tencent.com has address 111.48.99.22
Time to live: 0 minutes , 0 seconds
www.tencent.com has address 117.169.107.42
Time to live: 0 minutes , 0 seconds
url:www.qq.com
connect :0
---usedtime=[15] milliseconds.
recvfrom n : 60
www.qq.com has address 183.194.238.19
Time to live: 0 minutes , 0 seconds
www.qq.com has address 183.194.238.117
Time to live: 0 minutes , 0 seconds
url:www.aliyun.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 48
www.aliyun.com has address 106.11.172.51
Time to live: 0 minutes , 0 seconds
url:www.ctrip.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 63
www.ctrip.com has address 212.64.62.186
Time to live: 0 minutes , 0 seconds
www.ctrip.com has address 212.64.62.187
Time to live: 0 minutes , 0 seconds
url:hotels.ctrip.com
connect :0
---usedtime=[7] milliseconds.
recvfrom n : 50
hotels.ctrip.com has address 117.131.27.0
Time to live: 0 minutes , 0 seconds
url:hotels.ctrip.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 50
hotels.ctrip.com has address 117.131.27.0
Time to live: 0 minutes , 0 seconds
url:vacations.ctrip.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 53
vacations.ctrip.com has address 117.186.233.61
Time to live: 0 minutes , 0 seconds
url:flights.ctrip.com
connect :0
---usedtime=[15] milliseconds.
recvfrom n : 51
flights.ctrip.com has address 117.186.233.61
Time to live: 0 minutes , 0 seconds
url:trains.ctrip.com
connect :0
---usedtime=[14] milliseconds.
recvfrom n : 50
trains.ctrip.com has address 117.131.27.7
Time to live: 0 minutes , 0 seconds
url:bus.ctrip.com
connect :0
---usedtime=[14] milliseconds.
recvfrom n : 47
bus.ctrip.com has address 117.131.27.7
Time to live: 0 minutes , 0 seconds
url:car.ctrip.com
connect :0
---usedtime=[6] milliseconds.
recvfrom n : 47
car.ctrip.com has address 117.131.27.0
Time to live: 0 minutes , 0 seconds
url:piao.ctrip.com
connect :0
---usedtime=[26] milliseconds.
recvfrom n : 48
piao.ctrip.com has address 117.131.27.0
Time to live: 0 minutes , 0 seconds
url:tuan.ctrip.com
connect :0
---usedtime=[13] milliseconds.
recvfrom n : 48
tuan.ctrip.com has address 117.186.233.27
Time to live: 0 minutes , 0 seconds
url:you.ctrip.com
connect :0
---usedtime=[14] milliseconds.
recvfrom n : 47
you.ctrip.com has address 117.131.27.7
Time to live: 0 minutes , 0 seconds
url:g.ctrip.com
connect :0
---usedtime=[15] milliseconds.
recvfrom n : 45
g.ctrip.com has address 117.186.233.61
Time to live: 0 minutes , 0 seconds
url:lipin.ctrip.com
connect :0
---usedtime=[22] milliseconds.
recvfrom n : 49
lipin.ctrip.com has address 117.186.233.61
Time to live: 0 minutes , 0 seconds
url:ct.ctrip.com
connect :0
---usedtime=[14] milliseconds.
recvfrom n : 46
ct.ctrip.com has address 117.131.27.0
Time to live: 0 minutes , 0 seconds
43 个 url
url | 时间 |
---|---|
14 | |
6 | |
15 | |
7 | |
7 | |
15 | |
6 | |
7 | |
6 | |
8 | |
6 | |
6 | |
7 | |
15 | |
6 | |
14 | |
6 | |
7 | |
14 | |
6 | |
14 | |
6 | |
7 | |
14 | |
15 | |
9 | |
6 | |
15 | |
6 | |
6 | |
7 | |
6 | |
6 | |
15 | |
14 | |
14 | |
6 | |
26 | |
13 | |
14 | |
15 | |
22 | |
14 | |
合计 | 448 |
例子: 异步DNS请求
异步请求主要使用epoll
commit()
thread_callback()
init_ctx()
destroy()
- 初始化的时候先准备好 epoll epfd 方便 监听 异步的返回消息
- 接收到 异步返回消息的时候,调用回调函数来解析返回的消息。因此必须要先指定回调函数
- 实际提交请求 用 commit
- 销毁 用 destroy
Commit 细节:
1. 创建Socket() bind();
2. connect – 为什么 udp 需要 connect? connect 用于探测,打通链路
3. 准备好 DNS 协议
4. sendto()
同步方案 : recvfrom 等待
异步方案:发送就返回,把 fd 加入到 epoll 里面
创建一个线程,专门去检查 epoll 的触发返回,并执行解析。
代码:
// 1. 初始化 -- 创建 epoll 创建 thread
int dns_async_client_init(struct async_context *ctx,void* callback){
// 1
// epoll
if( ctx == NULL ) return -EINVAL;
ctx->epfd = epoll_create(1);
int ret = pthread_create(&ctx->thid,NULL,callback,ctx);
if(ret){
perror("pthread_create error");
return -1;
}
usleep(1);//child go first
return 0;
}
//===============================
// == 单个线程循环执行-主要负责从epoll 中recv
void dns_async_client_callback(void *arg){
struct async_context *ctx = (struct async_context*)arg;
printf("callbak:epfd:%d\n",ctx->epfd);
printf("callbak:thid:%ld\n",ctx->thid);
while(1){
//printf("callbak.\n");
struct epoll_event events[ASYNC_EVENT_LENGTH];
int nready = epoll_wait(ctx->epfd,events,ASYNC_EVENT_LENGTH,-1);
//printf("nready:%d\n", nready);
if (nready < 0) {
if (errno == EINTR || errno == EAGAIN) {
continue;
} else {
break;
}
} else if (nready == 0) {
continue;
}
//printf("nready:%d\n", nready);
int i = 0;
for(i = 0;i < nready; i++)
{
struct ep_arg *arg = (struct ep_arg *)events[i].data.ptr;
int sockfd = arg->sockfd;
//printf("sockfd:%d\n",sockfd);
char buffer[1024] = {0};
struct sockaddr_in addr;
size_t addr_len = sizeof(struct sockaddr_in);
int n = (int)recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
printf("===================\n");
printf("recvfrom n : %d\n", n);
struct dns_item *domains = NULL;
dns_parse_response(buffer, &domains);
printf("===================\n");
}
}
}
// ================= commit ======
int dns_async_client_commit(struct async_context *ctx,const char *domain)
{
// 1. 创建 socket()
//2.bind() 连接 Connect() 为什么 UDP 需要 connect
//3. dns protocol
//4. sendto()
//5. 同步 recvfrom 卡在这边 异步方案: 发送完就返回,fd 加到epoll 里面
int sockfd = socket(AF_INET,SOCK_DGRAM,0);
if(sockfd < 0){
perror("create socket failed\n");
exit(-1);
}
//printf("url:%s\n",domain);
struct sockaddr_in dest;
bzero(&dest,sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(53);
dest.sin_addr.s_addr = inet_addr(DNS_SVR);
int ret = connect(sockfd, (struct sockaddr *)&dest,sizeof(dest));
//printf("connect :%d\n",ret);
struct dns_header header = {0};
dns_create_header(&header);
struct dns_question question = {0};
dns_create_question(&question,domain);
char request[1024] = {0};
int req_len = dns_build_request(&header,&question,request);
int slen = (int)sendto(sockfd, request, req_len, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr));
struct ep_arg *arg = (struct ep_arg*)calloc(1,sizeof(struct ep_arg));
if(arg == NULL) return -1;
arg->sockfd = sockfd;
struct epoll_event ev;
ev.data.ptr = arg;
ev.events = EPOLLIN;
epoll_ctl(ctx->epfd,EPOLL_CTL_ADD,sockfd,&ev);
//printf(" epoll_ctl ADD: sockfd->%d, ret:%d\n", sockfd, ret);
return ret;
//close(sockfd);
//free(arg);
}
//======================
int dns_async_client_destory(struct async_context *ctx){
if( ctx == NULL ) return -EINVAL;
close(ctx->epfd);
pthread_cancel(ctx->thid);
return 0;
}
//=================main==
int main(int argc,char *argv[])
{
struct async_context ctx;
int res = dns_async_client_init(&ctx,dns_async_client_callback);
//printf("epfd:%d\n",ctx.epfd);
//printf("thid:%ld\n",ctx.thid);
if(res == -1 )
{
printf("error\n");
}
int count = sizeof(domain)/ sizeof(domain[0]);
int i = 0;
for(i=0;i<count;i++){
dns_async_client_commit(&ctx, domain[i]);
}
getchar();
}
有什么更好的方式能减轻 单个线程的CPU的压力。就需要协程。
为什么需要有协程?
1.同步的编程方式,异步的性能。
2. 比线程 和进程更轻量级的线程。具有自我调度功能的。
文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习:
服务器高级架构体系:
https://ke.qq.com/course/417774?flowToken=1010783