正文8:Http协议、Restful、异步http请求简单了解(未完善http相关信息,但是异步请求代码已经完成10/07)

1.http知识(待补充)

HTTP Content-type判断返回数据的类型:图片、视频还是文字形式
静态资源都是get
200代表http请求成功

2.异步怎么设计?

异步设计步骤(适用dns\http\redis\mysql等)

1)初始化异步操作的上下文(上下文就是环境)
  • 结构体如下
//上下文
struct async_context
{
	pthread_t threadid;		//线程id
	int epfd;				//套接字
};
a.创建线程

默认线程分配8M空间

b.epoll_create
c.代码

在这里插入图片描述

2)异步上下文的销毁
a.pthread_cancel
b.close
c.代码

在这里插入图片描述

3)commit提交请求
  • commit的参数列表:

①上下文epoll,表示加入到哪个epoll
②发送哪个服务器
③访问服务器的哪个资源
④返回结果怎么处理,处理函数

a.准备socket
b.connect
c.protocal
d.send
e.epoll_ctl(add , epollfd)把准备的io加入epoll里面
4)callback回调函数
a.epoll_wait()检测io有没有数据,有数据就开始读
b.recv()

3.同步http代码

  • 代码作用解释及效果展示

代码作用
该代码会发送http请求,然后打印数据,效果如下
在这里插入图片描述

  • 代码
编译指令  gcc sync_http.c  -o  sync_http
./sync_http

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
 
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>       /* close */
#include <netdb.h> 
 
#include <sys/epoll.h>
#include <pthread.h>
 
 
#define HTTP_VERSION    "HTTP/1.1"
#define USER_AGENT      "User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2\r\n"
#define ENCODE_TYPE     "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
#define CONNECTION_TYPE "Connection: close\r\n"
 
 
 
#define BUFFER_SIZE     4096
 

 //传入网络地址转成字符串传出
char *host_to_ip(const char *hostname) {

    //获取主机的地址,现在是有新的函数,这个函数已经过期了
    struct hostent *host_entry = gethostbyname(hostname); //gethostbyname_r / getaddrinfo
    if (host_entry) {
        //将网络地址换成字符串的格式
        return inet_ntoa(*(struct in_addr*)*host_entry->h_addr_list);
    } else {
        return NULL;
    }
}

//创建一个套接字
int http_create_socket( char *ip) {
 
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
    struct sockaddr_in sin = {0};
    sin.sin_addr.s_addr = inet_addr(ip);
    sin.sin_port = htons(80);               //主机字节序转网络字节序
    sin.sin_family = AF_INET;
 
    if (-1 == connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {
        return -1;
    }
 
    fcntl(sockfd, F_SETFL, O_NONBLOCK);
 
    return sockfd;
 
}
 

//将数据(HTTP请求头)发送出去,发给服务器
char *http_send_request(int sockfd, const char *hostname, const char *resource) {
 
    char buffer[BUFFER_SIZE] = {0};
     
    int len = sprintf(buffer, 
"GET %s %s\r\n\
Host: %s\r\n\
%s\r\n\
\r\n",
         resource, HTTP_VERSION,
         hostname,
         CONNECTION_TYPE
         );
    printf("request buffer:%s\n", buffer);
    send(sockfd, buffer, strlen(buffer), 0);
 
    struct timeval tv;
    tv.tv_sec = 5;
    tv.tv_usec = 0;
 
    fd_set fdread;
    FD_ZERO(&fdread);
    FD_SET(sockfd, &fdread);
 
    char *result = malloc(sizeof(int));
    result[0] = '\0';
 
    while (1) {
 
        int selection = select(sockfd+1, &fdread, NULL, NULL, &tv);
        if (!selection || !(FD_ISSET(sockfd, &fdread))) {
            break;
        } else {
            len = recv(sockfd, buffer, BUFFER_SIZE, 0);
            if (len == 0) break;
 
            result = realloc(result, (strlen(result) + len + 1) * sizeof(char));
            strncat(result, buffer, len);
        }
    }
 
    return result;
 
}
 
 
int http_client_commit(const char *hostname, const char *resource) {
    //1.获得ip
    char *ip = host_to_ip(hostname);

    //2.创建套接字,获得套接字
    int sockfd = http_create_socket(ip);

    //3.给服务器发送数据
    char *content =  http_send_request(sockfd, hostname, resource);
    if (content == NULL) {
        printf("have no data\n");
    }

    //4.打印出来数据
    puts(content);
    close(sockfd);
    free(content);

    return 0;
}


struct http_request {
    char *hostname;
    char *resource;
};
 

struct http_request reqs[] = {
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=beijing&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=changsha&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=shenzhen&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=shanghai&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=tianjin&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=wuhan&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=hefei&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=hangzhou&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=nanjing&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=jinan&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=taiyuan&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=wuxi&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=suzhou&language=zh-Hans&unit=c" },
};
 

struct async_context {
    pthread_t threadid;
    int epfd;
};

#define HOSTNAME_LENGTH     1024
#define ASYNC_CLIENT_NUM    1024

struct ep_arg {
    int sockfd;
    char hostname[HOSTNAME_LENGTH];
    async_result_cb cb;
};

typedef void (*async_result_cb)(const char *hostname, const char *result);

void *http_async_client_callback(void *arg) {

    struct async_context *ctx = (struct async_context*)arg;
    int epfd = ctx->epfd;

    while (1) {

        struct epoll_event events[]
        int nready = epoll_wait(epfd, events, ASYNC_CLIENT_NUM, -1);

        int i = 0;
        for (i = 0;i < nready;i ++) {
            
            struct ep_arg *data = (struct ep_arg*)events[i].data.ptr;
            int sockfd = data->sockfd;

            recv();

            epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);

            close(sockfd);
            free(data);

        }

    }

}


