C/C++精品项目之图床共享云存储(4):注册,登录,token,排序文件

目录

一:CHttpConn 类

二:注册

三:登录

四:获取文件


C/C++精品项目之图床共享云存储(1):基础组件-CSDN博客

C/C++精品项目之图床共享云存储(2):MySql连接池_mysql 连接池c++ ping-CSDN博客

C/C++精品项目之图床共享云存储(3):网络缓冲区类和main-CSDN博客

一:CHttpConn 类

        我们上一期讲了一点点这个 CHttpConn 类,这一期继续讲解这个类。首先我们回顾一下,我们客户端与服务端创建好连接之后,分配了一个socket,然后我们在连接上之后,触发回调函数,创建了一个CHttpConn对象,这个对象和这个socket进行绑定,然后这个对象进行初始化,再一次传入新的回调函数,当我们触发不同的读写事件,那么进入不同的读写回调函数。

void httpconn_callback(void *callback_data, uint8_t msg, uint32_t handle,uint32_t uParam, void *pParam) {NOTUSED_ARG(uParam);NOTUSED_ARG(pParam);// convert void* to uint32_t, oopsuint32_t conn_handle = *((uint32_t *)(&callback_data)); //获取连接标识  ,也就是获得跟socket绑定的http的handleCHttpConn *pConn = FindHttpConnByHandle(conn_handle);   //根据连接标识找到对应的http对象if (!pConn) {   //如果没有则返回// LogWarn("no find conn_handle: {}", conn_handle);return;}pConn->AddRef();        //添加引用计数switch (msg) {case NETLIB_MSG_READ:   //可读事件pConn->OnRead();break;case NETLIB_MSG_WRITE:  //可写事件pConn->OnWrite();break;case NETLIB_MSG_CLOSE:pConn->OnClose();   //关闭事件break;default:LogError("!!!httpconn_callback error msg:{}", msg); //fix me,可以考虑如果这里触发,直接关闭该连接// pConn->Close();break;}pConn->ReleaseRef(); //释放引用计数,如果数据发送完毕,对应的http 连接在这个位置为析构对象
}

        现在我们查看读事件中的代码:我们触发读事件,无非注册,登录,发送请求。我们在这个函数中,肯定是先将发送来的数据读出来,这个时候就用到我们网络缓冲区类了,读完数据后,要对数据进行解析。我们分别得到url和body的数据,我们根据传来的url进行区分不同的请求。而body是具体的一些参数。

//这里的onread函数:就是我们客户端向服务端发送数据,首先就是接收数据到读缓冲区,然后我们将数据拿出来,然后就是进行http解析,通过解析到不同的url \
我们能跳转到不同的处理函数中去。
void CHttpConn::OnRead() // CHttpConn业务层面的OnRead
{LogInfo("conn_handle_ = {}, socket_handle_ = {}", conn_handle_, socket_handle_);// 1. 把能读取的数据都读取出来for (;;) {uint32_t free_buf_len = in_buf_.GetAllocSize() - in_buf_.GetWriteOffset();if (free_buf_len < READ_BUF_SIZE + 1)   //这里多预留一个字节的目的是加上结束符时不会越界,因为我们可能会发送多条消息 in_buf_.Extend(READ_BUF_SIZE + 1);      //为了区分这些消息,我们每次接受完都要加上一个结束符,这样我们就可以通过结束符来区分不同的消息//读取socket数据int ret = netlib_recv(socket_handle_,in_buf_.GetBuffer() + in_buf_.GetWriteOffset(),               //写道buf的写后面READ_BUF_SIZE);if (ret <= 0)break; //没有数据可以读取了in_buf_.IncWriteOffset(ret); // 更新下一个接收数据的位置}// 2.通过http模块解析http请求的数据char *in_buf = (char *)in_buf_.GetBuffer();uint32_t buf_len = in_buf_.GetWriteOffset();in_buf[buf_len] = '\0'; // 末尾加上结束符 方便分析结束位置和打印,这里之所以不越界是因为有预留in_buf_.Extend(READ_BUF_SIZE + 1)// 如果buf_len 过长可能是受到攻击,则断开连接 ,目前我们接受的所有数据长度不得大于2Kif (buf_len > 2048) {LogError("get too much data: {}", in_buf);Close();return;}LogInfo("buf_len: {}, in_buf: {}", buf_len, in_buf); //将请求的数据都打印出来,方便调试分析http请求// 解析http数据http_parser_.ParseHttpContent(in_buf, buf_len); // 1. 从socket接口读取数据;2.然后把数据放到buffer in_buf; 3.http解析if (http_parser_.IsReadAll()) {string url = http_parser_.GetUrl();string content = http_parser_.GetBodyContent();LogInfo("url: {}", url);                     // for debug// 根据url处理不同的业务 if (strncmp(url.c_str(), "/api/reg", 8) == 0) { // 注册  url 路由。 根据根据url快速找到对应的处理函数, 能不能使用map,hash_HandleRegisterRequest(url, content);} else if (strncmp(url.c_str(), "/api/login", 10) == 0) { // 登录_HandleLoginRequest(url, content);} else if (strncmp(url.c_str(), "/api/myfiles", 10) == 0) { //获取我的文件数量_HandleMyfilesRequest(url, content);}  else {LogError("url unknown, url= {}", url);Close();}}
}

