WebServer -- 注册登录
目录
🍉整体内容
🌼流程图
🎂载入数据库表
提取用户名和密码
🚩同步线程登录注册
补充解释
代码
😘页面跳转
补充解释
代码
🍉整体内容
概述
TinyWebServer 中,使用数据库连接池实现服务器访问数据库的功能,使用 POST请求 完成 注册和登录的校验工作
内容
本博客介绍同步实现注册登录功能,具体涉及:流程图,载入数据库表,提取用户名和密码,注册登录流程,以及页面跳转的代码实现
- 流程图
服务器从报文中提取用户名密码,接着,完成注册登录校验后,实现页面跳转逻辑- 载入数据库表
将数据库的数据载入服务器- 提取用户名和密码
解析报文,提取用户名和密码- 注册登录流程
描述服务器注册和登录校验的流程- 页面跳转
详解页面跳转机制
🌼流程图
具体地,描述了 GET 和 POST 请求下的页面跳转流程👇
🎂载入数据库表
将数据库的用户名和密码,载入服务器的 map 中,map中,key是用户名,value是密码
// 用户名和密码
map<string, string> users;void http_conn::initmysql_result(connection_pool *connPool)
{// 先从连接池取一个连接MYSQL *mysql = NULL;connectionRAII mysqlcon(&mysql, connPool); // 利用connectionRAII封装的RAII机制获取数据库连接// 在 user 表中检索username, passwd数据,浏览器输入if (mysql_query(mysql, "SELECT username,passwd FROM user")) // 执行查询语句{LOG_ERROR("SELECT error:%s\n", mysql_error(mysql)); // 输出错误信息}// 表中检索完整的结果集MYSQL_RES *result = mysql_store_result(mysql); // 存储查询结果// 返回结果集中的列数int num_fields = mysql_num_fields(result); // 获取结果集中列的数量// 返回所有字段结构的数组MYSQL_FIELD *fields = mysql_fetch_fields(result); // 获取结果集中所有字段的信息// 从结果集获取下一行,将对应用户名和密码,存入 mapwhile (MYSQL_ROW row = mysql_fetch_row(result)) // 迭代每一行数据{string temp1(row[0]); // 提取用户名string temp2(row[1]); // 提取密码users[temp1] = temp2; // 将用户名和密码存入map中}
}
提取用户名和密码
服务器解析浏览器的请求报文,当解析为POST请求时,cgi 标志位设置为1,并将请求报文的消息体赋值给 m_string,进而提取出用户名和密码
// 用户名和密码
map<string, string> users;void http_conn::initmysql_result(connection_pool *connPool)
{// 先从连接池取一个连接MYSQL *mysql = NULL;connectionRAII mysqlcon(&mysql, connPool); // 利用connectionRAII封装的RAII机制获取数据库连接// 在 user 表中检索username, passwd数据,浏览器输入if (mysql_query(mysql, "SELECT username,passwd FROM user")) // 执行查询语句{LOG_ERROR("SELECT error:%s\n", mysql_error(mysql)); // 输出错误信息}// 表中检索完整的结果集MYSQL_RES *result = mysql_store_result(mysql); // 存储查询结果// 返回结果集中的列数int num_fields = mysql_num_fields(result); // 获取结果集中列的数量// 返回所有字段结构的数组MYSQL_FIELD *fields = mysql_fetch_fields(result); // 获取结果集中所有字段的信息// 从结果集获取下一行,将对应用户名和密码,存入 mapwhile (MYSQL_ROW row = mysql_fetch_row(result)) // 迭代每一行数据{string temp1(row[0]); // 提取用户名string temp2(row[1]); // 提取密码users[temp1] = temp2; // 将用户名和密码存入map中}
}
🚩同步线程登录注册
通过 m_url 定位 / 所在位置,根据 / 后第一个字符,判断是登录还是注册校验
- 2
- 登录校验
- 3
- 注册校验
根据校验结果,跳转对应页面;此外,对数据库操作时,需要通过锁来同步
补充解释
首先通过解析URL判断用户是要进行注册还是登录操作,这是通过检查URL中的下一个字符来实现的
如果是注册操作,首先会检查数据库中是否已经存在相同的用户名,如果不存在则向数据库中插入新的用户名和密码,并在map中记录该用户的信息
如果是登录操作,会直接在map中查找用户输入的用户名和密码,如果存在且匹配,则返回欢迎页面,否则返回登录错误页面
无论是注册还是登录,操作完成后都会修改URL,将用户重定向到相应的页面,以提供反馈给用户
std::strrchr - cppreference.com
👆返回字符串中,最后一次出现该字符的位置
std::strcpy - cppreference.com
👆strcpy(dest, src) src 复制到 dest
std::strcat - cppreference.com
👆strcat(dest, src) src 追加到 dest 后
代码
const char *p = strrchr(m_url, '/'); // 在字符串 m_url 中查找最后一次出现字符 '/' 的位置,并返回指向该位置的指针if (0 == m_SQLVerify) {if (*(p + 1) == '3') // 如果 URL 中的下一个字符是 '3'{// 如果是注册,先检测数据库中是否有重名// 没有重名,就增加数据char *sql_insert = (char *)malloc(sizeof(char) * 200); // 分配内存空间strcpy(sql_insert, "INSERT INTO user(username, passwd) VALUES("); // 拼接SQL语句strcat(sql_insert, "'"); // 拼接SQL语句strcat(sql_insert, name); // 拼接SQL语句strcat(sql_insert, "', '"); // 拼接SQL语句strcat(sql_insert, "password"); // 拼接SQL语句strcat(sql_insert, "')"); // 拼接SQL语句// 判断 map 中能否找到重复的用户名if (user.find(name) == users.end()) { // 如果在map中找不到重复的用户名// 向数据库插入数据时,需要通过锁来同步数据m_lock.lock(); // 加锁int res = mysql_query(mysql, sql_insert); // 执行SQL语句users.insert(pair<string, string>(name, password)); // 将用户名和密码插入map中m_lock.unlock(); // 解锁// 校验成功,跳转登录页面if (!res)strcpy(m_url, "/log.html"); // 修改URL,跳转至登录页面// 校验失败,跳转注册失败页面else strcpy(m_url, "/registerError.html"); // 修改URL,跳转至注册失败页面}else strcpy(m_url, "/registerError.html"); // 修改URL,跳转至注册失败页面}// 如果是登录,直接判断// 若浏览器输入的用户名和密码在表中可以查找到,返回 1,否则返回 0else if (*(p + 1) == '2') { // 如果 URL 中的下一个字符是 '2'if (users.find(name) != users.end() && users[name]) // 如果在map中找到用户名,并且密码正确strcpy(m_url, "/welcome.html"); // 修改URL,跳转至欢迎页面elsestrcpy(m_url, "/logError.html"); // 修改URL,跳转至登录错误页面}
}
😘页面跳转
通过 m_url 定位 / 所在位置,根据 / 后的第一个字符,使用分支语句实现页面跳转,具体👇
- 0
- 跳转注册页面,GET
- 1
- 跳转登录页面,GET
- 5
- 显示图片页面,POST
- 6
- 显示视频页面,POST
- 7
- 显示关注页面,POST
补充解释
malloc - cppreference.com
1)👆动态分配内存
#include <stdio.h>
#include <stdlib.h>int main(void)
{int *p1 = malloc(4*sizeof(int)); // 分配足够空间以存储一个包含 4 个整数的数组int *p2 = malloc(sizeof(int[4])); // 同上,直接命名类型int *p3 = malloc(4*sizeof *p3); // 同上,无需重复类型名称if(p1) {for(int n=0; n<4; ++n) // 填充数组p1[n] = n*n;for(int n=0; n<4; ++n) // 打印数组内容printf("p1[%d] == %d\n", n, p1[n]);}free(p1); // 释放动态分配的内存free(p2);free(p3);
}
std::strncpy - cppreference.com
2)👆char *strncpy(char *dest, const char *src, size_t n)
src 复制到 dest,最多赋值 n 个字符,如果 src 长度 < n,dest 剩余部分空字节 \0 填充
eg:
#include <cstring>
#include <iostream>int main()
{const char* src = "hi";char dest[6] = {'a', 'b', 'c', 'd', 'e', 'f'};std::strncpy(dest, src, 5);std::cout << "The contents of dest are: ";for (char c : dest){if (c)std::cout << c << ' ';elsestd::cout << "\\0" << ' ';}std::cout << '\n';
}
前 5 个字符被替换为 h i \0 \0 \0,第 6 个字符保留原来的 f
The contents of dest are: h i \0 \0 \0 f
代码
// 找到 url 中 / 所在位置,进而判断 / 后第一个字符
const char *p = strrchr(m_url, '/');// 注册页面
if (*(p + 1) == '0') {// 分配内存以存储 URL 字符串,使用类型转换将返回的指针转换为 char 类型指针char *m_url_real = (char *)malloc(sizeof(char) * 200);strcpy(m_url_real, "/register.html");// 将注册页面的 URL 复制到实际文件路径中strncpy(m_real_file + len, m_url_real, strlen(m_url_real));// 释放内存free(m_url_real);
}// 登录页面
else if (*(p + 1) == '1') {char *m_url_real = (char *)malloc(sizeof(char) * 200);strcpy(m_url_real, "/log.html");// 将登录页面的 URL 复制到实际文件路径中strncpy(m_real_file + len, m_url_real, strlen(m_url_real));// 释放内存free(m_url_real);
}// 图片页面
else if (*(p + 1) == '5') {char *m_url_real = (char *)malloc(sizeof(char) * 200);strcpy(m_url_real, "/picture.html");// 将图片页面的 URL 复制到实际文件路径中strncpy(m_real_file + len, m_url_real, strlen(m_url_real));// 释放内存free(m_url_real);
}// 视频页面
else if (*(p + 1) == '6') {char *m_url_real = (char *)malloc(sizeof(char) * 200);strcpy(m_url_real, "/vedio.html");// 将视频页面的 URL 复制到实际文件路径中strncpy(m_real_file + len, m_url_real, strlen(m_url_real));// 释放内存free(m_url_real);
}// 关注页面
else if (*(p + 1) == '7') {char *m_url_real = (char *)malloc(sizeof(char) * 200);strcpy(m_url_real, "/fans.html");// 将关注页面的 URL 复制到实际文件路径中strncpy(m_real_file + len, m_url_real, strlen(m_url_real));// 释放内存free(m_url_real);
}// 否则发送 url 实际请求的文件
else// 将原始 URL 复制到实际文件路径中strncpy(m_real_file + len, m_url, FILENAME_LEN - len - 1);
相关文章:

