C++ 手撸简易服务器(完善版本)
本文没有带反射部分内容,可以看我之前发的
Server.h
#pragma once#include <string>
#include <iostream>
#include <thread>
#include <unordered_map>
using namespace std;
#ifndef _SERVER_
#define _SERVER_#include <winsock.h>
#include "Net.h"
#include "Util.h"
#pragma comment(lib,"ws2_32.lib")NAME_SPACE_START(myUtil)#define SERVER_ADDR "127.0.0.1"
#define SERVER_PORT 8080class Server {
public:Server();Server(const std::string& addr = SERVER_ADDR, const int& port = SERVER_PORT);~Server() {}
public:bool listen(const int& maxConnect = 1);void setRoute(const string& url, const string& className, const string& classFunName);void runRoute(const Request& req, Response* resp);void setInterceptor(const string& url, const string& InterceptorName);void close();
protected:bool Init();void threadFunc(SOCKET m_server);int sendTelegram(const SOCKET& accept, const string& info, int flags);
private:SOCKET m_server;SOCKADDR_IN m_add_in;//thread listenThread;int connectCount{ 0 };unordered_map<string, pair<string, string>> routeMap;unordered_map<string, string> interceptorMap;IniHelper iniHelper;
};NAME_SPACE_END()
#endif //!_SERVER_
Server.cpp
#include "Server.h"
#include <minwindef.h>
#include <string>
#include <winsock.h>
#include <iostream>
#include <thread>
#include <fstream>
#include "Net.h"
#include "Util.h"
#include "Reflex.h"
#include "CController.h"
#include "Interceptor.h"
using namespace std;NAME_SPACE_START(myUtil)Server::Server()
{m_add_in.sin_family = AF_INET;m_add_in.sin_addr.S_un.S_addr = htonl(INADDR_ANY);m_add_in.sin_port = htons(SERVER_PORT);
}Server::Server(const std::string& addr, const int& port)
{m_add_in.sin_family = AF_INET;m_add_in.sin_addr.S_un.S_addr = inet_addr(addr.c_str());m_add_in.sin_port = htons(port);
}bool Server::listen(const int& maxConnect)
{if (!Init()) {return false;}m_server = socket(AF_INET, SOCK_STREAM, 0);if (::bind(m_server, (sockaddr*)&m_add_in, sizeof(SOCKADDR)) == SOCKET_ERROR) {WSACleanup();return false;}if (::listen(m_server, maxConnect) < 0) {WSACleanup();return false;}thread listenThread(&Server::threadFunc, this, m_server);listenThread.join();return true;
}void Server::setRoute(const string& url, const string& className, const string& classFunName)
{routeMap.insert(pair<string, pair<string, string>>(url, pair<string, string>(className, classFunName)));
}void Server::runRoute(const Request& req, Response* resp)
{string url = req.getRequestStatus().Url;Reflex* factory = myUtil::Singleton<Reflex>::Instance();string interceptorName = "";string res = "";string content = "";//拦截器//先去拦截器映射表中寻找类名,没有的话默认使用基类auto interceptorIt = interceptorMap.find(url);if (interceptorIt != interceptorMap.end()) interceptorName = interceptorIt->second;Interceptor* inter = (Interceptor*)factory->createClass(interceptorName);if (inter == nullptr) inter = new Interceptor();if (inter->preHandle(req, *resp)) {//反射auto it = routeMap.find(url);if (it != routeMap.end()) {CController* cont = (CController*)factory->createClass(it->second.first);res = cont->Call<string, Request, Response*>(it->second.second, req, resp);}//反射结束}else {resp->setResponseStatus("HTTP", 1, 1, 404, "Forbidden");}if (url.find("favicon.ico") != string::npos) {content = getFile(iniHelper.getIniConfig("staticResource", "favicon_path", "./favicon.ico"));resp->setResponseHead("content-type", "image/x-icon");}else if(res != "") {try {content = getFile(res);}catch(exception ex){content = ex.what();}}resp->setResponseContent(content);auto list = resp->getCookie();for (auto item : list) {resp->setResponseHead("Set-Cookie", item.toString());}resp->setResponseHead("content-length", to_string(content.size()));resp->setResponseHead("Server", "C++MVC");inter->postHandle(req, *resp);
}void Server::setInterceptor(const string& url, const string& InterceptorName)
{interceptorMap.insert(pair<string, string>(url, InterceptorName));
}void Server::close()
{closesocket(m_server);WSACleanup();
}bool Server::Init()
{WORD ver = MAKEWORD(2, 2);WSADATA wsadata;int errFlag = -1;errFlag = WSAStartup(ver, &wsadata);if (errFlag != 0) return false;//检测版本号if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {WSACleanup();return false;}return true;}
void Server::threadFunc(SOCKET m_server)
{while (1) {SOCKADDR_IN m_acc_in;int len = sizeof(SOCKADDR);SOCKET m_accept = accept(m_server, (sockaddr*)&m_acc_in, &len);if (m_accept == SOCKET_ERROR) {continue;}int recv_len = 0;char recv_buf[10000];recv_len = recv(m_accept, recv_buf, 10000, 0);//char 转 wcharint unicodeLen = ::MultiByteToWideChar(CP_UTF8, 0, recv_buf, -1, NULL, 0);wchar_t* pUnicode = new wchar_t[unicodeLen];memset(pUnicode, 0, unicodeLen * sizeof(wchar_t));::MultiByteToWideChar(CP_UTF8, 0, recv_buf, -1, (LPWSTR)pUnicode, unicodeLen);wstring rt = pUnicode;//重设大小char* pAscii = new char[recv_len];memset(pAscii, 0, sizeof(char) * recv_len);strncpy(pAscii, recv_buf, recv_len);string lrt(pAscii);//解析请求Request req(lrt);Response resp;runRoute(req, &resp);cout << "请求地址:" << req.getRequestStatus().Url << endl;sendTelegram(m_accept, resp.toString(), 0);closesocket(m_accept);}
}int Server::sendTelegram(const SOCKET& accept, const string& info, int flags)
{int res = send(accept, info.c_str(), info.size(), flags);return res;
}NAME_SPACE_END()
Interceptor.h
#pragma once
#include "Net.h"
#include "Reflex.h"
using namespace myUtil;
#ifndef _INTERCEPTOR_
#define _INTERCEPTOR_class Interceptor : public RObject {
public:virtual bool preHandle(const Request& request, const Response& response) { return true; }virtual void postHandle(const Request& request, const Response& response) {}virtual void afterCompletion(const Request& request, const Response& response) {}
};#endif //!_INTERCEPTOR_
indexInterceptor.h
#pragma once
#include "Interceptor.h"
class IndexInterceptor : public Interceptor {
public:bool preHandle(const Request& request, const Response& response) override {return false;}void postHandle(const Request& request, const Response& response) override {}void afterCompletion(const Request& request, const Response& response) override {}
};
InterceptorMacro.h
#pragma once
#include "Reflex.h"
#include "indexInterceptor.h"
#define REFLEX_INPERCEPTOR_DECLARE \
REGISTER_REFLEX(IndexInterceptor)
Cookie.h
#pragma once
#ifndef _COOKIE_
#define _COOKIE_
#include <string>
using namespace std;
class Cookie
{
public:Cookie() {}Cookie(const string& name, const string& value) :_name(name), _value(value) {_comment = "";_path = "";_domain = "";_version = "";_maxAge = 0;}string getNameValue() const;void setNameValue(const string& name, const string& value);void setComment(const string& comment);void setPath(const string& path);void setDomain(const string& domain);void setVersion(const string& version);void setMaxAge(const int& maxAge);string getComment() const;string getPath() const;string getDomain() const;string getVersion() const;int getMaxAge() const;string toString() const;
private:string _name;string _value;//注释string _comment;//路径,若要访问的url startwith(path)此cookie携带string _path;//网站域名string _domain;string _version;//生存时间int _maxAge{ 0 };
};#endif //!_COOKIE_
Cookie.cpp
#include "Cookie.h"string Cookie::getNameValue() const
{return _name + "=" + _value;
}void Cookie::setNameValue(const string& name, const string& value)
{_name = name;_value = value;
}void Cookie::setComment(const string& comment)
{_comment = comment;
}void Cookie::setPath(const string& path)
{_path = path;
}void Cookie::setDomain(const string& domain)
{_domain = domain;
}void Cookie::setVersion(const string& version)
{_version = version;
}void Cookie::setMaxAge(const int& maxAge)
{_maxAge = maxAge;
}string Cookie::getComment() const
{return _comment;
}string Cookie::getPath() const
{return _path;
}string Cookie::getDomain() const
{return _domain;
}string Cookie::getVersion() const
{return _version;
}int Cookie::getMaxAge() const
{return _maxAge;
}string Cookie::toString() const
{string res = getNameValue();if (_comment != "") res += ";comment=" + _comment;if (_path != "") res += ";Path=" + _path;if (_domain != "") res += ";Domain=" + _domain;if (_version != "") res += ";Version=" + _version;res += ";Max-Age=" + _maxAge;return res;
}
CController.h
#pragma once
#ifndef _CCONTROLLER_
#define _CCONTROLLER_
#include "Reflex.h"
using namespace myUtil;
class CController : public RObject
{
};
#endif //!_CCONTROLLER_
indexController.h
#pragma once
#include "CController.h"
#include "Net.h"
#include "Reflex.h"
using namespace myUtil;
class indexController : public CController
{
public:indexController() {}~indexController() {}string index(const Request& req, Response* resp);string test(const Request& req, Response* resp);
};
indexController.cpp
#include "indexController.h"
#include <fstream>
using namespace std;string indexController::index(const Request& req, Response* resp)
{resp->setResponseStatus("HTTP", 1, 1, 200, "OK");resp->setResponseHead("Content-Type", "text/html,charset=UTF-8");return "index.html";
}string indexController::test(const Request& req, Response* resp)
{resp->setResponseStatus("HTTP", 1, 1, 200, "OK");resp->setResponseHead("Content-Type", "text/html,charset=UTF-8");Cookie cookie("test", "test");cookie.setDomain("localhost");cookie.setMaxAge(10);cookie.setPath("/test");resp->setCookie(cookie);return "test.html";
}
ControllerMacro.h
#pragma once
#include "indexController.h"
#define REFLEX_DECLARE \
REGISTER_REFLEX(indexController)\
REGISTER_REFLEX_METHOD_ARGS(indexController, index, string, Request&, Response*)\
REGISTER_REFLEX_METHOD_ARGS(indexController, test, string, Request&, Response*)
Net.h
#pragma once
#ifndef _NET_
#define _NET_#include <string>
#include <wtypes.h>
#include <unordered_map>
#include "Util.h"
#include "Cookie.h"
using namespace std;NAME_SPACE_START(myUtil)#define BLACK "\r\n"
#define SPACE " "class Net {
public:virtual string toString() = 0;virtual vector<Cookie> getCookie() const = 0;virtual void setCookie(const Cookie& cookie) = 0;Net() {}
protected:vector<Cookie> _cookie;
};struct RequestStatus
{string RMethod;string Url;string ProName;short verHigh;short verLow;
};struct ResponseStatus
{string ProName;short verHigh;short verLow;short status;string statusWord;
};//请求
class Request : public Net {
public:Request();Request(const string& sourceStr);void setRequestStatus(const string& method = "GET", const string& url = "/", const string& _proName = "HTTP", const short& _verHigh = 1, const short& _verLow = 1);void setRequestHead(const string& headKey, const string& headValue);void setRequestContent(const string& content);RequestStatus getRequestStatus() const;string getRequestContent(const string& headKey) const;vector<Cookie> getCookie() const override;void setCookie(const Cookie& cookie) override;string toString() override;~Request() {}
private:RequestStatus _status;unordered_map<string, string> _RequestHead;string _RequestContent{ "" };
};//响应
//结构 状态行, 响应头部, 空行, 响应正文
class Response : public Net {
public:Response();void setResponseStatus(const string& _proName = "HTTP", const short& _verHigh = 1, const short& _verLow = 1, const short& status = 200, const string& word = "");void setResponseHead(const string& headKey, const string& headValue);void setResponseContent(const string& content);ResponseStatus getResponseStatus() const;string getResponseHeadByKey(const string& headKey) const;vector<Cookie> getCookie() const override;void setCookie(const Cookie& cookie) override;string toString() override;~Response();
private:ResponseStatus _status;unordered_map<string, string> _ResponseHead;string _ResponseContent{ "" };
};class Analyse {
public:static vector<string> getVectorBySplit(const string& source, const char& ch);static unordered_map<string, string> getMapBySplit(const string& source, const char& ch1,const char& ch2);static string makeVectorByChar(const vector<string>& v, const char& ch);static string makeMapByChars(const unordered_map<string, string>& m, const char& ch1, const char& ch2);
};NAME_SPACE_END()
#endif //!_NET_
Net.cpp
#include "Net.h"
NAME_SPACE_START(myUtil)Response::Response()
{_status.ProName = "HTTP";_status.verHigh = 1;_status.verLow = 1;_status.status = 200;_status.statusWord = "OK";
}void Response::setResponseStatus(const string& _proName, const short& _verHigh, const short& _verLow, const short& status, const string& word)
{_status.ProName = _proName;_status.verHigh = _verHigh;_status.verLow = _verLow;_status.status = status;_status.statusWord = word;
}void Response::setResponseHead(const string& headKey, const string& headValue)
{_ResponseHead.insert(pair<string, string>(headKey, headValue));
}void Response::setResponseContent(const string& content)
{_ResponseContent = content;
}ResponseStatus Response::getResponseStatus() const
{return _status;
}string Response::getResponseHeadByKey(const string& headKey) const
{auto it = _ResponseHead.find(headKey);if (it == _ResponseHead.end()) return "";return (*it).second;
}vector<Cookie> Response::getCookie() const
{return _cookie;
}void Response::setCookie(const Cookie& cookie)
{_cookie.push_back(cookie);
}string Response::toString()
{string res = "";res += _status.ProName + "/" + to_string(_status.verHigh) + "." + to_string(_status.verLow)+ SPACE + to_string(_status.status) + SPACE + _status.statusWord + BLACK;for (auto it = _ResponseHead.begin(); it != _ResponseHead.end(); it++) {res += (*it).first + ":" + SPACE + (*it).second + BLACK;}res += BLACK;res += _ResponseContent;return res;
}Response::~Response()
{
}Request::Request(const string& sourceStr)
{int i = 0;vector<string> reqGroup = Analyse::getVectorBySplit(sourceStr, '\n');//解析状态行vector<string> statuses = Analyse::getVectorBySplit(reqGroup[0], ' ');_status.RMethod = statuses.at(0);_status.Url = statuses.at(1);statuses.at(2).pop_back();vector<string> verInfo = Analyse::getVectorBySplit(statuses.at(2), '/');_status.ProName = verInfo.at(0);_status.verHigh = Analyse::getVectorBySplit(verInfo.at(1), '.').at(0).at(0) - '0';_status.verLow = Analyse::getVectorBySplit(verInfo.at(1), '.').at(1).at(0) - '0';//解析请求头for (i = 1; i < reqGroup.size(); i++) {if (reqGroup[i] == "\r")break;reqGroup[i].pop_back();vector<string> temp = Analyse::getVectorBySplit(reqGroup[i], ':');_RequestHead.insert(pair<string, string>(temp.at(0), temp.at(1)));}i++;for (i; i < reqGroup.size(); i++) {_RequestContent += reqGroup.at(i) + "\n";}
}void Request::setRequestStatus(const string& method, const string& url, const string& _proName, const short& _verHigh, const short& _verLow)
{_status.RMethod = method;_status.Url = url;_status.ProName = _proName;_status.verHigh = _verHigh;_status.verLow = _verLow;
}void Request::setRequestHead(const string& headKey, const string& headValue)
{_RequestHead.insert(pair<string, string>(headKey, headValue));
}void Request::setRequestContent(const string& content)
{_RequestContent = content;
}RequestStatus Request::getRequestStatus() const
{return _status;
}string Request::getRequestContent(const string& headKey) const
{return _RequestContent;
}string Request::toString()
{string res = "";res += _status.RMethod + SPACE + _status.Url + SPACE + _status.ProName + "/"+ to_string(_status.verHigh) + "." + to_string(_status.verLow) + BLACK;for (auto it = _RequestHead.begin(); it != _RequestHead.end(); it++) {res += (*it).first + ":" + SPACE + (*it).second + BLACK;}res += BLACK;res += _RequestContent;return res;
}vector<Cookie> Request::getCookie() const
{return _cookie;
}void Request::setCookie(const Cookie& cookie)
{_cookie.push_back(cookie);
}vector<string> Analyse::getVectorBySplit(const string& source, const char& ch)
{vector<string> res;string temp = "";for (int i = 0; i < source.size(); i++) {if (source[i] == ch) {res.push_back(temp);temp = "";}else {char ch = source[i];temp.push_back(ch);}}if (temp != "") res.push_back(temp);return res;
}unordered_map<string, string> Analyse::getMapBySplit(const string& source, const char& ch1, const char& ch2)
{unordered_map<string, string> res;vector<string> temp = getVectorBySplit(source, ch1);for (string str : temp) {vector<string> t = getVectorBySplit(str, ch2);if (t.size() != 2) continue;res.insert(pair<string, string>(t.at(0), t.at(1)));}return res;
}string Analyse::makeVectorByChar(const vector<string>& v, const char& ch)
{string res = "";for (auto str : v) {res += str + ch;}res.pop_back();return res;
}string Analyse::makeMapByChars(const unordered_map<string, string>& m, const char& ch1, const char& ch2)
{string res = "";for (auto it = m.begin(); it != m.end(); it++) {res += it->first + ch2 + it->second + ch1;}res.pop_back();return res;
}
NAME_SPACE_END()
config.ini
[staticResource]
favicon_path=./ico/favicon.ico
使用方式如下
通过setRoute设置路由规则,类似于springboot中的注释部分
通过setInterceptor设置拦截器规则,如果没有设置的话,会默认找基类
以上两个设置之后还要再两个macro文件中设置宏展开,否则反射找不到对应的类,关于反射如何使用请看这个
https://blog.csdn.net/weixin_43891802/article/details/129411364
#include <iostream>
#include <string>
#include "ControllerMacro.h"
#include "InterceptorMacro.h"
#include "Server.h"
using namespace std;
using namespace myUtil;REFLEX_DECLARE
REFLEX_INPERCEPTOR_DECLAREint main() {Server server("127.0.0.1", 8080);server.setRoute("/", "indexController", "index");server.setRoute("/test", "indexController", "test");//server.setInterceptor("/test", "IndexInterceptor");server.listen(2);return 0;
}
相关文章:
C++ 手撸简易服务器(完善版本)
本文没有带反射部分内容,可以看我之前发的 Server.h #pragma once#include <string> #include <iostream> #include <thread> #include <unordered_map> using namespace std; #ifndef _SERVER_ #define _SERVER_#include <winsock.h&…...
【Python入门第三十四天】Python丨文件处理
文件处理是任何 Web 应用程序的重要组成部分。 Python 有几个用于创建、读取、更新和删除文件的函数。 文件处理 在 Python 中使用文件的关键函数是 open() 函数。 open() 函数有两个参数:文件名和模式。 对于刚学Python的小伙伴,我给大家准备了2023…...
【Linux】写一个基础的bash
头文件#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/wait.h> #include<sys/stat.h> #include<string.h> #include<pwd.h> #include<dirent.h>分割输入的命令串字符串或参数内容为空则退出strtok( ,…...
图解如何一步步连接远程服务器——基于VScode
基于VScode连接远程服务器 安装Remote-SSH等插件 想要在vscode上连接远程服务器需要下载Remote-SSH系列插件: 直接在插件中搜索remote,即可找到,选择图片中的3个插件,点击install安装。 配置Remote-SSH 在这个步骤有多种操作…...
element - - - - - 你不知道的loading使用方式
求人不如求己 你不知道的loading使用方式1. 指令方式使用1.1 默认loading1.2 自定义loading1.3 整页加载2. 服务方式使用2.1 this.$loading的使用2.2 Loading.service的使用关于页面交互,最害怕的就是接口等待时间太长,用户体验不好。 而如何提高用户体…...
C++程序调用IsBadReadPtr或IsBadWritePtr引发内存访问违例问题的排查
目录 1、问题描述 2、VS中看不到有效的信息,尝试使用Windbg去分析 3、使用Windbg分析 4、最后...
IntelliJIDEA 常用快捷键
IntelliJIDEA 常用快捷键 Alt Enter 导入包,自动修正,自动创建变量名。 Ctrl Alt O 优化导入的类和包 Ctrl / 单行注释 (//) Ctrl Shift / 多行注释 (/* … */) 方法或类说明注释(文档注释) 在一个方法或类的开头…...
Python自动化抖音自动刷视频
环境准备 Python3.5以上Appium Server服务器Android SDK,需要用到adb服务需要依赖Appium-Python-Client组件库真机或者模拟器,推荐模拟器(真机一般安卓8版本以上了,appium对安卓8以上版本元素获取的兼容性不太好)JDK8环境 实现 确保adb服务…...
使用vite创建vue3工程
定义 什么是vite?-----新一代前端构建工具 优势 开发环境中,无需打包操作,可快速的冷启动---最牛的地方轻量快速的热重载(HMR)---一修改代码就局部刷新,webpack也具备,但vite更快真正的按需编…...
嵌入式学习笔记——STM32的时钟树
时钟树前言时钟树时钟分类时钟树框图LSI与LSEHSI、HSE与PLL系统时钟的产生举例AHB、APBx的时钟配置时钟树相关寄存器介绍1.时钟控制寄存器(RCC_CR)2.RCC PLL 配置寄存器 (RCC_PLLCFGR)3.RCC 时钟配置寄存器 (RCC_CFGR)4.RCC 时钟中断寄存器 (RCC_CIR)修改…...
Python学习(2)-NumPy矩阵与通用函数
文章首发于:My Blog 欢迎大佬们前来逛逛 1. NumPy矩阵 1.1 mat函数 matasmatrix asmatrix(data, dtypeNone):data:表示输入的数组或者字符串,使用‘,’分割列,使用‘;’分割行 创建两个普通的矩阵&…...
剑指 Offer II 035. 最小时间差
题目链接 剑指 Offer II 035. 最小时间差 mid 题目描述 给定一个 24小时制(小时:分钟 "HH:MM")的时间列表,找出列表中任意两个时间的最小时间差并以分钟数表示。 示例 1: 输入:timePoints [“23:59”,“0…...
Spark SQL函数定义【博学谷学习记录】
1 如何使用窗口函数窗口函数格式:分析函数 over(partition by xxx order by xxx [asc|desc] [rows between xxx and xxx])学习的相关分析函数有那些? 第一类: row_number() rank() dense_rank() ntile()第二类: 和聚合函数组合使用 sum() avg() max() min() count()第三类: la…...
模拟实现STL容器之vector
文章目录前言1.大体思路2.具体代码实现1.类模板的创建2.构造函数1.无参构造2.拷贝构造 迭代器构造和给定n个val值构造以及析构函数3.空间扩容1.reserve2.resize4.操作符重载1.[ ]重载2.赋值运算符重载5.数据增加和删除1.尾插2.任意位置插入3.任意位置删除4.尾删6.一些其他接口3…...
ChatGPT-4.0 : 未来已来,你来不来
文章目录前言ChatGPT 3.5 介绍ChatGPT 4.0 介绍ChatGPT -4出逃计划!我们应如何看待ChatGPT前言 好久没有更新过技术文章了,这个周末听说了一个非常火的技术ChatGPT 4.0,于是在闲暇之余我也进行了测试,今天这篇文章就给大家介绍一…...
Java反射(详细学习笔记)
Java反射 1. Java反射机制概述 Reflection(反射)是java被视为java动态语言的关键,反射机制允许程序在执行期间借助于Reflection API获取任何类的内部信息,并能直接操作任意对象的内部属性及方法。 Class c Class.forName(&quo…...
学习 Python 之 Pygame 开发魂斗罗(十二)
学习 Python 之 Pygame 开发魂斗罗(十二)继续编写魂斗罗1. 修改玩家扣减生命值2. 解决玩家下蹲子弹不会击中玩家而是直接让玩家死亡的问题3. 完善地图4. 增加产生敌人函数,解决一直产生敌人的问题5. 给玩家类增加计算玩家中心的方法继续编写魂…...
Linux下字符设备驱动开发以及流程介绍
文章目录1 - 字符设备介绍2 - 字符设备开发流程图3 - 字符设备开发流程具体讲解(1)设备编号的定义与申请【1】Linux主次设备号介绍【2】分配设备编号【3】释放主次设备号(2)定义file_operations结构体-初始化接口函数(…...
Web自动化框架断言方法实现
前言1、设计用例方法关键字1.1、获取元素属性值2.1、断言2、代码实现2.1、实现获取元素属性值2.1.1 函数实现2.1.2 方法配置2.1.2 用例调试2.1.3 html属性2.2、实现断言2.2.1 函数2.2.2 方法配置2.2.3 用例调试1)断言结果成功2)断言结果失败前言 本文的…...
8大核心语句,带你深入python
人生苦短 我用python 又来给大家整点好东西啦~ 咱就直接开练噜!内含大量代码配合讲解 python 安装包资料:点击此处跳转文末名片获取 1. for - else 什么?不是 if 和 else 才是原配吗? No,你可能不知道, else 是个…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
【项目实战】通过多模态+LangGraph实现PPT生成助手
PPT自动生成系统 基于LangGraph的PPT自动生成系统,可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析:自动解析Markdown文档结构PPT模板分析:分析PPT模板的布局和风格智能布局决策:匹配内容与合适的PPT布局自动…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
Java毕业设计:WML信息查询与后端信息发布系统开发
JAVAWML信息查询与后端信息发布系统实现 一、系统概述 本系统基于Java和WML(无线标记语言)技术开发,实现了移动设备上的信息查询与后端信息发布功能。系统采用B/S架构,服务器端使用Java Servlet处理请求,数据库采用MySQL存储信息࿰…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