二:注册

        对于注册来说,无非就是从数据库上查询和插入嘛。首先我们通过解析body中的所有参数,也就是我们注册所填的参数:姓名,昵称,密码,等等。我们通过一个http解析的函数将这些数据解析出来,然后进入到注册的具体环节。

int ApiRegisterUser(string &post_data, string &resp_json) {int ret = 0;string user_name;string nick_name;string pwd;string phone;string email;LogInfo("post_data: {}", post_data);// 判断数据是否为空if (post_data.empty()) {LogError("decodeRegisterJson failed");// 封装注册结果encodeRegisterJson(1, resp_json);return -1;}// 解析jsonif (decodeRegisterJson(post_data, user_name, nick_name, pwd, phone, email) < 0) {LogError("decodeRegisterJson failed");// 封装注册结果encodeRegisterJson(1, resp_json);return -1;}// 注册账号ret = registerUser(user_name, nick_name, pwd, phone, email);// 封装注册结果ret = encodeRegisterJson(ret, resp_json);return 0;
}

        注册的具体环节也就是和数据库打交道,我们首先通过我们的MySql连接池获得一个空闲的连接,然后给数据库发送查询命令,看看是否存在了已经。已存在返回具体的值,不存在则进行插入环节。我们准备好具体的SQL命令,通过添加其中所需要的参数,然后执行。这样就注册成功了。

int registerUser(string &user_name, string &nick_name, string &pwd,string &phone, string &email) {int ret = 0;uint32_t user_id;CDBManager *db_manager = CDBManager::getInstance();                 //通过数据库的manger,我们可以拿到相关数据库的连接池。CDBConn *db_conn = db_manager->GetDBConn("tuchuang_master");AUTO_REL_DBCONN(db_manager, db_conn);       //栈上构建一个对象 退出的时候自动把连接规划连接池// 先查看用户是否存在string str_sql = FormatString("select * from user_info where user_name='%s'", user_name.c_str());LogInfo("执行: {}", str_sql);CResultSet *result_set = db_conn->ExecuteQuery(str_sql.c_str());if (result_set && result_set->Next()) { // 检测是否存在用户记录// 存在在返回LogWarn("id: {}, user_name: {}  已经存在", result_set->GetInt("id"), result_set->GetString("user_name"));delete result_set;ret = 2;} else { // 如果不存在则注册time_t now;char create_time[TIME_STRING_LEN];//获取当前时间now = time(NULL);strftime(create_time, TIME_STRING_LEN - 1, "%Y-%m-%d %H:%M:%S",localtime(&now));str_sql = "insert into user_info ""(`user_name`,`nick_name`,`password`,`phone`,`email`,`create_""time`) values(?,?,?,?,?,?)";LogInfo("执行: {}", str_sql);// mysql操作 如果不熟悉可以参考:https://www.yuque.com/linuxer/linux_senior/rcz4xl?singleDoc# 《mysql api c客户端》CPrepareStatement *stmt = new CPrepareStatement();if (stmt->Init(db_conn->GetMysql(), str_sql)) {uint32_t index = 0;string c_time = create_time;stmt->SetParam(index++, user_name);stmt->SetParam(index++, nick_name);stmt->SetParam(index++, pwd);stmt->SetParam(index++, phone);stmt->SetParam(index++, email);stmt->SetParam(index++, c_time);bool bRet = stmt->ExecuteUpdate();if (bRet) {ret = 0;user_id = db_conn->GetInsertId();LogInfo("insert user_id: {}", user_id); //用户id是自增id} else {LogError("insert user_info failed. {}", str_sql);ret = 1;}}delete stmt;}return ret;
}

 注册成功我们也需要返回一些数据给客户端,首先就是通信的头部,然后就是将代表注册成功的信息填入我们要发送的数据中,然后发送。