WebServer -- 注册登录
目录 🍉整体内容 🌼流程图 🎂载入数据库表 提取用户名和密码 🚩同步线程登录注册 补充解释 代码 😘页面跳转 补充解释 代码 🍉整体内容 概述 TinyWebServer 中,使用数据库连接池实现…...

C3_W2_Collaborative_RecSys_Assignment_吴恩达_中英_Pytorch
Practice lab: Collaborative Filtering Recommender Systems(实践实验室:协同过滤推荐系统) In this exercise, you will implement collaborative filtering to build a recommender system for movies. 在本次实验中,你将实现协同过滤来构建一个电影推荐系统。 …...
Elasticsearch使用function_score查询酒店和排序
需求 基于用户地理位置,对酒店做简单的排序,非个性化的推荐。酒店评分包含以下: 酒店类型(依赖用户历史订单数据):希望匹配出更加符合用户使用的酒店类型酒店评分:评分高的酒店用户体验感好ge…...

iOS消息发送流程
Objc的方法调用基于消息发送机制。即Objc中的方法调用,在底层实际都是通过调用objc_msgSend方法向对象消息发送消息来实现的。在iOS中, 实例对象的方法主要存储在类的方法列表中,类方法则是主要存储在原类中。 向对象发送消息,核心…...

【接口测试】常见HTTP面试题
目录 HTTP GET 和 POST 的区别 GET 和 POST 方法都是安全和幂等的吗 接口幂等实现方式 说说 post 请求的几种参数格式是什么样的? HTTP特性 HTTP(1.1) 的优点有哪些? HTTP(1.1) 的缺点有哪些&#x…...

