HTTP/HTTPS服务器

article/2025/9/18 10:00:52

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			1typedef 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;//long last_active;};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;//ev->last_active = time(NULL);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;}#endifreturn 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解析出来的数据进行拼接,然后再将拼接好的数据会发给浏览器。


http://chatgpt.dhexx.cn/article/778coBgI.shtml

相关文章

HTTPS服务器

目录 HTTPS简介 https概念 SSL/TLS 加/解密方式 对称加密 非对称加密 消息摘要 数字签名 数字证书 http与https的区别 作业需求 ​编辑 HTTPS简介 https概念 HTTPS &#xff08;全称&#xff1a;Hyper Text Transfer Protocol over SecureSocket Layer&#xff0…

HTTP服务器搭建

文章目录 实验环境说明服务器配置下载安装HTTP服务修改配置文件 客户端测试Linux客户端测试Windows客户端测试 实验环境说明 主机系统系统版本IP地址服务端LinuxRHEL 7.0192.168.43.128/24客户端LinuxRHEL 7.4192.168.43.15/24客户端Windows 11Windows 11192.168.43.1/24 基本环…

WEB/HTTP服务器搭建

HTTP 对于软件都有服务和客户&#xff0c;有服务端和客户端 服务 就是在操作系统运行一个或者多个程序&#xff0c;并为客户端提供相应所需的服务 协议 就是计算机网络中进行数据交换而建立的规则、标准或约定的集合。只有遵守这个约定&#xff0c;计算机之间才能相互通信…

ESP32基础应用之HTTP 服务器

文章目录 1 HTTP服务器简介2 ApiPost测试工具3 HTTP服务器实验3.1 ApiPost之GET测试3.2 ApiPost之POST测试3.3 ApiPost值PUT测试 参考资料&#xff1a; esp32 http服务器编程指南 1 HTTP服务器简介 HTTP服务器一般指Web服务器&#xff0c;是指驻留于因特网上某种类型计算机的…

http 服务器搭建

目录 http 服务器搭建有很多条数据时用response.write测试服务器是否开启 http 服务器搭建 通过http模块提供的方法可以创建服务器 1 引入http模块 &#xff08;nodejs内置模块&#xff0c; 可以直接引入&#xff09; const http require(http);创建服务器http.createServer…

HTTP服务器(一)HTTP服务器入门介绍

一、简介 1.1、做项目的时候&#xff0c;要请求REST服务器。而FEST服务器还是用http基本原理(即阉割版)来请求。 1.2、请求端口EndPoint/请求方法Method/请求内容格式ContentType/请求的数类型PostData(一般为JSon格式)。 1.3、我们熟悉的HTTP、FTP、Telnet等协议都是建立在…

【八】http服务器开发--实现一个http服务器

文章目录 一、整体概述二、接收http请求三、响应http请求四、完整代码即效果4.1 完整代码&#xff1a;4.2 实现过程 附&#xff08;stat函数&#xff09; 一、整体概述 本节主要实现一个http服务器的示例&#xff1b;该http服务器的主要功能是&#xff0c;在浏览器端访问服务器…

蓝桥杯c语言基础试题答案,试题 基础练习 Huffuman树 蓝桥杯 C语言

资源限制 时间限制&#xff1a;1.0s 内存限制&#xff1a;512.0MB 问题描述 Huffman树在编码中有着广泛的应用。在这里&#xff0c;我们只关心Huffman树的构造过程。 给出一列数{pi}{p0, p1, …, pn-1}&#xff0c;用这列数构造Huffman树的过程如下&#xff1a; 1. 找到{pi}中最…

蓝桥杯基础试题 字母图形 C语言实现

资源限制 时间限制&#xff1a;1.0s 内存限制&#xff1a;256.0MB 问题描述 利用字母可以组成一些美丽的图形&#xff0c;下面给出了一个例子&#xff1a; ABCDEFG BABCDEF CBABCDE DCBABCD EDCBABC 这是一个5行7列的图形&#xff0c;请找出这个图形的规律&#xff0c…

蓝桥杯c语言b组试题及答案,2014蓝桥杯C语言本科B组预赛试题

2014蓝桥杯C语言本科B组预赛试题 2014 蓝桥杯 C/C语言本科 B 组预赛试题12014 蓝桥杯 C/C语言本科 B 组预赛试题(完整)1.啤酒和饮料啤酒每罐 2.3 元&#xff0c;饮料每罐 1.9 元。小明买了若干啤酒和饮料&#xff0c;一共花了 82.3 元。我们还知道他买的啤酒比饮料的数量少&am…