// 账号注册处理
void CHttpConn::_HandleRegisterRequest(string &url, string &post_data) {string resp_json;int ret = ApiRegisterUser(post_data, resp_json);char *http_body = new char[HTTP_RESPONSE_HTML_MAX];uint32_t ulen = resp_json.length();snprintf(http_body, HTTP_RESPONSE_HTML_MAX, HTTP_RESPONSE_HTML, ulen,resp_json.c_str()); 	ret = Send((void *)http_body, strlen(http_body));delete[] http_body;LogInfo("Send remain = {}", ret); 
}

三:登录

        其实登录和注册也差不多,就是从数据库中查询是否有这个用户,先解析姓名和密码,然后通过一个函数验证这个姓名和密码是否存在于数据库中。

int ApiUserLogin(string &post_data, string &resp_json) {string user_name;string pwd;string token;// 判断数据是否为空if (post_data.empty()) {return -1;}// 解析jsonif (decodeLoginJson(post_data, user_name, pwd) < 0) {LogError("decodeRegisterJson failed");encodeLoginJson(1, token, resp_json);return -1;}// 验证账号和密码是否匹配if (verifyUserPassword(user_name, pwd) < 0) {LogError("verifyUserPassword failed");encodeLoginJson(1, token, resp_json);return -1;}// 生成token并存储在redisif (setToken(user_name, token) < 0) {LogError("setToken failed");encodeLoginJson(1, token, resp_json);return -1;} else {encodeLoginJson(0, token, resp_json);return 0;}
}

这个函数就是进行检查的,先连接数据库,执行相应的命令,返回具体的结果。

int verifyUserPassword(string &user_name, string &pwd) {int ret = 0;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn);// 先查看用户是否存在string strSql;strSql = FormatString("select password from user_info where user_name='%s'", user_name.c_str());CResultSet *result_set = db_conn->ExecuteQuery(strSql.c_str());uint32_t nowtime = time(NULL);if (result_set && result_set->Next()) {// 存在在返回string password = result_set->GetString("password");LogInfo("mysql-pwd: {}, user-pwd: {}", password, pwd);if (result_set->GetString("password") == pwd)ret = 0;elseret = -1;} else { // 如果不存在则注册ret = -1;}delete result_set;return ret;
}

 当我们验证登陆成功之后,我们开始创建一个token,token是什么呢?
Token 的中文有人翻译成 “令牌”,意思就是,你拿着这个令牌,才能过一些关卡。
        Token 是一个用户自定义的任意字符串。在成功提交了开发者自定义的这个字符串之后,Token 的值会保存到服务器后台。只有服务器和客户端前端知道这个字符串,于是 Token 就成了这两者之间的密钥,它可以让服务器确认请求是来自客户端还是恶意的第三方。这里所说的 Token,本质上就是 http session。使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:
1) 客户端使用用户名跟密码请求登录
2) 服务端收到请求,去验证用户名与密码
3) 验证成功后,服务端生成一个 Token,这个 Token 可以存储在内存、磁盘、或者数据库里,再把这个 Token 发送给客户端
4) 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里或者 Local Storage

5) 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token
6) 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据

