B/S模式的web通信(高并发服务器)
这里写目录标题
- 目标
- 实现的目标
- 服务器代码(采用epoll实现服务器)
- 整体框架
- main函数
- init_listen_fd函数(负责对lfd初始化的那一系列操作)
- epoll_run函数
- do_accept函数
- do_read函数
- 内容
- 补充:http中的getline函数
- 详解do_read函数(借助http协议进行通信的具体数据读写过程)
- 首先实现单文件请求
- 思路
- 代码实现
- 补充:正则表达式(简要介绍)
- 补充:上述代码中用到的函数的原型
- strncasecmp
- stat
目标
实现的目标

我们要实现,在服务器上启动服务之后,通过浏览器访问Ip+port,可以访问到服务器某个目录的文件
当然也可以使用域名访问,域名就是对IP+port进行了包装,域名需要申请
服务器代码(采用epoll实现服务器)
整体框架


main函数

首先,为了更加灵活的进行服务端的启动,我们在main函数中,定义两个参数,用于进行启动参数的配置和读取
首先,判断argv是否读到了三个字符串,如果小于三个字符串,则启动时没有按照规定进行启动参数设置,提示启输入./server 端口 以及要发布的文件所在路径
之后,通过argv[2],拿到输入的第二个字符串,即端口,将其atoi,转为字面值一样的int型,就拿到了开放的端口
之后,因为http协议中,对于从浏览器发送而来的文件的位置,是以启动配置的第三个参数为参考根目录的相对路径,服务器要设法拿到服务器对应的路径,而拼接路径又过于繁琐,所以,使用chdir函数,该函数可以让服务端的工作目录跳转到某个目录下(实际上就是与cd的作用一样),所以,chdir(argv[2]),就是将服务器跳转到第三个参数所指明的目录内,这样,从http协议封装出来的数据包拿到的数据,可以直接拿到当前服务器使用,因为服务器的工作目录已经跳转到第三个参数的目录了
之后使用封装的函数,根据传来的端口,启动epoll监听
init_listen_fd函数(负责对lfd初始化的那一系列操作)


即,根据传入进来的port(端口)、epfd(epoll树根)等参数,进行lfd的创建以及上树
包括,创建socket、创建服务器地址结构、设置端口复用、给lfd绑定地址、设置监听上限、将lfd上树,预监听其读事件。
最后返回lfd
epoll_run函数

根据传入的port,首先,创建一个epoll_event数组,用于后续的epoll_wait监听时返回套接字数组。创建epoll监听树树根,之后传入init_listen_fd函数,创建监听套接字lfd,并将其上树

在lfd上树之后,我们就可以开始监听了
while循环内,使用epoll_wait开始监听,将监听结果返回到all_events数组。
之后拿到返回值,就是all_events数组的有效元素个数,也是其循环上限,
在循环内,我们默认只处理服务器的读事件(也就是只处理接收请求),其他事件不处理
将其元素挨个取出,转为指针,如果不是事件,直接continue,如果是,进行后续代码:
读事件又分为是lfd收到数据还是其他cfd受到数据:
如果是lfd,进行accept进行新的cfd创建与连接
如果是cfd,那么进行数据的读取
do_accept、do_read函数也是封装的函数
do_accept函数

根据传入进来的lfd,和epoll的树根,进行新的cfd的创建以及相关设置
首先,调用accept函数,进行新的cfd的创建,返回新的cfd,
之后,将其设置为非阻塞
之后设置ET模式,这是经典的ET+非阻塞,是高效模式
最后将其挂上树,进行监听
do_read函数
内容
补充:http中的getline函数

参数:第一个是表示从cfd文件描述符中读,第二个是传出参数,表示读到的数据,第三个是传出参数对应实参的开辟空间大小,使用时,直接传sizeof(第二个实参)
其中,recv的第四个参数如果是0,那么就是真实的从缓冲区拿数据读进来
而如果其第四个参数是MSG_PEEK,那么就是拷贝读取,并不会动缓冲区中的数据
上图中的所有recv,每次都是只读取一个字节
返回值为所读取到的字节数,将数据读进传出参数buf中
详解do_read函数(借助http协议进行通信的具体数据读写过程)
首先实现单文件请求
思路

1、首先使用封住的getline,读收到的http协议的请求行。
2、然后对其进行拆分
3、之后判断文件是否存在,使用stat()函数,如下:

