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
}