int setToken(string &user_name, string &token) {int ret = 0;CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("token");AUTO_REL_CACHECONN(cache_manager, cache_conn);token = RandomString(32); // 随机32个字母if (cache_conn) {//用户名:token, 86400有效时间为24小时cache_conn->SetEx(user_name, 86400, token); // redis做超时} else {ret = -1;}return ret;
}

 在最后我们不是已经生成好token了吗,我们要将这个token返回给客户端,让客户端前端和服务器之间有令牌。

void CHttpConn::_HandleLoginRequest(string &url, string &post_data)
{string resp_json;int ret = ApiUserLogin( post_data, resp_json);char *http_body = new char[HTTP_RESPONSE_HTML_MAX];uint32_t ulen = resp_json.length();snprintf(http_body, HTTP_RESPONSE_HTML_MAX, HTTP_RESPONSE_HTML, ulen,resp_json.c_str()); 	ret = Send((void *)http_body, strlen(http_body));delete[] http_body;LogInfo("Send remain = {}", ret); 
}

四:获取文件

        获取文件列表,这个操作是在登陆成功之后做的事情,那么让我们看看其中token的作用,以及我们怎么获取文件列表的。首先我们看到的就是一些变量,下面就是一个解析cmd的函数,这里的cmd是什么意思呢?比如我们要访问 127.0.0.1:80/api/myfiles&cmd=normal ,我们写入这个url,那么就代表我们要对文件进行排序操作。而count这个变量是在具体的json里面的。如果为0,那就是返回全部。

        接下来除了一开始的解析 json 参数外,最重要的也就是验证token,因为这里的操作已经是在登录以后得了。

//获取文件列表,首先就是要解析json,看看是要获取文件个数还是文件信息,无论是什么,首先就是要获取token,这个是为了确保使我们当前的连接 \
如果是数量,那么我们就直接通过传入来的用户名进行查找,当然查找之前要验证token,所以我们要连接mysql和redis,当token通过就开始查找数量
//如果是对文件进行排序的话,和它差不多,首先获取具体命令,验证token,连接mysql,拿取数据。
int ApiMyfiles(string &url, string &post_data, string &resp_json) {// 解析url有没有命令// count 获取用户文件个数// display 获取用户文件信息,展示到前端char cmd[20];string user_name;string token;           //这个tocken是在服务之间进行传递的,用来验证用户的合法性,它存在于json中。int ret = 0;int start = 0; //文件起点int count = 0; //文件个数//解析命令 解析url获取自定义参数,这里的自定义参数是指按什么顺序返回,比如从大到小QueryParseKeyValue(url.c_str(), "cmd", cmd, NULL);LogInfo("url: {}, cmd: {} ",url, cmd);if (strcmp(cmd, "count") == 0) {// 解析jsonif (decodeCountJson(post_data, user_name, token) < 0) {encodeCountJson(1, 0, resp_json);LogError("decodeCountJson failed");return -1;}//验证登陆token,成功返回0,失败-1ret = VerifyToken(user_name, token); // util_cgi.hif (ret == 0) {// 获取文件数量if (handleUserFilesCount(user_name, count) < 0) { //获取用户文件个数LogError("handleUserFilesCount failed");encodeCountJson(1, 0, resp_json);} else {LogInfo("handleUserFilesCount ok, count: {}", count);encodeCountJson(0, count, resp_json);}} else {LogError("VerifyToken failed");encodeCountJson(1, 0, resp_json);}return 0;} else {if ((strcmp(cmd, "normal") != 0) && (strcmp(cmd, "pvasc") != 0) &&(strcmp(cmd, "pvdesc") != 0)) {LogError("unknow cmd: {}", cmd);encodeCountJson(1, 0, resp_json);}//获取用户文件信息 127.0.0.1:80/api/myfiles&cmd=normal//按下载量升序 127.0.0.1:80/api/myfiles?cmd=pvasc//按下载量降序127.0.0.1:80/api/myfiles?cmd=pvdescret = decodeFileslistJson(post_data, user_name, token, start,count); //通过json包获取信息LogInfo("user_name: {}, token:{}, start: {}, count: {}", user_name,token, start, count);if (ret == 0) {//验证登陆token,成功返回0,失败-1ret = VerifyToken(user_name, token); // util_cgi.hif (ret == 0) {string str_cmd = cmd;if (getUserFileList(str_cmd, user_name, start, count,resp_json) < 0) { //获取用户文件列表LogError("getUserFileList failed");encodeCountJson(1, 0, resp_json);}} else {LogError("VerifyToken failed");encodeCountJson(1, 0, resp_json);}} else {LogError("decodeFileslistJson failed");encodeCountJson(1, 0, resp_json);}}return 0;
}