服务器硬件基础知识
1. 服务器分类 服务器分类 服务器的分类没有一个统一的标准。 从多个多个维度来看服务器的分类可以加深我们对各种服务器的认识。 N.B. CISC: complex instruction set computing 复杂指令集计算 RISC: reduced instruction set computer 精简指令集计算 EPIC: explicitly p…...

matlab实现层次聚类与k-均值聚类算法
1. 原理 1.层次聚类:通过计算两类数据点间的相似性,对所有数据点中最为相似的两个数据点进行组合,并反复迭代这一过程并生成聚类树 2.k-means聚类:在数据集中根据一定策略选择K个点作为每个簇的初始中心,然后将数据划…...

【机器学习】包裹式特征选择之递归特征消除法
🎈个人主页:豌豆射手^ 🎉欢迎 👍点赞✍评论⭐收藏 🤗收录专栏:机器学习 🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进…...

【ArcGIS】重采样栅格像元匹配问题:不同空间分辨率栅格数据统一
重采样栅格像元匹配问题:不同空间分辨率栅格数据统一 原始数据数据1:GDP分布数据2.1:人口密度数据2.2:人口总数数据3:土地利用类型 数据处理操作1:将人口密度数据投影至GDP数据(栅格数据的投影变…...

Qt 简约又简单的加载动画 第七季 音量柱风格
今天和大家分享两个音量柱风格的加载动画,这次的加载动画的最大特点就是简单,只有几行代码. 效果如下: 一共三个文件,可以直接编译运行 //main.cpp #include "LoadingAnimWidget.h" #include <QApplication> #include <QGridLayout> int main(int argc…...
【JS】数值精度缺失问题解决方案
方法一: 保留字符串类型,传给后端 方法二: 如果涉及到计算,用以下方法 // 核心思想 在计算前,将数字乘以相同倍数,让他没有小数位,然后再进行计算,然后再除以相同的倍数࿰…...

c++基础知识补充4
单独使用词汇 using std::cout; 隐式类型转换型初始化:如A a1,,此时可以形象地理解为int i1;double ji;,此时1可以认为创建了一个值为1的临时对象,然后对目标对象进行赋值,当对象为多参数时,使用(1…...
leetcode230. 二叉搜索树中第K小的元素
lletcode 230. 二叉搜索树中第K小的元素,链接:https://leetcode.cn/problems/kth-smallest-element-in-a-bst 题目描述 给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 …...
医学大数据|文献阅读|有关“肠癌+机器学习”的研究记录
目录 1.机器学习算法识别结直肠癌中的免疫相关lncRNA signature 2.基于机器学习的糖酵解相关分子分类揭示了结直肠癌癌症患者预后、TME和免疫疗法的差异,2区7 3.整合深度学习-病理组学、放射组学和免疫评分预测结直肠癌肺转移患者术后结局 4.最新7.4分纯生信&am…...

Linux信号【systemV】
目录 前言 正文: 1消息队列 1.1什么是消息队列? 1.2消息队列的数据结构 1.3消息队列的相关接口 1.3.1创建 1.3.2释放 1.3.3发送 1.3.4接收 1.4消息队列补充 2.信号量 2.1什么是信号量 2.2互斥相关概念 2.3信号量的数据结构 2.4…...

node.js最准确历史版本下载
先进入官网:Node.js https://nodejs.org/en 嫌其他博客多可以到/release下载:Node.js,在blog后面加/release https://nodejs.org/en/blog/release/ 点击next翻页,同样的道理...

UE5 C++ 单播 多播代理 动态多播代理
一. 代理机制,代理也叫做委托,其作用就是提供一种消息机制。 发送方 ,接收方 分别叫做 触发点和执行点。就是软件中的观察者模式的原理。 创建一个C Actor作为练习 二.单播代理 创建一个C Actor MyDeligateActor作为练习 在MyDeligateAc…...

前端学习、CSS
CSS可以嵌入到HTML中使用。 每个CSS语法包含两部分,选择器和应用的属性。 div用来声明针对页面上的哪些元素生效。 具体设置的属性以键值对形式表示,属性都在{}里,属性之间用;分割,键和值之间用:分割。 因为CSS的特殊命名风格…...

Flink基本原理 + WebUI说明 + 常见问题分析
Flink 概述 Flink 是一个用于进行大规模数据处理的开源框架,它提供了一个流式的数据处理 API,支持多种编程语言和运行时环境。Flink 的核心优点包括: 低延迟:Flink 可以在毫秒级的时间内处理数据,提供了低延迟的数据…...
3. 文档概述(Documentation Overview)
3. 文档概述(Documentation Overview) 本章节简要介绍一下Spring Boot参考文档。它包含本文档其它部分的链接。 本文档的最新版本可在 docs.spring.io/spring-boot/docs/current/reference/ 上获取。 3.1 第一步(First Steps) …...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...