网络编程6:网路同步请求和异步请求

客户端请求的时候,有同步请求与异步请求之分:
同步请求,一个请求,等返回后,再继续之后的操作;
异步请求,一个请求,不等待返回,继续做其他操作,有返回后有再进行操作。

如何将同步请求,写成异步请求呢,比如说sql同步请求,http同步请求…

以DNS请求为例
借用这位巨佬写的DNS请求代码
https://blog.csdn.net/mirkerson/article/details/5907955

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define DNS_SVR "114.114.114.114"
 
#define DNS_HOST  0x01
#define DNS_CNAME 0x05
 
void 
send_dns_request(int socketfd, const char *dns_name);
 
void
parse_dns_response(int socketfd);
 
/**
 * Generate DNS question chunk
 */
static void 
generate_question(const char *dns_name
		, unsigned char *buf , int *len);
 
/**
 * Check whether the current byte is 
 * a dns pointer or a length
 */
static int
is_pointer(int in);
 
/**
 * Parse data chunk into dns name
 * @param chunk The complete response chunk
 * @param ptr The pointer points to data
 * @param out This will be filled with dns name
 * @param len This will be filled with the length of dns name
 */
static void
parse_dns_name(unsigned char *chunk , unsigned char *ptr
		, char *out , int *len);

void parse_dns_response(int socketfd){
 
	unsigned char buf[1024];
	unsigned char *ptr = buf;
	struct sockaddr_in addr;
	char *src_ip;
	int n , i , flag , querys , answers;
	int type , ttl , datalen , len;
	char cname[128] , aname[128] , ip[20] , *cname_ptr;
	unsigned char netip[4];
	size_t addr_len = sizeof(struct sockaddr_in);
 
	//n = recvfrom(socketfd , buf , sizeof(buf) , 0
	//	, (struct sockaddr*)&addr , &addr_len);
	n = recv(socketfd,buf,sizeof(buf),0);
	ptr += 4; /* move ptr to Questions */
	querys = ntohs(*((unsigned short*)ptr));
	ptr += 2; /* move ptr to Answer RRs */
	answers = ntohs(*((unsigned short*)ptr));
	ptr += 6; /* move ptr to Querys */
	/* move over Querys */
	for(i= 0 ; i < querys ; i ++){
		for(;;){
			flag = (int)ptr[0];
			ptr += (flag + 1);
			if(flag == 0)
				break;
		}
		ptr += 4;
	}
	printf("-------------------------------\n");
	/* now ptr points to Answers */
	for(i = 0 ; i < answers ; i ++){
		bzero(aname , sizeof(aname));
		len = 0;
		parse_dns_name(buf , ptr , aname , &len);
		ptr += 2; /* move ptr to Type*/
		type = htons(*((unsigned short*)ptr));
		ptr += 4; /* move ptr to Time to live */
		ttl = htonl(*((unsigned int*)ptr));
		ptr += 4; /* move ptr to Data lenth */
		datalen = ntohs(*((unsigned short*)ptr));
		ptr += 2; /* move ptr to Data*/
		if(type == DNS_CNAME){
			bzero(cname , sizeof(cname));
			len = 0;
			parse_dns_name(buf , ptr , cname , &len);
			printf("%s is an alias for %s\n" , aname , cname);
			ptr += datalen;
		}
		if(type == DNS_HOST){
			bzero(ip , sizeof(ip));
			if(datalen == 4){
				memcpy(netip , ptr , datalen);
				inet_ntop(AF_INET , netip , ip , sizeof(struct sockaddr));
				printf("%s has address %s\n" , aname , ip);
				printf("\tTime to live: %d minutes , %d seconds\n"
						, ttl / 60 , ttl % 60);
			}
			ptr += datalen;
		}
 
	}
	ptr += 2;
}
 
static void
parse_dns_name(unsigned char *chunk
		, unsigned char *ptr , char *out , int *len){
	int n , alen , flag;
	char *pos = out + (*len);
 
	for(;;){
		flag = (int)ptr[0];
		if(flag == 0)
			break;
		if(is_pointer(flag)){
			n = (int)ptr[1];
			ptr = chunk + n;
			parse_dns_name(chunk , ptr , out , len);
			break;
		}else{
			ptr ++;
			memcpy(pos , ptr , flag);	
			pos += flag;
			ptr += flag;
			*len += flag;
			if((int)ptr[0] != 0){
				memcpy(pos , "." , 1);
				pos += 1;
				(*len) += 1;
			}
		}
	}
 
}
 
static int is_pointer(int in){
	return ((in & 0xc0) == 0xc0);
}
 
void send_dns_request(int socketfd,const char *dns_name){
 
	unsigned char request[256];
	unsigned char *ptr = request;
	unsigned char question[128];
	int question_len;
 
 
	generate_question(dns_name , question , &question_len);
 
	*((unsigned short*)ptr) = htons(0xff00);
	ptr += 2;
	*((unsigned short*)ptr) = htons(0x0100);
	ptr += 2;
	*((unsigned short*)ptr) = htons(1);
	ptr += 2;
	*((unsigned short*)ptr) = 0;
	ptr += 2;
	*((unsigned short*)ptr) = 0;
	ptr += 2;
	*((unsigned short*)ptr) = 0;
	ptr += 2;
	memcpy(ptr , question , question_len);
	ptr += question_len;
 
	//sendto(socketfd , request , question_len + 12 , 0
	//   , (struct sockaddr*)&dest , sizeof(struct sockaddr));
	send(socketfd,request,question_len + 12 , 0);
}
 