我们这里的验证token是需要连接redis数据库,为什么用户数据和文件数据存放在mysql,而token需要放在redis,因为redis是内存数据库,速度很快,我们每一次的内部操作都需要验证token,所以存放在redis中。我们通过传入来的用户信息,来获得具体的token。

int VerifyToken(string &user_name, string &token) {int ret = 0;CacheManager *cache_manager = CacheManager::getInstance();// increase message countCacheConn *cache_conn = cache_manager->GetCacheConn("token");AUTO_REL_CACHECONN(cache_manager, cache_conn);if (cache_conn) {string tmp_token = cache_conn->Get(user_name);if (tmp_token == token) {ret = 0;} else {ret = -1;}} else {ret = -1;}return ret;
}

接下来肯定就是具体的查找逻辑了,当然在这操作之前,肯定要获取一个空闲的mysql连接了。

int DBGetUserFilesCountByUsername(CDBConn *db_conn, string user_name,int &count) {count = 0;int ret = 0;// 先查看用户是否存在string str_sql;str_sql = FormatString("select count(*) from user_file_list where user='%s'", user_name.c_str());LogInfo("执行: {}", str_sql);CResultSet *result_set = db_conn->ExecuteQuery(str_sql.c_str());if (result_set && result_set->Next()) {// 存在在返回count = result_set->GetInt("count(*)");LogInfo("count: {}", count);ret = 0;delete result_set;} else if (!result_set) { // 操作失败LogError("{} 操作失败", str_sql);LogError("{} 操作失败", str_sql);ret = -1;} else {// 没有记录则初始化记录数量为0ret = 0;LogInfo("没有记录: count: {}", count);}return ret;
}

而这里就是查找全部的文件信息了,一样是线连接mysql数据库,然后设置sql命令,返回结果之后,我们将结果返回给客户端。

int getUserFileList(string &cmd, string &user_name, int &start, int &count,string &str_json) {LogInfo("getUserFileList into");int ret = 0;int total = 0;string str_sql;CDBManager *db_manager = CDBManager::getInstance();CDBConn *db_conn = db_manager->GetDBConn("tuchuang_slave");AUTO_REL_DBCONN(db_manager, db_conn);CacheManager *cache_manager = CacheManager::getInstance();CacheConn *cache_conn = cache_manager->GetCacheConn("token");AUTO_REL_CACHECONN(cache_manager, cache_conn);ret = getUserFilesCount(db_conn, cache_conn, user_name,total); // 总共的文件数量if (ret < 0) {LogError("getUserFilesCount failed");return -1;} else {if (total == 0) {Json::Value root;root["code"] = 0;root["count"] = 0;root["total"] = 0;Json::FastWriter writer;str_json = writer.write(root);LogWarn("getUserFilesCount = 0");return 0;}}//多表指定行范围查询if (cmd == "normal") //获取用户文件信息{str_sql = FormatString("select user_file_list.*, file_info.url, file_info.size,  file_info.type from file_info, user_file_list where user = '%s' \and file_info.md5 = user_file_list.md5 limit %d, %d",user_name.c_str(), start, count);} else {LogError("unknown cmd: {}", cmd);return -1;}LogInfo("执行: {}", str_sql);CResultSet *result_set = db_conn->ExecuteQuery(str_sql.c_str());if (result_set) {// 遍历所有的内容// 获取大小int file_index = 0;Json::Value root, files;root["code"] = 0;while (result_set->Next()) {Json::Value file;file["user"] = result_set->GetString("user");file["md5"] = result_set->GetString("md5");file["create_time"] = result_set->GetString("create_time");file["file_name"] = result_set->GetString("file_name");file["share_status"] = result_set->GetInt("shared_status");file["pv"] = result_set->GetInt("pv");file["url"] = result_set->GetString("url");file["size"] = result_set->GetInt("size");file["type"] = result_set->GetString("type");files[file_index] = file;file_index++;}root["files"] = files;root["count"] = file_index;root["total"] = total;Json::FastWriter writer;str_json = writer.write(root);delete result_set;return 0;} else {LogError("{} 操作失败", str_sql);return -1;}
}

        本篇主要讲解,注册,登录,token,获取文件的一些具体操作,其中比较重要的就是token,而其他的业务操作全都是和数据库打交道,将查询的结果返回到客户端这里。0voice · GitHub

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/19591.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Spring AOP面向切面的编程