该函数传入文件路径,以及一个传出参数变量,即可通过返回值,判断文件是否存在,而这里传路径时,直接将拆分完的路径传入即可,会因为服务器已经在main函数时被chdir切换为了对应的目录,而浏览器也是以服务器启动时提供的路径为参考,所以,可以直接传入
4、判断传来的路径是文件还是目录(若是文件进行后续操作,若是目录,则将目录信息发送回去,此处不做研究)
5、使用open函数,打开文件,并将其内容读取到
6、首先封装http应答的数据包的协议头:
主要包含两部分,一个是第一行的版本号、状态码、状态描述
另一个是附加信息中的“文件类型”
7、将数据封装到协议头之后,最终生成http应答数据包,将其发送回浏览器
代码实现

更正:145行,sizeof内应该是line,而不是buf
因为我们会频繁用到将某个cfd,关闭,且下树的操作,所以,可以将该操作封装为一个函数,如上图中的disconnect函数
之后,对do_read进行实现,首先,创建并初始化一个字符数组
之后,get_line读取数据,传入“传出参数”line,拿到数据
之后,对返回值进行判断,如果返回值为0,表示是对端关闭,所以,我们也进行disconnect操作
而如果不是0,则要进行数据的拆分了,现在在line数组中的是“GET ./hello.c HTTP/1.1”,即是由三个字符串组成的字符数组,接下来是拆分这三个字符串:
使用sscanf函数:

首先定义三个字符数组,分别用于存储拆分后的结果
之后,调用sscanf函数,第一个参数传入要拆分的对象,第二个参数需要传入格式说明符(如%s,%d等),这里我们传入一个正则表达式,大概功能与“%s %s %s”差不多,注意要%s之间是空格,不可忽略


之后:

对于图中的第一部分,作用是:读取第一行之后的附加内容,不让其存留于缓冲区中,防止其影响下一次的读取。(因为我们这里心知肚明收到的肯定是GET请求,所以,并没有判断是否读到了附加内容结束,因为对于不是GET的请求,将附加内容读掉之后,接下来的“数据正文”是不可以直接丢掉的,应该存起来留用)
图中,如果len==‘\n’,本来是想表示读到了回车,也就是空行的下一行会自带换行回车,本意是想判断是否读完了整个附加数据(即读到了空行的下一行),但是这里的判断操作有点迷,到时候可视具体情况来定
对于第二部分,是将读到的第一个字符串与“GET”对比,查看是否相同,即查看第一个字符串是否是GET,
在linux中:
使用strncasecmp,该方法是将第一个参数与第二个参数相比看是否相同,且忽略大小写,且可以通过第三个参数指定比较前多少个字符
而strcasecmp,没有n,该方法也是比较第一个参数与第二个参数是否相同,忽略大小写,但是无法指定比较前多少个字符
(若此时用的是string,那么可以使用c_str()方法,将其转为char指针类型,再进行函数比较,或者将string每个字符统一转为大写,再与“GET”比较)
win中忽略大小写的字符指针之间的比较的函数是:stricmp()
上图中,比较读到的第一个字符串,与“GET”,且只比较前3个字符,这里是担心因为’\0’而误判,实际上这两个都被补了‘\0’,但是只比较前三个会无需考虑是否有’\0’。
而如果确实是GET的话,返回值为0,如果返回0,那么就要进行相关GET的操作了,字符指针=path+1,因为path第一个字符是‘/’,他并不是文件名的一部分,所以,跳过,直接+1,从第二个字符读取
然后封装一个函数,将文件名传入,进行相关的处理
之后对于http_request()函数:

我们已经确定了请求是GET,接下来就是对GET做相关的操作,对于GET请求,我们要判断文件是否存在,
判断文件是否存在,使用stat方法,使用方法如上图,先创建一个结构体,struct stat类型的结构体,之后,调用stat方法,第一个参数:文件名,第二个参数:结构体地址(该函数使用时要保证服务器当前工作目录切换到了要搜索文件所在的目录)
如果返回值不是0,那么代表文件不存在,这时要回发给浏览器404错误页面,而服务器的代码可以有一个perror,来进行异常显示,至于要不要exit退出服务器,并不一定,根据具体需求。
前面都没有问题的话,说明文件存在,这里,就要判断其是文件还是文件夹,使用stat自带的宏来判断:
S_ISREG(结构体变量.st_mode),如果为真,则是文件,如果S_ISDIR(结构体变量.st_mode)为真,则是文件夹(或者说是文件目录)
如果是文件,那么接下来的操作就是
1、回发http应答
2、回发请求的数据内容
对于我们封装的回发函数(send_respond):