static void
generate_question(const char *dns_name , unsigned char *buf , int *len){
	char *pos;
	unsigned char *ptr;
	int n;
 
	*len = 0;
	ptr = buf;	
	pos = (char*)dns_name; 
	for(;;){
		n = strlen(pos) - (strstr(pos , ".") ? strlen(strstr(pos , ".")) : 0);
		*ptr ++ = (unsigned char)n;
		memcpy(ptr , pos , n);
		*len += n + 1;
		ptr += n;
		if(!strstr(pos , ".")){
			*ptr = (unsigned char)0;
			ptr ++;
			*len += 1;
			break;
		}
		pos += n + 1;
	}
	*((unsigned short*)ptr) = htons(1);
	*len += 2;
	ptr += 2;
	*((unsigned short*)ptr) = htons(1);
	*len += 2;
}

同步请求

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define DNS_SVR "114.114.114.114"


char *host[] = {
	"baidu.com",
	"bilibili.com",
	"gamersky.com",
	"taobao.com",
	"weibo.com",
	"jd.com",
	"dangdang.com",
	"amazon.com",
	"google.com",
};


int init_sock(){
	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1){
		perror("create socket failed");
		return -1;
	}

	struct sockaddr_in dest;
	dest.sin_family = AF_INET;
	dest.sin_port = htons(53);
	inet_pton(AF_INET,DNS_SVR,&dest.sin_addr);
	
	if(connect(socketfd,(struct sockaddr*)&dest,sizeof(dest)) < 0){
		perror("connect socket failed");
		close(socketfd);
		return -1;
	}

	return socketfd;
}

int main(){
	int i;
	int socketfd;
	for(i=0;i<sizeof(host)/sizeof(char*);i++){
		socketfd = init_sock();
		if(socketfd == -1){
			break;
		}

		send_dns_request(socketfd,host[i]);
		parse_dns_response(socketfd);
		close(socketfd);
	}
 
	return 0;
}

异步请求

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include <sys/epoll.h>
#include <pthread.h>

#define DNS_SVR "114.114.114.114"

char *host[] = {
	"baidu.com",
	"bilibili.com",
	"gamersky.com",
	"taobao.com",
	"weibo.com",
	"jd.com",
	"dangdang.com",
	"amazon.com",
	"google.com",
};

int init_sock(){
	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1){
		perror("create socket failed");
		return -1;
	}

	struct sockaddr_in dest;
	dest.sin_family = AF_INET;
	dest.sin_port = htons(53);
	inet_pton(AF_INET,DNS_SVR,&dest.sin_addr);
	
	if(connect(socketfd,(struct sockaddr*)&dest,sizeof(dest)) < 0){
		perror("connect socket failed");
		close(socketfd);
		return -1;
	}

	return socketfd;
}

void* thread_run(void*arg){
	int epfd = *(int*)arg;

	struct epoll_event events[100];
	int nready,i,socketfd;
	for(;;){
		nready = epoll_wait(epfd,events,100,1000);
		if(nready == 0){
			break;
		}
		for(i=0;i<nready;i++){
			socketfd = events[i].data.fd;
			if(events[i].events &EPOLLIN){
				parse_dns_response(socketfd);
			}
			epoll_ctl(epfd,EPOLL_CTL_DEL,socketfd,NULL);
			close(socketfd);
		}
	}
	pthread_exit(NULL);
}

int main(){
	int i;
	int socketfd;

	int epfd = epoll_create(1);
	struct epoll_event ev;
	pthread_t tid;

	pthread_create(&tid,NULL,thread_run,(void*)&epfd);

	for(i=0;i<sizeof(host)/sizeof(char*);i++){
		socketfd = init_sock();
		if(socketfd == -1){
			break;
		}

		send_dns_request(socketfd,host[i]);
		ev.data.fd = socketfd;
		ev.events = EPOLLIN;
		epoll_ctl(epfd,EPOLL_CTL_ADD,socketfd,&ev);
	}
	pthread_join(tid,NULL);
	close(epfd);
	return 0;
}

epoll的线程安全
https://blog.csdn.net/u011344601/article/details/51997886

king式四元组

  1. 初始化异步操作的环境
  • 需要创建线程
  • io多路复用epoll
  1. 销毁异步操作
  2. 提交异步请求
  • 准备socket
  • connect
  • protocol
  • send
  • epoll_ctl, EPOLL_CTL_ADD
  1. 等待调用异步回调函数
  • epoll_wait
  • recv()

番外

  1. 当send和recv操作不在同一个线程/进程的时候, 如何判断是哪个请求对应哪个回复?
  • 采用自定义协议, 发送请求中携带标志, 返回结果中也带有相同标志;
  • 采用epoll, epoll_ctl add的时候, 带上发送请求的标志ev.data.ptr;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值