一、场景设定和问题复现: 1.准备AOP项目:spring-aop-annotation pom.xml <dependencies><!--spring context依赖--><!--当你引入Spring Context依赖之后&#xff0c;表示将Spring的基础依赖引入了--><dependency><groupId>org.springframework…

已有docker增加端口号,不用重新创建Docker

已有docker增加端口号&#xff0c;不用重新创建Docker 1. 整体描述2. 具体实现2.1 查看容器id2.2 停止docker服务2.3 修改docker配置文件2.4 重启docker服务 3. 总结 1. 整体描述 docker目前使用的非常多&#xff0c;但是每次更新都需要重新创建docker&#xff0c;也不太方便&…

【WSL+Kali】进行系统升级时在 Setting up libc6:amd64 (2.37-15) ... 卡住不动

问题描述 当尝试执行以下命令进行系统升级时&#xff1a; sudo apt upgrade升级进程在以下步骤中卡住不动&#xff1a; Setting up libc6:amd64 (2.37-15) ...重启系统后&#xff0c;该问题仍然存在&#xff0c;如下图所示&#xff1a; 原因分析 apt命令是一个用于处理包的…

三、谷粒商城- Spring Cloud Alibaba(3)

&#x1f33b;&#x1f33b; 目录 &#x1f33b;&#x1f33b; 一、SpringCloud Alibaba1.1、SpringCloud Alibaba 简介1.2、SpringCloud Alibaba-Nacos[作为注册中心]1.2.1 将微服务注册到 nacos 中1.2.2 服务注册到 nacos&#xff0c;远程调用 1.3、SpringCloud Alibaba-Naco…

SpringFramework实战指南

1. SpringIoC容器和核心概念 1.1. Spring IoC容器和容器实现 1.1.1. SpringIoc容器接口 BeanFactory 接口提供了一种高级配置机制&#xff0c;能够管理任何类型的对象&#xff0c;是SpringIoC容器标准化超接口&#xff01; ApplicationContext 是 BeanFactory 的子接口。它扩…

【油猴脚本】00013 案例 Tampermonkey油猴脚本, 仅用于学习,不要乱搞。添加UI交互实现自定义,更多页抓取数据(1),JavaScript爬虫HTML+Css+JavaScript编写

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【油…

Debezium-MySqlConnectorTask

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 MySqlConnectorTask&#xff0c;用于读取MySQL的二进制日志并生成对应的数据变更事件 整体架构流程 技术名词解释 数据库模式&#xff08;Database Schema&#xff09; 数据库模式是指数据库中数据的组织结构和定义&…

Linux脚本练习

通过shell脚本分析部署nginx网络服务 1.接收用户部署的服务名称 2.判断服务是否安装 ​ 已安装&#xff1b;自定义网站配置路径为/www&#xff1b;并创建共享目录和网页文件&#xff1b;重启服务 ​ 没有安装&#xff1b;安装对应的软件包 3.测试 判断服务是否成功运…

Windows系统编程 - 进程间通信

文章目录 前言概述发送消息WM_COPYDATADLL共享段文件映射文件映射步骤相关API讲解文件映射 进程间的通信&#xff08;有文件版本&#xff09;文件映射 进程间的通信&#xff08;匿名版本&#xff09; 管道相关API讲解父子之间的匿名进程通信GetStdHandleSTARTUPINFO指定句柄测试…