这里我们应该清楚,所谓协议,实际上就是一堆字符串,他按照一定的格式,将有效数据糅合进一堆字符串中,所以,我们回发时,也是,主要进行字符串的拼接操作
首先,对于参数,我们需要:与浏览器通信的cfd(因为我们服务器还是使用socket的epoll服务器),状态码(用no表示),状态码描述(disp指针接收),文件类型(即附加内容中的第四行,这是必不可少的),文件长度
在调用时,将参数传入(这里注意,此前http_request并没有接收cfd,这里在此处更改,写代码的时候注意)
之后,在send_respond中:
首先定义一个字符串buf,初始化为0
之后使用sprintf,将字符串拼接到buf中,使用方法见上图。
而想要再次拼接,且在buf原有的内容后面追加的话,继续调用sprintf,但是第一个参数传buf+strlen(buf)
注意,在http中的换行是\r\n
将所有内容使用send发过去之后,还要将第9行的/r/n发回去
至此,GET的协议应答就发送完了
而对于,buf+srtlen(buf)的可行性:

strlen会计算字符串的字符个数,且不计算最后的\0,所以,不管最后有没有\0,buf+strlen(buf),都会来到有效数据的下一个位置,
如果有\0,那么就覆盖其\0,毕竟我们都想要追加内容,\0作为结束符,自然要被覆盖,且必须被覆盖,
如果没有,自然在后面接着写数据即可
发送完了协议应答,就要发送下面的数据正文了:

打开指定的本地文件,用fd接收(注意这里的fd是代表本地文件,而之前的cfd是通信文件描述符,即套接字,二者不要混淆),之后使用read从打开的本地文件中读数据给buf,然后再使用send将buf中的数据写给cfd
补充:正则表达式(简要介绍)
正则表达式,就是专门对字符串进行操作的,他可以表示出你对字符串的任何需求

其中,刚刚的[ ^空格],是匹配除空格之外的任意字符
更多请查看下列网站:

补充:上述代码中用到的函数的原型
strncasecmp

stat

下面是使用stat的宏来判断“使用stat搜索的文件”是文件还是文件夹:

