HTTP/HTTPS
一、消息传播过程
1、发送消息的时候做的事情。 比如说客户端发送给的消息是:Hello World 首先是在客户端这里,数据会经过一系列处理从应用层传输到数据链路层。也就对应着调用send,将数据拷贝到内核,内核再协议栈,然后协议栈将数据通过网卡 --> 网络发送出去,服务端接收数据最后将数据拿到应用层,这时候服务端会最三件事,读数据、解析数据和判断是否关注下一个事件,其中在解析数据的时候还会判断是否读完数据。
2、这时候去运行我们写的http代码会出现问题。 代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define BUFFER_LENGTH 1024
#define MAX_EPOLL_EVENTS 1024
#define SERVER_PORT 8888
#define PORT_COUNT 1
typedef int NCALLBACK(int ,int, void*);
struct ntyevent {
int fd;
int events;
void *arg;
int (*callback)(int fd, int events, void *arg);
int status;
char buffer[BUFFER_LENGTH];
int length;
};
struct eventblock {
struct eventblock *next;
struct ntyevent *events;
};
struct ntyreactor {
int epfd;
int blkcnt;
struct eventblock *evblks;
};
int recv_cb(int fd, int events, void *arg);
int send_cb(int fd, int events, void *arg);
struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd);
void nty_event_set(struct ntyevent *ev, int fd, NCALLBACK callback, void *arg) {
ev->fd = fd;
ev->callback = callback;
ev->events = 0;
ev->arg = arg;
return ;
}
int nty_event_add(int epfd, int events, struct ntyevent *ev) {
struct epoll_event ep_ev = {0, {0}};
ep_ev.data.ptr = ev;
ep_ev.events = ev->events = events;
int op;
if (ev->status == 1) {
op = EPOLL_CTL_MOD;
} else {
op = EPOLL_CTL_ADD;
ev->status = 1;
}
if (epoll_ctl(epfd, op, ev->fd, &ep_ev) < 0) {
printf("event add failed [fd=%d], events[%d]\n", ev->fd, events);
return -1;
}
return 0;
}
int nty_event_del(int epfd, struct ntyevent *ev) {
struct epoll_event ep_ev = {0, {0}};
if (ev->status != 1) {
return -1;
}
ep_ev.data.ptr = ev;
ev->status = 0;
epoll_ctl(epfd, EPOLL_CTL_DEL, ev->fd, &ep_ev);
return 0;
}
int recv_cb(int fd, int events, void *arg) {
struct ntyreactor *reactor = (struct ntyreactor*)arg;
struct ntyevent *ev = ntyreactor_idx(reactor, fd);
if (ev == NULL) return -1;
int len = recv(fd, ev->buffer, BUFFER_LENGTH, 0);
nty_event_del(reactor->epfd, ev);
if (len > 0) {
ev->length = len;
ev->buffer[len] = '\0';
printf("recv [%d]:%s\n", fd, ev->buffer);
nty_event_set(ev, fd, send_cb, reactor);
nty_event_add(reactor->epfd, EPOLLOUT, ev);
} else if (len == 0) {
nty_event_del(reactor->epfd, ev);
printf("recv_cb --> disconnect\n");
close(ev->fd);
} else {
if (errno == EAGAIN && errno == EWOULDBLOCK) {
} else if (errno == ECONNRESET){
nty_event_del(reactor->epfd, ev);
close(ev->fd);
}
printf("recv[fd=%d] error[%d]:%s\n", fd, errno, strerror(errno));
}
return len;
}
int send_cb(int fd, int events, void *arg) {
struct ntyreactor *reactor = (struct ntyreactor*)arg;
struct ntyevent *ev = ntyreactor_idx(reactor, fd);
if (ev == NULL) return -1;
int len = send(fd, ev->buffer, ev->length, 0);
if (len > 0) {
printf("send[fd=%d], [%d]%s\n", fd, len, ev->buffer);
nty_event_del(reactor->epfd, ev);
nty_event_set(ev, fd, recv_cb, reactor);
nty_event_add(reactor->epfd, EPOLLIN, ev);
} else {
nty_event_del(reactor->epfd, ev);
close(ev->fd);
printf("send[fd=%d] error %s\n", fd, strerror(errno));
}
return len;
}
int accept_cb(int fd, int events, void *arg) {
struct ntyreactor *reactor = (struct ntyreactor*)arg;
if (reactor == NULL) return -1;
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
int clientfd;
if ((clientfd = accept(fd, (struct sockaddr*)&client_addr, &len)) == -1) {
if (errno != EAGAIN && errno != EINTR) {
}
printf("accept: %s\n", strerror(errno));
return -1;
}
int flag = 0;
if ((flag = fcntl(clientfd, F_SETFL, O_NONBLOCK)) < 0) {
printf("%s: fcntl nonblocking failed, %d\n", __func__, MAX_EPOLL_EVENTS);
return -1;
}
struct ntyevent *event = ntyreactor_idx(reactor, clientfd);
if (event == NULL) return -1;
nty_event_set(event, clientfd, recv_cb, reactor);
nty_event_add(reactor->epfd, EPOLLIN, event);
printf("new connect [%s:%d], pos[%d]\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), clientfd);
return 0;
}
int init_sock(short port) {
int fd = socket(AF_INET, SOCK_STREAM, 0);
fcntl(fd, F_SETFL, O_NONBLOCK);
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(port);
bind(fd, (struct sockaddr*)&server_addr, sizeof(server_addr));
if (listen(fd, 20) < 0) {
printf("listen failed : %s\n", strerror(errno));
return -1;
}
printf("listen server port : %d\n", port);
return fd;
}
int ntyreactor_alloc(struct ntyreactor *reactor) {
if (reactor == NULL) return -1;
if (reactor->evblks == NULL) return -1;
struct eventblock *blk = reactor->evblks;
while (blk->next != NULL) {
blk = blk->next;
}
struct ntyevent* evs = (struct ntyevent*)malloc((MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
if (evs == NULL) {
printf("ntyreactor_alloc ntyevent failed\n");
return -2;
}
memset(evs, 0, (MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
struct eventblock *block = malloc(sizeof(struct eventblock));
if (block == NULL) {
printf("ntyreactor_alloc eventblock failed\n");
return -3;
}
block->events = evs;
block->next = NULL;
blk->next = block;
reactor->blkcnt ++;
return 0;
}
struct ntyevent *ntyreactor_idx(struct ntyreactor *reactor, int sockfd) {
if (reactor == NULL) return NULL;
if (reactor->evblks == NULL) return NULL;
int blkidx = sockfd / MAX_EPOLL_EVENTS;
while (blkidx >= reactor->blkcnt) {
ntyreactor_alloc(reactor);
}
int i = 0;
struct eventblock *blk = reactor->evblks;
while (i++ != blkidx && blk != NULL) {
blk = blk->next;
}
return &blk->events[sockfd % MAX_EPOLL_EVENTS];
}
int ntyreactor_init(struct ntyreactor *reactor) {
if (reactor == NULL) return -1;
memset(reactor, 0, sizeof(struct ntyreactor));
reactor->epfd = epoll_create(1);
if (reactor->epfd <= 0) {
printf("create epfd in %s err %s\n", __func__, strerror(errno));
return -2;
}
struct ntyevent* evs = (struct ntyevent*)malloc((MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
if (evs == NULL) {
printf("create epfd in %s err %s\n", __func__, strerror(errno));
close(reactor->epfd);
return -3;
}
memset(evs, 0, (MAX_EPOLL_EVENTS) * sizeof(struct ntyevent));
struct eventblock *block = malloc(sizeof(struct eventblock));
if (block == NULL) {
free(evs);
close(reactor->epfd);
return -3;
}
block->events = evs;
block->next = NULL;
reactor->evblks = block;
reactor->blkcnt = 1;
return 0;
}
int ntyreactor_destory(struct ntyreactor *reactor) {
close(reactor->epfd);
struct eventblock *blk = reactor->evblks;
struct eventblock *blk_next;
while (blk != NULL) {
blk_next = blk->next;
free(blk->events);
free(blk);
blk = blk_next;
}
return 0;
}
int ntyreactor_addlistener(struct ntyreactor *reactor, int sockfd, NCALLBACK *acceptor) {
if (reactor == NULL) return -1;
if (reactor->evblks == NULL) return -1;
struct ntyevent *event = ntyreactor_idx(reactor, sockfd);
if (event == NULL) return -1;
nty_event_set(event, sockfd, acceptor, reactor);
nty_event_add(reactor->epfd, EPOLLIN, event);
return 0;
}
int ntyreactor_run(struct ntyreactor *reactor) {
if (reactor == NULL) return -1;
if (reactor->epfd < 0) return -1;
if (reactor->evblks == NULL) return -1;
struct epoll_event events[MAX_EPOLL_EVENTS+1];
int checkpos = 0, i;
while (1) {
int nready = epoll_wait(reactor->epfd, events, MAX_EPOLL_EVENTS, 1000);
if (nready < 0) {
printf("epoll_wait error, exit\n");
continue;
}
for (i = 0;i < nready;i ++) {
struct ntyevent *ev = (struct ntyevent*)events[i].data.ptr;
if ((events[i].events & EPOLLIN) && (ev->events & EPOLLIN)) {
ev->callback(ev->fd, events[i].events, ev->arg);
}
if ((events[i].events & EPOLLOUT) && (ev->events & EPOLLOUT)) {
ev->callback(ev->fd, events[i].events, ev->arg);
}
}
}
}
int main(int argc, char *argv[]) {
struct ntyreactor *reactor = (struct ntyreactor*)malloc(sizeof(struct ntyreactor));
ntyreactor_init(reactor);
unsigned short port = SERVER_PORT;
if (argc == 2) {
port = atoi(argv[1]);
}
int i = 0;
int sockfds[PORT_COUNT] = {0};
for (i = 0;i < PORT_COUNT;i ++) {
sockfds[i] = init_sock(port+i);
ntyreactor_addlistener(reactor, sockfds[i], accept_cb);
}
ntyreactor_run(reactor);
ntyreactor_destory(reactor);
for (i = 0;i < PORT_COUNT;i ++) {
close(sockfds[i]);
}
free(reactor);
return 0;
}
原因就是我们在服务端接收到了连接请求,但是我们并没有在服务器进行处理。
GET / HTTP/1.1 Host: 192.168.43.195:8888 Connection: keep-alive Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9
上面这段是浏览器发送请求之后客户端第一次接收到的东西,这个东西是http报文,我们主要关注的地方在第一行数据,第一行数据标明的信息分别是GET请求、请求的资源目录和http的版本。HTTP的请求分两种,还有一种是post请求,这两种方法都属于TCP链接,因此本质上并没有什么区别,两者的差别主要体现在处理不同浏览器提出的http请求上。
首先对于GET请求,请求的参数是放在URL中,虽然理论上可以在url中无限添加参数,但是数据量太大对浏览器来说是很重的负担,因此,大多数浏览器都会限制url的长度,一般为2K,而服务器处理的长度一般在64K。因此,对于超出的数据,有些服务器会拷贝下来,有的则直接忽略。POST请求是将参数和数据放到requested body中的,这样就对数据进行了封装作用,也就是对用户不可见,因此GEt的安全性低,post的高。
还有就是GET是发送一个TCP包,http header和data放在一起,一次发送,等待服务器响应,然后返回数据;POST是将http header和data 分开发送的,也就是发送两个TCP报文,第一个是http header,就相当于浏览器给服务器打了个招呼说,我要发送数据了,起床准备接收数据了。。。 然后第二次才会把data发送出去,也就是第二个TCP报文。
那现在浏览器为什么不使用GET而使用POST呢,GET一次就可以发送,比POST发送两次的效率肯定高呀?我们考虑问题不能只看一方面,GET确实比POST效率高,但是实际上在网络较好的情况下,两种方法的时间效率差距基本可以忽略不计,而在网络不好的情况下,两次TCP包在检验数据完整性上比一次TCP包有太大优势了,而且考虑到安全性,选择POST都比GET有利多了。
说多了,我们接下来看看在服务端应该怎样处理http请求。
int readline(char* allbuf,int idx,char* linebuf) {
int len = strlen(allbuf);
for (;idx < len; ++idx) {
if(allbuf[idx]=='\r' && allbuf[idx+1]=='\n')
return idx+2;
else
*(linebuf++) = allbuf[idx];
}
return -1;
}
readline函数是对服务器接收到并存储在rbuffer中的数据进行解析,实现原理是一行行读入,因为http数据包每一行都是以’\r\n’结束的,因此,每次读到、他\r\n 时就停止并将读取到的位置索引返回,方便下一次继续读取数据。
int nty_http_request(struct ntyevent *ev) {
char linebuffer[1024] = {0};
int idx = readline(ev->buffer, 0, linebuffer);
if (strstr(linebuffer, "GET")) {
ev->method = HTTP_METHOD_GET;
int i = 0;
while(linebuffer[sizeof("GET ") + i] != ' ') i ++;
linebuffer[sizeof("GET ") + i] = '\0';
sprintf(ev->resource, "%s/%s", HTTP_WEB_ROOT, linebuffer+sizeof("GET "));
} else if (strstr(linebuffer, "POST")) {
ev->method = HTTP_METHOD_POST;
}
}
这个函数的作用是将readline读取到的数据进行解析,并将解析的数据根据GET和POST方法去存储到ntyevent中去,也就是将http请求的http header中的不部分数据存储起来,以供发送数据费浏览器使用。
int nty_http_response_get_method(struct ntyevent *ev) {
int len;
int filefd = open(ev->resource, O_RDONLY);
if (filefd == -1) {
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 78\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n"
"<html><head><title>Test</title></head><body><h1>Hello World</h1><body/></html>");
ev->wlength = len;
} else {
struct stat stat_buf;
fstat(filefd, &stat_buf);
close(filefd);
len = sprintf(ev->wbuffer,
"HTTP/1.1 200 OK\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %ld\r\n"
"Content-Type: text/html\r\n"
"Date: Sat, 06 Aug 2022 13:16:46 GMT\r\n\r\n", stat_buf.st_size);
ev->wlength = len;
}
#endif
return len;
}
这里就是需要去会发给浏览器的数据了。这样就完成了整个流程了。
二、总结
1、http/https协议的格式 2、GET和PSOT请求的区别。
a. GET发送一次TCP包,POST发送两次,GET是将参数当道URL中的,POST放在request body中的。 b. GET请求是不安全的,因为请求的资源是显示在URL中的,POST是安全的,因为不会显示在URL中。 c. GET一次传送数据量不能超过2K,POST理论上没有限制,但不同标准下要求不一样,IIS4中最大为80KB,IIS5最大为100KB。 d. GET请求只能进行url编码,而POST支持多种编码方式。 e. GET请求会被浏览器主动缓存,而POST不会,除非手动设置。GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。 f. get是从服务器上获取数据,post是向服务器传送数据。
3、request和response之间的区别是什么,怎么调用的。
request是请求,是浏览器对服务器的数据请求,服务器需要左的工作就是拿到请求数据对数据进行解析 response是服务器会发数据给浏览器,也就是响应,服务器将根据request解析出来的数据进行拼接,然后再将拼接好的数据会发给浏览器。
|