基于yolov8、yolov5的植物类别识别系统(含UI界面、训练好的模型、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8、yolov8 SE注意力机制 或 yolov5、yolov5 SE注意力机制 &#xff0c; 直接提供最少两个训练好的模型。模型十分重要&#xff0c;因为有些同学的电脑没有 GPU&#xff0…

1+X应急响应(网络)系统信息收集分析:

系统信息收集分析&#xff1a; 系统启动项和计划任务分析&#xff1a; 系统进程&#xff0c;服务分析&#xff1a; 内存取证&#xff1a; 系统崩溃转储&#xff1a;

智慧环保平台_大数据平台_综合管理平台_信息化云平台

系统原理   智慧环保是新一代信息技术变革的产物&#xff0c;是信息资源日益成为重要生产要素和信息化向更高阶段发展的表现&#xff0c;是经济社会发展的新引擎。   现今&#xff0c;环保信息化建设进入高速发展阶段。在此轮由物联网掀起的信息浪潮下&#xff0c;环境信息…

如何通过电脑监控软件远程监控一台电脑的所有屏幕画面记录

7-1 本教程介绍一个简单的工具&#xff0c;可以安装在电脑中&#xff0c;按设置的时间间隔&#xff0c;自动对屏幕截图保存&#xff0c;并且可以在有网络的其它电脑上远程提取截图文件。 该软件用于自动记录电脑的屏幕画面内容和变化&#xff0c;如果你有这方面的使用场景&am…

深度解读混合专家模型(MoE):算法、演变与原理

假设一个专家团队共同解决复杂问题。每位专家都拥有独特的技能&#xff0c;团队通过高效分配任务实现了前所未有的成功。这就是混合专家&#xff08;Mixture-of-Experts&#xff0c;MoE&#xff09;模型架构背后的基本思想&#xff0c;这种方法允许机器学习系统&#xff0c;特别…

电商微服务项目第一天(品牌管理)

1.BaseTrademarkController&#xff08;品牌管理CRUD&#xff09; /*** 添加品牌* param baseTrademark* return*/PostMapping("baseTrademark/save")public Result<BaseTrademark> save(RequestBody BaseTrademark baseTrademark){baseTrademarkService.save(…

初探Ranking系统的离在线满意度评估

【引子】在上周发布了《大模型应用系列&#xff1a;从Ranking到Reranking》之后&#xff0c; 有AI 产品经理问我&#xff0c;如何评估Ranking 系统的性能呢&#xff1f; 再进一步&#xff0c;如何评估RAG系统的性能呢&#xff1f; 老码农整理了一下在搜索引擎方面的感受&#x…

初识C++ (五)

没事干就学习 auto关键字 auto是C程序设计语言的关键字。自C11以来&#xff0c;auto关键字用于两种情况&#xff1a;声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。C98标准中auto关键字用于自动变量的声明&#xff0c;但由于使用极少且多余…

shell脚本判断nginx安装和运行

shell脚本判断nginx安装和运行 脚本内容&#xff1a; 传入服务名称&#xff1a; read -p "请输入要判断的程序名称:" service_name 查看服务进程&#xff1a; countps -aux | grep -cw $service_name 判断nginx是否安装&#xff08;系统中是否有nginx命令&#xff…

电脑msvcr100.dll丢失的解决方法,详细介绍多个解决方法

由于系统中关键文件msvcr100.dll的缺失&#xff0c;用户可能会遭遇一系列始料未及的困扰与问题。msvcr100.dll是Microsoft Visual C运行库中的一个核心动态链接库文件&#xff0c;对于许多应用程序的正常运行至关重要。当这个特定的dll文件丢失时&#xff0c;可能会导致部分软件…

Windows安装vcpkg教程(VS2022)

内容摘要&#xff1a; 本文详细介绍如何在Windows系统上使用 Git 克隆 vcpkg 仓库来安装vcpkg工具&#xff0c;并链接Visual Studio 2022。 目录 一、关于vcpkg 二、开发环境 三、安装Git 四、使用 Git 克隆 vcpkg 仓库 一、关于vcpkg vcpkg 是一个开源的 C 包管理工具&am…