相关文章:
B/S模式的web通信(高并发服务器)
这里写目录标题 目标实现的目标 服务器代码(采用epoll实现服务器)整体框架main函数init_listen_fd函数(负责对lfd初始化的那一系列操作)epoll_run函数do_accept函数do_read函数内容补充:http中的getline函数 详解do_re…...
C语言每日一题—约瑟夫问题
13个人围成一圈,从第1个人开始顺序报号1、2、3,凡报到3的人退出圈子。找出最后留在圈子里的人原来的序号。要求用结构体编程实现。***输出提示:"\n出圈成员及顺序:" ***输出格式:"%3d" ***输出提示…...
语言:C#
一、VSCode生成exe 二、...
[力扣题解]45. 跳跃游戏 II
题目:45. 跳跃游戏 II 思路 贪心法; 只需记录2个变量,当前点能达到的最远距离,和上一步能到达的最远距离; (真有意思,代码随想录给出的是curDistance,nextDistance2个,…...
pywinauto操作windows应用(未完成)
pywinauto 脚本制作 一 、获取窗口句柄 首先获取句柄,其次扫描组件,然后对按钮和文本进行操作 安装依赖 pip install pywin32 -i https://pypi.doubanio.com/simple扫描全部的句柄 import win32gui# GetDesktopWindow 获得代表整个屏幕的一个窗口&a…...
(超详细讲解)实现将idea的java程序打包成exe (新版,可以在没有java的电脑下运行,即可以发给好朋友一起玩)
目录 实现打包到exe大概步骤 工具准备 1.将java程序文件打包成jar文件 2.准备好jre文件 3.使用exe4j软件打包好 4.最终打包 实现打包到exe大概步骤 1.打包需要满足的条件:将java文件转成jar文件的工具exe4j、 以及需要满足jdk1.8以上(因安装exe4…...
学习软考----数据库系统工程师29
数据操作 SELECT基本结构 简单查询 连接查询 子查询 聚集函数 分组查询 字符串操作 集合操作 外连接 INSERT INTO语句 DELETE语句 UPDATE语句...
STL中的优先级队列
目录 1.引言 2.简介 3.基本操作 4.实现原理 5.自定义优先级比较 6.相关题目 7.能特点 8.总结 1.引言 在C标准库中,优先级队列是一种非常有用的数据结构,它允许我们根据元素的优先级来对其进行排序和访问。这种数据结构在多种应用场景中都发挥着重…...
浅谈Acrel-2000ES储能能量管理系统的设计与应用-安科瑞 蒋静
0 前言 为进一步提升河南省分布式光伏发电发展水平,促进行业健康可持续发展,河南省发布关于促进分布式光伏发电健康可持续发展的通知。对于储能行业,可以用到安科瑞Acrel-2000ES储能能量管理系统。 储能柜EMS能量管理系统 1、产品名称 储…...
会员卡积分小程序系统源码商业运营版 行业一站式解决方案附带源代码以及搭建安装部署教程
系统概述 会员卡积分小程序系统源码商业运营版是一套完整的会员卡积分系统解决方案,包含前端小程序、后端管理系统以及数据库设计。该系统支持多种会员卡类型、积分规则设定、积分兑换、优惠券发放等功能,满足企业对于会员积分管理的各种需求。同时&…...
uniapp 百度地图 拖动获取经纬度级搜索连用
import loadBMap from /utils/loadBMap.js// 百度聚合具体代码 // 拖动 initMapc() {let that thisloadBMap(百度key).then(() > {map new BMap.Map(mapContainer)const centerPoint new BMap.Point(this.longitude, this.latitude)map.centerAndZoom(centerPoint, this.…...
Yarn的安装和使用详细教程(Mac/Window)
目录 Yarn是什么? Mac安装Yarn 使用Homebrew安装Yarn 使用npm安装Yarn Windows安装Yarn 使用npm安装Yarn Yarn使用 常用命令: 特殊命令: Yarn是什么? Yarn是一个流行的包管理工具,用于管理JavaScript项目的依…...
高考志愿系统-学生管理模块分析
1.获取学生信息: 接口:http://localhost:81/dev-api/college_entrance/student/list?pageNum1&pageSize10 请求方式get 默认传参pageNum和pageSize,表示当前页,每页展示数量 首先通过startPage()方法获取分页参数当前页&…...
【问题实操】银河高级服务器操作系统实例分享,开机之后反复重启
1.服务器环境以及配置 物理机/虚拟机/云/容器 物理机 外网/私有网络/无网络 私有网络 处理器: PHYTIUM FT2000PLUS 2200 MHz 内存: 128 GiB 整机类型/架构: HIKVISION DS-V BIOS版本: HK 601FBE02HK 网卡࿱…...
攻防世界-web-unseping
题目 知识点 PHP代码审计PHP序列化和反序列化PHP中魔术方法命令执行绕过方式 解读源码 <?php highlight_file(__FILE__);class ease{private $method;private $args;function __construct($method, $args) {$this->method $method;$this->args $args;}function …...
网络网络层之(4)IPv4协议
网络网络层之(1)IPv4协议 Author: Once Day Date: 2024年4月4日 一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦… 漫漫长路,有人对你微笑过嘛… 全系列文档可参考专栏:通信网络技术_Once-Day的…...
16-LINUX--线程安全
一。线程安全 线程安全即就是在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是 一样的、正确的。那么就说这些线程是安全的。 要保证线程安全需要做到: 1) 对线程同步,保证同一时刻只有一个线程访问临界资…...
Flask SQLAlchemy 技术指南
文章目录 什么是 Flask SQLAlchemy?安装 Flask SQLAlchemy创建 Flask 应用和数据库模型添加和查询数据运行 Flask 应用总结**数据库迁移(Database Migrations)****复杂查询****关系模型****事务处理****性能优化****安全性****扩展功能** Fla…...
js通过时间对JSON中的数据进行排序
需求 现在需要通过每一个数据段的date字段对数组的整体数据进行排序! 元数据如下: var data [{"filename": "123","date": "2024-05-10 19:53:57","stand": "GB-14","filter":…...
leetcode206-Reverse Linked List
题目 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1] 分析 用一个指针记录当前位置,另外一个指针记录当前位置的前一个位置,…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...
C/C++ 中附加包含目录、附加库目录与附加依赖项详解
在 C/C 编程的编译和链接过程中,附加包含目录、附加库目录和附加依赖项是三个至关重要的设置,它们相互配合,确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中,这些概念容易让人混淆,但深入理解它们的作用和联…...
Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...
nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
基于鸿蒙(HarmonyOS5)的打车小程序
1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...