struct async_context* http_async_client_init(void) {

    struct async_context *ctx = (struct async_context*)malloc(sizeof(struct async_context));
    if (!ctx) {
        return NULL;
    }

    ctx->epfd = epoll_create(1);

    int ret = pthread_create(&ctx->threadid, NULL, http_async_client_callback, ctx); //创建的线程
    if (ret) {
        perror("pthread_create");
        close(ctx->epfd);
        free(ctx);
        return NULL;
    }
    usleep(1);

    return ctx;
    
}

struct http_async_client_uninit(struct async_context *ctx) {

    pthread_cancel(ctx->threadid);
    //pthread_join(ctx->threadid, &ctx);

    close(ctx->epfd);

    free(ctx);

}

//
int http_async_client_commit(struct async_context *ctx, const char *hostname, const char *resource, async_result_cb cb) {

    char buffer[BUFFER_SIZE] = {0};
     
    int len = sprintf(buffer, 
"GET %s %s\r\n\
Host: %s\r\n\
%s\r\n\
\r\n",
         resource, HTTP_VERSION,
         hostname,
         CONNECTION_TYPE
         );
    printf("request buffer:%s\n", buffer);


    char *ip = host_to_ip(hostname);
 
    int sockfd = http_create_socket(ip);
    send(sockfd, buffer, strlen(buffer), 0);


    struct ep_arg *eparg = (struct ep_arg*)malloc(sizeof(struct ep_arg));
    if (!eparg) {
        return NULL;
    }
    struct epoll_event ev;
    ev.data.ptr = eparg;
    ev.events = EPOLLIN;

    epoll_ctl(ctx->epfd, sockfd, EPOLL_CTL_ADD, &ev);

 


    return result;

}

 
int main(int argc, char *argv[]) {

     
    int count = sizeof(reqs) / sizeof(reqs[0]);
    int i = 0;
    for (i = 0;i < count;i ++) {
        http_client_commit(reqs[i].hostname, reqs[i].resource);
    }

 
    return 0;
}

4.异步http代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#include <unistd.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
 
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>       /* close */
#include <netdb.h> 
 
#include <sys/epoll.h>
#include <pthread.h>
 
 
#define HTTP_VERSION    "HTTP/1.1"
#define USER_AGENT      "User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:10.0.2) Gecko/20100101 Firefox/10.0.2\r\n"
#define ENCODE_TYPE     "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
#define CONNECTION_TYPE "Connection: close\r\n"
 
 
 
