3.1.3 异步请求池的实现

一、同步与异步

1.同步的流程:1.发起请求 ----->> 等待 ------>2. 接收响应 ----> 如何改造为异步?
2.同步/异步 两者之间的关系:异步比同步的性能要好
3.同步: 发送请求 等待结果 发送请求 等待结果 …
4.异步: 发送请求后不需要等待结果继续发送,有了结果再一起处理

二、异步的实现

//King式四元组
1.commint(); 1.socket()---> 2.connect -->> 为什么UDP需要connect? --> 发送一个包打通链路
                 -->3.dns protocol ---> 4.shento ---> 5. 异步采用 epoll_ctl(ADD); 同步采用 recvfrom()
2.thread_callback();
3.init_ctx();	 // 1. epoll_create 	2. pthread_create
4.destroy_ctx();

// 1. 参数值明确、返回值明确
int dns_async_client_init(struct async_context *ctx)

// 2. 先malloc再init
struct async_context *dns_async_client_init(void)
// 此处使用第一种

1. dns_async_client_init

//epoll init
//thread init
struct async_context *dns_async_client_init(void) {

	int epfd = epoll_create(1); // 
	if (epfd < 0) return NULL;

	struct async_context *ctx = calloc(1, sizeof(struct async_context));
	if (ctx == NULL) {
		close(epfd);
		return NULL;
	}
	ctx->epfd = epfd;

	pthread_t thread_id;
	int ret = pthread_create(&thread_id, NULL, dns_async_client_proc, ctx);
	if (ret) {
		perror("pthread_create");
		return NULL;
	}
	usleep(1); //child go first

	return ctx;
}

2. dns_async_client_proc

///回调函数
//epoll_wait
//result callback
static void* dns_async_client_proc(void *arg) {
	struct async_context *ctx = (struct async_context*)arg;

	int epfd = ctx->epfd;
	// 不断遍历epoll的IO中有没有数据可读
	while (1) {

		struct epoll_event events[ASYNC_CLIENT_NUM] = {0};

		int nready = epoll_wait(epfd, events, ASYNC_CLIENT_NUM, -1);
		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 ++) {
			
		//遍历IO进行对应处理
			struct ep_arg *data = (struct ep_arg*)events[i].data.ptr;
			int sockfd = data->sockfd;

			char buffer[1024] = {0};
			
			struct sockaddr_in addr;
			size_t addr_len = sizeof(struct sockaddr_in);
			int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);

			//数据解析
			struct dns_item *domain_list = NULL;
			int count = dns_parse_response(buffer, &domain_list);

			data->cb(domain_list, count); //call cb
			
			int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
			//printf("epoll_ctl DEL --> sockfd:%d\n", sockfd);

			close(sockfd); /

			dns_async_client_free_domains(domain_list, count);
			free(data);

		}
		
	}
	
}

3. dns_async_client_free_domains

void dns_async_client_free_domains(struct dns_item *list, int count) {
	int i = 0;

	for (i = 0;i < count;i ++) {
		free(list[i].domain);
		free(list[i].ip);
	}

	free(list);
}

4. dns_async_client_commit

commint()----1.socket()—> 2.connect -->> 为什么UDP需要connect? --> 发送一个包打通链路
–>3.dns protocol —> 4.shento —> 5. 异步采用 epoll_ctl(ADD); 同步采用 recvfrom

//dns_async_client_commit(ctx, domain)
//socket init
//dns_request
//sendto dns send
int dns_async_client_commit(struct async_context* ctx, const char *domain, async_result_cb cb) {
	// 1.socket
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0) {
		perror("create socket failed\n");
		exit(-1);
	}

	printf("url:%s\n", domain);

	set_block(sockfd, 0); //nonblock

	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);
	
	//2.连接对应server
	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 = sendto(sockfd, request, req_len, 0, (struct sockaddr*)&dest, sizeof(struct sockaddr));
	
	struct ep_arg *eparg = (struct ep_arg*)calloc(1, sizeof(struct ep_arg));
	if (eparg == NULL) return -1;
	eparg->sockfd = sockfd;
	eparg->cb = cb;

	struct epoll_event ev;
	ev.data.ptr = eparg;
	ev.events = EPOLLIN;
	// 异步的方式 epoll_ctl
	ret = epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev); 
	//printf(" epoll_ctl ADD: sockfd->%d, ret:%d\n", sockfd, ret);

	return ret;

}

三、协程

1.什么是协程 —> 1.有异步的性能 2.和同步的编程方式一样
2.为什么要有协程?–>> 更轻量级地实现业务
3.异步跳转机制的流程:commit 提交—>> 创建IO—> 准备dns新协议---->> recv_from —> 判断IO是否就绪,继续下一条
在这里插入图片描述

//在一个函数内部commit 既发送请求 也等待结果
//async_recv_from
//1.检测sockfd是否就绪
//2.if就绪,执行recv_from
//3.if没有就绪,跳转

int async_recv_from(int sockfd){
	//如果可读直接返回
	if(poll(sockfd,POLLIN)){
		recv_from();
	}else{//不可读加入epoll
		epoll_ctl(epfd,sockfd);
		longjump();
	}
	return;
}
callback(){
	epoll_wait();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值