• 异步DNS的实现


    目录

    1. 同步和异步是什么?

    2. 为什么异步比同步快?

    3. 设计一个异步的框架(异步DNS)

    框架组成

    1. Init_ctx

    2. Commit

    3. Thread_callback  

    4. Destroy_ctx()

     完整代码

    运行效果

    3. 总结

    什么是协程?


    1. 同步和异步是什么?

    同步:执行一个操作之后,等待结果,然后才继续执行后续的操作。

    异步:执行一个操作后,可以去执行其他的操作,然后等待通知再回来执行刚才没执行完的操作。

    阻塞:进程给CPU传达一个任务之后,一直等待CPU处理完成,然后才执行后面的操作。

    非阻塞:进程给CPU传达任我后,继续处理后续的操作,隔断时间再来询问之前的操作是否完成。这样的过程其实也叫轮询

    2. 为什么异步比同步快?

    同步要发起请求后,等待结果返回后再发送下一个请求。(串行)

    异步不用等待结果返回,一直发送请求。相当于一起发送,一起返回(并行)

     

    3. 设计一个异步的框架(异步DNS)

    框架组成

    1. Init_ctx();  epoll_create pthread_create
    2. Commit();
    3. Thread_callback();  线程检测IO是否可读
    4. Destroy_ctx(); 销毁

    1. Init_ctx

    epoll初始化,线程的初始化

    1. struct async_context *dns_async_client_init(void) {
    2. int epfd = epoll_create(1); //
    3. if (epfd < 0) return NULL;
    4. struct async_context *ctx = calloc(1, sizeof(struct async_context));
    5. if (ctx == NULL) {
    6. close(epfd);
    7. return NULL;
    8. }
    9. ctx->epfd = epfd;
    10. pthread_t thread_id;
    11. int ret = pthread_create(&thread_id, NULL, dns_async_client_proc, ctx);
    12. if (ret) {
    13. perror("pthread_create");
    14. return NULL;
    15. }
    16. usleep(1); //child go first
    17. return ctx;
    18. }

    2. Commit

            主要工作的创建socket、dns协议的封装,然后发送UDP请求给DNS服务器,将sockfd加入epoll监控。

    1. //dns_async_client_commit(ctx, domain)
    2. //socket init
    3. //dns_request
    4. //sendto dns send
    5. int dns_async_client_commit(struct async_context* ctx, const char *domain, async_result_cb cb) {
    6. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    7. if (sockfd < 0) {
    8. perror("create socket failed\n");
    9. exit(-1);
    10. }
    11. printf("url:%s\n", domain);
    12. //设置为非阻塞
    13. set_block(sockfd, 0); //nonblock
    14. struct sockaddr_in dest;
    15. bzero(&dest, sizeof(dest));
    16. dest.sin_family = AF_INET;
    17. dest.sin_port = htons(53);
    18. dest.sin_addr.s_addr = inet_addr(DNS_SVR);
    19. int ret = connect(sockfd, (struct sockaddr*)&dest, sizeof(dest));
    20. //printf("connect :%d\n", ret);
    21. struct dns_header header = {0};
    22. dns_create_header(&header); //填充 header
    23. struct dns_question question = {0};
    24. dns_create_question(&question, domain); //填充 question
    25. char request[1024] = {0};
    26. int req_len = dns_build_request(&header, &question, request); //准备请求头
    27. int slen = sendto(sockfd, request, req_len, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr));
    28. struct ep_arg *eparg = (struct ep_arg*)calloc(1, sizeof(struct ep_arg));
    29. if (eparg == NULL) return -1;
    30. eparg->sockfd = sockfd;
    31. eparg->cb = cb;
    32. //加入epoll
    33. struct epoll_event ev;
    34. ev.data.ptr = eparg;
    35. ev.events = EPOLLIN;
    36. ret = epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev);
    37. //printf(" epoll_ctl ADD: sockfd->%d, ret:%d\n", sockfd, ret);
    38. return ret;
    39. }

    3. Thread_callback  

            线程检测IO是否可读,解析DNS应答,打印IP信息。

    1. //dns_async_client_proc()
    2. //epoll_wait
    3. //result callback
    4. static void* dns_async_client_proc(void *arg) {
    5. struct async_context *ctx = (struct async_context*)arg;
    6. int epfd = ctx->epfd;
    7. while (1) {
    8. struct epoll_event events[ASYNC_CLIENT_NUM] = {0};
    9. int nready = epoll_wait(epfd, events, ASYNC_CLIENT_NUM, -1);
    10. if (nready < 0) {
    11. if (errno == EINTR || errno == EAGAIN) { //非阻塞
    12. continue;
    13. } else {
    14. break;
    15. }
    16. } else if (nready == 0) {
    17. continue;
    18. }
    19. printf("nready:%d\n", nready);
    20. int i = 0;
    21. for (i = 0;i < nready;i ++) {
    22. struct ep_arg *data = (struct ep_arg*)events[i].data.ptr;
    23. int sockfd = data->sockfd;
    24. char buffer[1024] = {0};
    25. struct sockaddr_in addr;
    26. size_t addr_len = sizeof(struct sockaddr_in);
    27. int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
    28. struct dns_item *domain_list = NULL;
    29. int count = dns_parse_response(buffer, &domain_list);
    30. data->cb(domain_list, count); //call cb
    31. int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
    32. //printf("epoll_ctl DEL --> sockfd:%d\n", sockfd);
    33. close(sockfd); /
    34. dns_async_client_free_domains(domain_list, count);
    35. free(data);
    36. }
    37. }
    38. }

    4. Destroy_ctx()

    销毁线程和关闭epfd.

    1. int dns_async_client_destroy(struct async_context *ctx) {
    2. if (ctx == NULL) return -EINVAL;
    3. pthread_cancel(ctx->thid);
    4. close(ctx->epfd);
    5. return 0;
    6. }

     完整代码

    1. //gcc -o client async_dns_client_noblock.c -lpthread
    2. #include <stdio.h>
    3. #include <stdlib.h>
    4. #include <string.h>
    5. #include <unistd.h>
    6. #include <errno.h>
    7. #include <fcntl.h>
    8. #include <sys/types.h>
    9. #include <sys/socket.h>
    10. #include <netinet/in.h>
    11. #include <sys/epoll.h>
    12. #include <netdb.h>
    13. #include <arpa/inet.h>
    14. #include <pthread.h>
    15. #define DNS_SVR "192.168.1.1"
    16. #define DNS_HOST 0x01
    17. #define DNS_CNAME 0x05
    18. #define ASYNC_CLIENT_NUM 1024
    19. //DNS 头部
    20. struct dns_header {
    21. unsigned short id;
    22. unsigned short flags;
    23. unsigned short qdcount;
    24. unsigned short ancount;
    25. unsigned short nscount;
    26. unsigned short arcount;
    27. };
    28. //DNS请求
    29. struct dns_question {
    30. int length;
    31. unsigned short qtype;
    32. unsigned short qclass;
    33. char *qname;
    34. };
    35. struct dns_item {
    36. char *domain;
    37. char *ip;
    38. };
    39. typedef void (*async_result_cb)(struct dns_item *list, int count);
    40. //epoll结构体
    41. struct async_context {
    42. int epfd;
    43. };
    44. //epoll参数
    45. struct ep_arg {
    46. int sockfd;
    47. async_result_cb cb;
    48. };
    49. //填充 header
    50. int dns_create_header(struct dns_header *header) {
    51. if (header == NULL) return -1;
    52. memset(header, 0, sizeof(struct dns_header));
    53. srandom(time(NULL));
    54. header->id = random();
    55. header->flags |= htons(0x0100);
    56. header->qdcount = htons(1);
    57. return 0;
    58. }
    59. //填充 question
    60. int dns_create_question(struct dns_question *question, const char *hostname) {
    61. if (question == NULL) return -1;
    62. memset(question, 0, sizeof(struct dns_question));
    63. question->qname = (char*)malloc(strlen(hostname) + 2);
    64. if (question->qname == NULL) return -2;
    65. question->length = strlen(hostname) + 2;
    66. question->qtype = htons(1);
    67. question->qclass = htons(1);
    68. const char delim[2] = ".";
    69. char *hostname_dup = strdup(hostname);
    70. char *token = strtok(hostname_dup, delim);
    71. char *qname_p = question->qname;
    72. while (token != NULL) {
    73. size_t len = strlen(token);
    74. *qname_p = len;
    75. qname_p ++;
    76. strncpy(qname_p, token, len+1);
    77. qname_p += len;
    78. token = strtok(NULL, delim);
    79. }
    80. free(hostname_dup);
    81. return 0;
    82. }
    83. //准备请求头
    84. int dns_build_request(struct dns_header *header, struct dns_question *question, char *request) {
    85. int header_s = sizeof(struct dns_header);
    86. int question_s = question->length + sizeof(question->qtype) + sizeof(question->qclass);
    87. int length = question_s + header_s;
    88. int offset = 0;
    89. memcpy(request+offset, header, sizeof(struct dns_header));
    90. offset += sizeof(struct dns_header);
    91. memcpy(request+offset, question->qname, question->length);
    92. offset += question->length;
    93. memcpy(request+offset, &question->qtype, sizeof(question->qtype));
    94. offset += sizeof(question->qtype);
    95. memcpy(request+offset, &question->qclass, sizeof(question->qclass));
    96. return length;
    97. }
    98. static int is_pointer(int in) {
    99. return ((in & 0xC0) == 0xC0);
    100. }
    101. static int set_block(int fd, int block) {
    102. int flags = fcntl(fd, F_GETFL, 0);
    103. if (flags < 0) return flags;
    104. if (block) {
    105. flags &= ~O_NONBLOCK;
    106. } else {
    107. flags |= O_NONBLOCK;
    108. }
    109. if (fcntl(fd, F_SETFL, flags) < 0) return -1;
    110. return 0;
    111. }
    112. static void dns_parse_name(unsigned char *chunk, unsigned char *ptr, char *out, int *len) {
    113. int flag = 0, n = 0, alen = 0;
    114. char *pos = out + (*len);
    115. while (1) {
    116. flag = (int)ptr[0];
    117. if (flag == 0) break;
    118. if (is_pointer(flag)) {
    119. n = (int)ptr[1];
    120. ptr = chunk + n;
    121. dns_parse_name(chunk, ptr, out, len);
    122. break;
    123. } else {
    124. ptr ++;
    125. memcpy(pos, ptr, flag);
    126. pos += flag;
    127. ptr += flag;
    128. *len += flag;
    129. if ((int)ptr[0] != 0) {
    130. memcpy(pos, ".", 1);
    131. pos += 1;
    132. (*len) += 1;
    133. }
    134. }
    135. }
    136. }
    137. //解析 response
    138. static int dns_parse_response(char *buffer, struct dns_item **domains) {
    139. int i = 0;
    140. unsigned char *ptr = buffer;
    141. ptr += 4;
    142. int querys = ntohs(*(unsigned short*)ptr);
    143. ptr += 2;
    144. int answers = ntohs(*(unsigned short*)ptr);
    145. ptr += 6;
    146. for (i = 0;i < querys;i ++) {
    147. while (1) {
    148. int flag = (int)ptr[0];
    149. ptr += (flag + 1);
    150. if (flag == 0) break;
    151. }
    152. ptr += 4;
    153. }
    154. char cname[128], aname[128], ip[20], netip[4];
    155. int len, type, ttl, datalen;
    156. int cnt = 0;
    157. struct dns_item *list = (struct dns_item*)calloc(answers, sizeof(struct dns_item));
    158. if (list == NULL) {
    159. return -1;
    160. }
    161. for (i = 0;i < answers;i ++) {
    162. bzero(aname, sizeof(aname));
    163. len = 0;
    164. dns_parse_name(buffer, ptr, aname, &len);
    165. ptr += 2;
    166. type = htons(*(unsigned short*)ptr);
    167. ptr += 4;
    168. ttl = htons(*(unsigned short*)ptr);
    169. ptr += 4;
    170. datalen = ntohs(*(unsigned short*)ptr);
    171. ptr += 2;
    172. if (type == DNS_CNAME) {
    173. bzero(cname, sizeof(cname));
    174. len = 0;
    175. dns_parse_name(buffer, ptr, cname, &len);
    176. ptr += datalen;
    177. } else if (type == DNS_HOST) {
    178. bzero(ip, sizeof(ip));
    179. if (datalen == 4) {
    180. memcpy(netip, ptr, datalen);
    181. inet_ntop(AF_INET , netip , ip , sizeof(struct sockaddr));
    182. //printf("%s has address %s\n" , aname, ip);
    183. //printf("\tTime to live: %d minutes , %d seconds\n", ttl / 60, ttl % 60);
    184. list[cnt].domain = (char *)calloc(strlen(aname) + 1, 1);
    185. memcpy(list[cnt].domain, aname, strlen(aname));
    186. list[cnt].ip = (char *)calloc(strlen(ip) + 1, 1);
    187. memcpy(list[cnt].ip, ip, strlen(ip));
    188. cnt ++;
    189. }
    190. ptr += datalen;
    191. }
    192. }
    193. *domains = list;
    194. ptr += 2;
    195. return cnt;
    196. }
    197. //域名请求
    198. int dns_client_commit(const char *domain) {
    199. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    200. if (sockfd < 0) {
    201. perror("create socket failed\n");
    202. exit(-1);
    203. }
    204. printf("url:%s\n", domain);
    205. set_block(sockfd, 0); //nonblock
    206. struct sockaddr_in dest;
    207. bzero(&dest, sizeof(dest));
    208. dest.sin_family = AF_INET;
    209. dest.sin_port = htons(53);
    210. dest.sin_addr.s_addr = inet_addr(DNS_SVR);
    211. int ret = connect(sockfd, (struct sockaddr*)&dest, sizeof(dest));
    212. //printf("connect :%d\n", ret);
    213. struct dns_header header = {0};
    214. dns_create_header(&header);
    215. struct dns_question question = {0};
    216. dns_create_question(&question, domain);
    217. char request[1024] = {0};
    218. int req_len = dns_build_request(&header, &question, request);
    219. int slen = sendto(sockfd, request, req_len, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr));
    220. while (1) {
    221. char buffer[1024] = {0};
    222. struct sockaddr_in addr;
    223. size_t addr_len = sizeof(struct sockaddr_in);
    224. int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
    225. if (n <= 0) continue;
    226. printf("recvfrom n : %d\n", n);
    227. struct dns_item *domains = NULL;
    228. dns_parse_response(buffer, &domains);
    229. break;
    230. }
    231. return 0;
    232. }
    233. //销毁
    234. void dns_async_client_free_domains(struct dns_item *list, int count) {
    235. int i = 0;
    236. for (i = 0;i < count;i ++) {
    237. free(list[i].domain);
    238. free(list[i].ip);
    239. }
    240. free(list);
    241. }
    242. //dns_async_client_proc()
    243. //epoll_wait
    244. //result callback
    245. static void* dns_async_client_proc(void *arg) {
    246. struct async_context *ctx = (struct async_context*)arg;
    247. int epfd = ctx->epfd;
    248. while (1) {
    249. struct epoll_event events[ASYNC_CLIENT_NUM] = {0};
    250. int nready = epoll_wait(epfd, events, ASYNC_CLIENT_NUM, -1);
    251. if (nready < 0) {
    252. if (errno == EINTR || errno == EAGAIN) { //非阻塞
    253. continue;
    254. } else {
    255. break;
    256. }
    257. } else if (nready == 0) {
    258. continue;
    259. }
    260. printf("nready:%d\n", nready);
    261. int i = 0;
    262. for (i = 0;i < nready;i ++) {
    263. struct ep_arg *data = (struct ep_arg*)events[i].data.ptr;
    264. int sockfd = data->sockfd;
    265. char buffer[1024] = {0};
    266. struct sockaddr_in addr;
    267. size_t addr_len = sizeof(struct sockaddr_in);
    268. int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
    269. struct dns_item *domain_list = NULL;
    270. int count = dns_parse_response(buffer, &domain_list);
    271. data->cb(domain_list, count); //call cb
    272. int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
    273. //printf("epoll_ctl DEL --> sockfd:%d\n", sockfd);
    274. close(sockfd); /
    275. dns_async_client_free_domains(domain_list, count);
    276. free(data);
    277. }
    278. }
    279. }
    280. //dns_async_client_init()
    281. //epoll init
    282. //thread init
    283. struct async_context *dns_async_client_init(void) {
    284. int epfd = epoll_create(1); //
    285. if (epfd < 0) return NULL;
    286. struct async_context *ctx = calloc(1, sizeof(struct async_context));
    287. if (ctx == NULL) {
    288. close(epfd);
    289. return NULL;
    290. }
    291. ctx->epfd = epfd;
    292. pthread_t thread_id;
    293. int ret = pthread_create(&thread_id, NULL, dns_async_client_proc, ctx);
    294. if (ret) {
    295. perror("pthread_create");
    296. return NULL;
    297. }
    298. usleep(1); //child go first
    299. return ctx;
    300. }
    301. //dns_async_client_commit(ctx, domain)
    302. //socket init
    303. //dns_request
    304. //sendto dns send
    305. int dns_async_client_commit(struct async_context* ctx, const char *domain, async_result_cb cb) {
    306. int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    307. if (sockfd < 0) {
    308. perror("create socket failed\n");
    309. exit(-1);
    310. }
    311. printf("url:%s\n", domain);
    312. //设置为非阻塞
    313. set_block(sockfd, 0); //nonblock
    314. struct sockaddr_in dest;
    315. bzero(&dest, sizeof(dest));
    316. dest.sin_family = AF_INET;
    317. dest.sin_port = htons(53);
    318. dest.sin_addr.s_addr = inet_addr(DNS_SVR);
    319. int ret = connect(sockfd, (struct sockaddr*)&dest, sizeof(dest));
    320. //printf("connect :%d\n", ret);
    321. struct dns_header header = {0};
    322. dns_create_header(&header); //填充 header
    323. struct dns_question question = {0};
    324. dns_create_question(&question, domain); //填充 question
    325. char request[1024] = {0};
    326. int req_len = dns_build_request(&header, &question, request); //准备请求头
    327. int slen = sendto(sockfd, request, req_len, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr));
    328. struct ep_arg *eparg = (struct ep_arg*)calloc(1, sizeof(struct ep_arg));
    329. if (eparg == NULL) return -1;
    330. eparg->sockfd = sockfd;
    331. eparg->cb = cb;
    332. //加入epoll
    333. struct epoll_event ev;
    334. ev.data.ptr = eparg;
    335. ev.events = EPOLLIN;
    336. ret = epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev);
    337. //printf(" epoll_ctl ADD: sockfd->%d, ret:%d\n", sockfd, ret);
    338. return ret;
    339. }
    340. //回调函数
    341. static void dns_async_client_result_callback(struct dns_item *list, int count) {
    342. int i = 0;
    343. for (i = 0;i < count;i ++) {
    344. printf("name:%s, ip:%s\n", list[i].domain, list[i].ip);
    345. }
    346. }
    347. char *domain[] = {
    348. "www.ntytcp.com",
    349. "bojing.wang",
    350. "www.baidu.com",
    351. "tieba.baidu.com",
    352. "news.baidu.com",
    353. "zhidao.baidu.com",
    354. "music.baidu.com",
    355. "image.baidu.com",
    356. "v.baidu.com",
    357. "map.baidu.com",
    358. "baijiahao.baidu.com",
    359. "xueshu.baidu.com",
    360. "cloud.baidu.com",
    361. "www.163.com",
    362. "open.163.com",
    363. "auto.163.com",
    364. "gov.163.com",
    365. "money.163.com",
    366. "sports.163.com",
    367. "tech.163.com",
    368. "edu.163.com",
    369. "www.taobao.com",
    370. "q.taobao.com",
    371. "sf.taobao.com",
    372. "yun.taobao.com",
    373. "baoxian.taobao.com",
    374. "www.tmall.com",
    375. "suning.tmall.com",
    376. "www.tencent.com",
    377. "www.qq.com",
    378. "www.aliyun.com",
    379. "www.ctrip.com",
    380. "hotels.ctrip.com",
    381. "hotels.ctrip.com",
    382. "vacations.ctrip.com",
    383. "flights.ctrip.com",
    384. "trains.ctrip.com",
    385. "bus.ctrip.com",
    386. "car.ctrip.com",
    387. "piao.ctrip.com",
    388. "tuan.ctrip.com",
    389. "you.ctrip.com",
    390. "g.ctrip.com",
    391. "lipin.ctrip.com",
    392. "ct.ctrip.com"
    393. };
    394. int main(int argc, char *argv[]) {
    395. #if 0
    396. dns_client_commit(argv[1]);
    397. #else
    398. struct async_context *ctx = dns_async_client_init();
    399. if (ctx == NULL) return -2;
    400. int count = sizeof(domain) / sizeof(domain[0]);
    401. int i = 0;
    402. for (i = 0;i < count;i ++) {
    403. dns_async_client_commit(ctx, domain[i], dns_async_client_result_callback);
    404. //sleep(2);
    405. }
    406. getchar();
    407. #endif
    408. }

    运行效果

    3. 总结

    同步的方案

    Recvfrom()

    异步的方案

    Epoll_ctl();

    异步的优点:异步比同步性能更好

    异步的缺点:异步不好理解,流程不清晰

    什么是协程?

    1.有异步的性能

    2.同步的编程方式

            协程,是一种比线程更加轻量级的存在,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

  • 相关阅读:
    微突发丢包的艺术
    Android源码下载
    【Vue3】使用v-model实现父子组件通信(常用在组件封装规范中)
    C++DAY48
    C语言文件操作
    前端食堂技术周刊第 100 期:TS 5.3 Beta、ViteConf2023、Rspress 1.0、Fresh 1.5、Chrome 118
    如何在 pyqt 中自定义工具提示 ToolTip
    yolov6训练记录
    最优化方法——QR分解
    六轴传感器 SH3001
  • 原文地址:https://blog.csdn.net/kakaka666/article/details/126438490