2018年蓝桥杯C语言B组

第一题 标题&#xff1a;第几天 2000年的1月1日&#xff0c;是那一年的第1天。 那么&#xff0c;2000年的5月4日&#xff0c;是那一年的第几天&#xff1f; 注意&#xff1a;需要提交的是一个整数&#xff0c;不要填写任何多余内容 第二题 标题&#xff1a;明码 汉字的字…

(求救大佬)蓝桥杯C语言试题 算法训练 C++ CH08 01

题目&#xff1a; 求解我的代码有什么问题&#xff0c;为什么不能通过蓝桥的编译&#xff0c;但运行结果都正确。 我的代码&#xff1a; #include<stdio.h> int main() { double ssh,zrf,y,x,g,f; int a,b,c,d,t,x1,x2,x3,x4,q; int i,j; scanf("%d%d%d%d",&a…

蓝桥杯训练题C语言

开学后就很少抽空写写题了&#xff0c;所以这次只有几道题目和大家分享一下了。 有些真题也有些VIP基础题&#xff0c;想和大家分享一下。&#xff08;有些是仿的其他博主的代码&#xff0c;&#xff0c;&#xff0c;&#xff0c;因为有些确实难&#xff08;呜呜呜~&#xff0…

蓝桥杯研究生c语言试题答案,蓝桥杯试题C语言答案.doc

蓝桥杯试题C语言答案.doc 下载提示(请认真阅读)1.请仔细阅读文档&#xff0c;确保文档完整性&#xff0c;对于不预览、不比对内容而直接下载带来的问题本站不予受理。 2.下载的文档&#xff0c;不会出现我们的网址水印。 3、该文档所得收入(下载内容预览)归上传者、原创作者&am…

蓝桥杯 C语言 试题 历届试题 格子刷油漆

试题 历届试题 格子刷油漆 问题描述   X国的一段古城墙的顶端可以看成 2*N个格子组成的矩形&#xff08;如下图所示&#xff09;&#xff0c;现需要把这些格子刷上保护漆。 你可以从任意一个格子刷起&#xff0c;刷完一格&#xff0c;可以移动到和它相邻的格子&#xff08;…

蓝桥杯 C语言 试题 历届试题 网络寻路

试题 历届试题 网络寻路 问题描述 X 国的一个网络使用若干条线路连接若干个节点。节点间的通信是双向的。某重要数据包&#xff0c;为了安全起见&#xff0c;必须恰好被转发两次到达目的地。该包可能在任意一个节点产生&#xff0c;我们需要知道该网络中一共有多少种不同的转发…

蓝桥杯 C语言 试题 算法训练 审美课

试题 算法训练 审美课 问题描述   《审美的历程》课上有n位学生&#xff0c;帅老师展示了m幅画&#xff0c;其中有些是梵高的作品&#xff0c;另外的都出自五岁小朋友之手。老师请同学们分辨哪些画的作者是梵高&#xff0c;但是老师自己并没有答案&#xff0c;因为这些画看上…

C语言课程设计——25道蓝桥杯练习题

文章目录 一、基础练习1.fib数列题目解题思路解题代码解法一(简单递推)&#xff1a;时间复杂度O(n)解法二(矩阵快速幂)&#xff1a;时间复杂度O(logn) 2.闰年判断题目解题思路解题代码 3. 数列特征题目解题思路解题代码 4.查找整数题目解题思路解题代码解法一&#xff1a;C风格…

蓝桥杯C语言程序设计真题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、真题总结 前言 下面是蓝桥杯历年的真题希望对大家有用 一、真题 1&#xff0c;隔行变色 Excel表的格子很多&#xff0c;为了避免把某行的数据和相邻行混…

2022年第十三届蓝桥杯大赛C组真题C/C++解析(上)

**今天给大家带来2022年&#xff0c;第十三届蓝桥杯大赛的真题解析**转眼间&#xff0c;距离考试已经过去很长时间了&#xff0c;今天解元给大家解析一下&#xff0c;有问题欢迎大家指点 :笑: 下面进入正题 前言填空题1.排列字母2.特殊时间 编程题1.纸张尺寸1.1纸张大小代码 2.…