#define BUFFER_SIZE     4096
 
//传入网络地址转成字符串传出
char *host_to_ip(const char *hostname) {
    //gethostbyname线程不安全,也就是说有全局变量
    //获取主机的地址,现在是有新的函数,这个函数已经过期了
    struct hostent *host_entry = gethostbyname(hostname);//gethostbyname_r线程安全,但只解决ipv4,ipv6还得靠getaddrinfo
    if (host_entry) {
        //将网络地址换成字符串的格式
        return inet_ntoa(*(struct in_addr*)*host_entry->h_addr_list);
    } else {
        return NULL;
    }
}
 
int http_create_socket( char *ip) {
 
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
    struct sockaddr_in sin = {0};
    sin.sin_addr.s_addr = inet_addr(ip);
    sin.sin_port = htons(80);
    sin.sin_family = AF_INET;
 
    if (-1 == connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {
        return -1;
    }
 
    fcntl(sockfd, F_SETFL, O_NONBLOCK);
 
    return sockfd;
 
}
 
 
char *http_send_request(int sockfd, const char *hostname, const char *resource) {
 
    char buffer[BUFFER_SIZE] = {0};
     
    int len = sprintf(buffer, 
"GET %s %s\r\n\
Host: %s\r\n\
%s\r\n\
\r\n",
         resource, HTTP_VERSION,
         hostname,
         CONNECTION_TYPE
         );
    printf("==33333333333333333333333==\n");
    printf("request buffer:%s\n", buffer);
    send(sockfd, buffer, strlen(buffer), 0);
    printf("==44444444444444444444444==\n");
 
    struct timeval tv;
    tv.tv_sec = 5;
    tv.tv_usec = 0;
 
    fd_set fdread;
    FD_ZERO(&fdread);
    FD_SET(sockfd, &fdread);
 
    char *result = malloc(sizeof(int));
    result[0] = '\0';
 
    while (1) {
 
        int selection = select(sockfd+1, &fdread, NULL, NULL, &tv);
        if (!selection || !(FD_ISSET(sockfd, &fdread))) {
            break;
        } else {
            len = recv(sockfd, buffer, BUFFER_SIZE, 0);
            if (len == 0) break;
 
            result = realloc(result, (strlen(result) + len + 1) * sizeof(char));
            strncat(result, buffer, len);
        }
    }
 
    return result;
 
}
 
 
int http_client_commit(const char *hostname, const char *resource) {
    char *ip = host_to_ip(hostname);
 
    int sockfd = http_create_socket(ip);
 
    char *content =  http_send_request(sockfd, hostname, resource);
    if (content == NULL) {
        printf("have no data\n");
    }
 
    puts(content);
    close(sockfd);
    free(content);

    return 0;
}
 
 
#define ASYNC_CLIENT_NUM        1024
#define HOSTNAME_LENGTH         128

//回调函数参数,参数一表示是哪个服务器返回的,参数二表示返回的结果是什么
typedef void (*async_result_cb)(const char *hostname, const char *result);
 
 
struct ep_arg {
    int sockfd;                             //
    char hostname[HOSTNAME_LENGTH];         //
    async_result_cb cb;                     //回调函数
};
 
 
struct async_context {
    int epfd;
    pthread_t thread_id;
};
 
struct http_request {
    char *hostname;
    char *resource;
};
 

//提交一次,往红黑树加一个io
int http_async_client_commit(struct async_context *ctx, const char *hostname, const char *resource, async_result_cb cb) {

    //1.获得IP字符串
    char *ip = host_to_ip(hostname);
    if (ip == NULL) return -1;

    //2.创建socket并设置非阻塞
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
    struct sockaddr_in sin = {0};
    sin.sin_addr.s_addr = inet_addr(ip);
    sin.sin_port = htons(80);
    sin.sin_family = AF_INET;
 
    if (-1 == connect(sockfd, (struct sockaddr*)&sin, sizeof(struct sockaddr_in))) {
        return -1;
    }
 
    fcntl(sockfd, F_SETFL, O_NONBLOCK);
 
    char buffer[BUFFER_SIZE] = {0};
     
    int len = sprintf(buffer, 
"GET %s %s\r\n\
Host: %s\r\n\
%s\r\n\
\r\n",
         resource, HTTP_VERSION,
         hostname,
         CONNECTION_TYPE
         );
 
    printf("request buffer:%s\n", buffer);
    int slen = send(sockfd, buffer, strlen(buffer), 0);
 
    //epoll是可以带参数的,传一个结构体参数
    struct ep_arg *eparg = (struct ep_arg*)calloc(1, sizeof(struct ep_arg));
    if (eparg == NULL) return -1;
    eparg->sockfd = sockfd;
    eparg->cb = cb;

    //问题1:epoll为什么是线程安全的?
    //问题2:怎么让服务器知道发给谁?
    //解答①:自定义协议存发送给客户端的相关信息
    //解答②:epoll把参数传过去,epoll存结构体,结构体包括客户端ID及包ID
    struct epoll_event ev;
    ev.data.ptr = eparg;
    ev.events = EPOLLIN;
 
    int ret = epoll_ctl(ctx->epfd, EPOLL_CTL_ADD, sockfd, &ev); 
 
    return ret;
 
}


static void *http_async_client_callback(void *arg) {
 
    struct async_context *ctx = (struct async_context*)arg;
    int epfd = ctx->epfd;
 
    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 ++) {
 
            struct ep_arg *data = (struct ep_arg*)events[i].data.ptr;
            int sockfd = data->sockfd;
             
            char buffer[BUFFER_SIZE] = {0};
            struct sockaddr_in addr;
            size_t addr_len = sizeof(struct sockaddr_in);
            int n = recv(sockfd, buffer, BUFFER_SIZE, 0);
 
            data->cb(data->hostname, buffer); //call cb

            //操作完后把事件清空,删除这个fd
            int ret = epoll_ctl(epfd, EPOLL_CTL_DEL, sockfd, NULL);
            //printf("epoll_ctl DEL --> sockfd:%d\n", sockfd);
 
            close(sockfd); /

            //避免内存泄漏
            free(data);
 
        }
         
    }
 
}

