客户端请求的时候,有同步请求与异步请求之分:
同步请求,一个请求,等返回后,再继续之后的操作;
异步请求,一个请求,不等待返回,继续做其他操作,有返回后有再进行操作。
如何将同步请求,写成异步请求呢,比如说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式四元组
- 初始化异步操作的环境
- 需要创建线程
- io多路复用epoll
- 销毁异步操作
- 提交异步请求
- 准备socket
- connect
- protocol
- send
- epoll_ctl, EPOLL_CTL_ADD
- 等待调用异步回调函数
- epoll_wait
- recv()
番外
- 当send和recv操作不在同一个线程/进程的时候, 如何判断是哪个请求对应哪个回复?
- 采用自定义协议, 发送请求中携带标志, 返回结果中也带有相同标志;
- 采用epoll, epoll_ctl add的时候, 带上发送请求的标志ev.data.ptr;