//初始化环境
struct async_context *http_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;
 
    int ret = pthread_create(&ctx->thread_id, NULL, http_async_client_callback, ctx);//创建子线程
    if (ret) {
        perror("pthread_create");
        return NULL;
    }
    usleep(1);  //加usleep,允许子线程先行,主线程就会让出CPU
 
    return ctx;
 
}
 
int http_async_client_uninit(struct async_context *ctx) {
 
    close(ctx->epfd);
    pthread_cancel(ctx->thread_id);//不用pthread_join是因为这个函数是等待自线程返回的,这里不需要返回子线程的结果

    return 0;
}
 
struct http_request reqs[] = {
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=beijing&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=changsha&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=shenzhen&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=shanghai&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=tianjin&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=wuhan&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=hefei&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=hangzhou&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=nanjing&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=jinan&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=taiyuan&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=wuxi&language=zh-Hans&unit=c" },
    {"api.seniverse.com", "/v3/weather/now.json?key=0pyd8z7jouficcil&location=suzhou&language=zh-Hans&unit=c" },
};
 
 
static void http_async_client_result_callback(const char *hostname, const char *result) {
     
    printf("hostname:%s, result:%s\n\n\n\n", hostname, result);

}
 
 
 
int main(int argc, char *argv[]) {

//同步
#if 0
     
    int count = sizeof(reqs) / sizeof(reqs[0]);
    int i = 0;
    for (i = 0;i < count;i ++) {
        http_client_commit(reqs[i].hostname, reqs[i].resource);
    }
     
#else
//异步
    //1.初始化环境
    struct async_context *ctx = http_async_client_init();
    if (ctx == NULL) return -1;

    //2.提交请求
    int count = sizeof(reqs) / sizeof(reqs[0]);
    int i = 0;
    for (i = 0;i < count;i ++) {
        http_async_client_commit(ctx, reqs[i].hostname, reqs[i].resource, http_async_client_result_callback);
    }
 
 
    getchar();
#endif
 
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值