当前位置: 首页 > news >正文

TCP客户端connect断线重连

文章目录

  • TCP客户端connect断线重连
    • 1、为什么要断线重连
    • 2、实现代码

img

TCP客户端connect断线重连

1、为什么要断线重连

客户端会面临服务器崩溃的情况,我们可以试着写一个客户端重连的代码,模拟并理解一些客户端行为,比如游戏客户端等.

考虑到下面2种情况:

  • 服务器故障突然断开连接,过几秒又恢复连接,那么在这等待的几秒,客户端可以重新发起连接。
  • 客户端wifi突然断了,也就是没网,然后过几秒又恢复了,客户端又可以重新发起连接了。

2、实现代码

代码主要实现细节:

  1. 枚举类型Status:

    • 用于表示连接状态,包括NEW, CONNECTED, CONNECTING, DISCONNECTED, CLOSE
  2. 全局常量:

    • defaultsockfd,默认套接字文件描述符为-1。

    • retryinterval,重连间隔时间为1秒。

    • retryamxtimes,重连最大次数为5次。

  3. Connection类:

    • 构造函数初始化服务器IP、端口、状态、重连间隔和最大重连次数。

    • ConnectStatus()方法返回当前连接状态。

    • Connect()方法负责建立连接,并设置连接状态。

    • Process()方法负责发送和接收数据,处理通信过程中的各种状态变化。

    • ReConnect()方法处理重连逻辑,尝试在连接失败时进行多次重连。

    • DisConnect()方法负责断开连接并重置套接字文件描述符。

  4. TcpClient类:

    • 构造函数初始化连接对象。

    • Execute()方法根据连接状态执行相应操作,包括建立连接、处理数据、重连和断开连接。

  5. Usage()函数:

    • 用于输出使用说明。
  6. main()函数:

    • 检查命令行参数,初始化TcpClient对象并执行连接逻辑。
  • TcpClient.cc文件:
#include <iostream>
#include <string>
#include <strings.h>
#include <unistd.h>#include "Comm.hpp"enum class Status
{NEW,CONNECTED,CONNECTING,DISCONNECTED,CLOSE
};const int defaultsockfd = -1;
const int retryinterval = 1; // 重连间隔时间
const int retryamxtimes = 5; // 重连最大次数class Connection
{
public:Connection(std::string serverip, uint16_t serverport): _sockfd(defaultsockfd),_serverip(serverip),_serverport(serverport),_status(Status::NEW),_retry_interval(retryinterval),_retry_max_times(retryamxtimes){}Status ConnectStatus(){return _status;}void Connect(){_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){_status = Status::DISCONNECTED;exit(CREATE_ERROR);}struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(_serverport);// int inet_pton(int af, const char *src, void *dst);inet_pton(AF_INET, _serverip.c_str(), &server.sin_addr.s_addr); // 也可以&server.sin_addr,因为sin_addr只有s_addrint n = ::connect(_sockfd, CONV(&server), sizeof(server));if (n < 0){DisConnect(); //_status = Status::DISCONNECTED;return;}std::cout << "connect success" << std::endl;_status = Status::CONNECTED; // 已连接}void Process(){while (true){// 发送数据std::string message;std::cout << "Please Enter# ";std::getline(std::cin, message);int n = send(_sockfd, message.c_str(), message.size(), 0);if (n > 0){// 接收数据char buff[1024];int m = recv(_sockfd, buff, sizeof(buff) - 0, 0);if (m > 0){buff[m] = 0;std::cout << "Server Echo$ " << buff << std::endl;}else{_status = Status::DISCONNECTED; // 接收不成功就重连std::cerr << "recv error" << std::endl;break;}}else{_status = Status::CLOSE; // 发送不成功就退出std::cerr << "send error" << std::endl;break;}}}void ReConnect(){_status = Status::CONNECTING;int cnt = 1;while (true){Connect();if (_status == Status::CONNECTED){break;}std::cout << "正在重连,重连次数 : " << cnt++ << std::endl;if (cnt > _retry_max_times){_status = Status::CLOSE; // 重连失败std::cout << "重连失败,请检查网络.." << std::endl;break;}sleep(_retry_interval);}}void DisConnect(){if (_sockfd > defaultsockfd){close(_sockfd);_sockfd = defaultsockfd;}}private:int _sockfd;std::string _serverip;uint16_t _serverport;Status _status;int _retry_interval;int _retry_max_times;
};class TcpClient
{
public:TcpClient(std::string serverip, uint16_t serverport) : _connect(serverip, serverport){}void Execute(){while (true){switch (_connect.ConnectStatus()){case Status::NEW:_connect.Connect();break;case Status::CONNECTED:_connect.Process();break;case Status::DISCONNECTED:_connect.ReConnect();break;case Status::CLOSE:_connect.DisConnect();return; // 断开连接了,重连不管用了default:break;}}}private:Connection _connect;
};void Usage()
{std::cout << "Please use format : ./tcp_client serverip serverport" << std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage();exit(USAGE_ERROR);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);TcpClient tcpclient(serverip, serverport);tcpclient.Execute();return 0;
}

和UDP服务器响应程序一样,客户端发什么就回什么,只不过多了建立连接的步骤。

  • Comm.hpp文件
#pragma once
#include "InetAddr.hpp"enum errorcode
{CREATE_ERROR = 1,BIND_ERROR,LISTEN_ERROR,SEND_ERROR,RECV_ERROR,CONNECT_ERROR,FORK_ERROR,USAGE_ERROR
};#define CONV(ADDR) ((struct sockaddr *)ADDR)std::string CombineIpAndPort(InetAddr addr)
{return "[" + addr.Ip() + ":" + std::to_string(addr.Port()) + "] ";
}
  • InetAddr.hpp文件
#pragma once#include <iostream>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string>class InetAddr
{void GetAddress(std::string *ip, uint16_t *port){// char *inet_ntoa(struct in_addr in);*ip = inet_ntoa(_addr.sin_addr);*port = ntohs(_addr.sin_port);}public:InetAddr(const struct sockaddr_in &addr) : _addr(addr){GetAddress(&_ip, &_port);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}bool operator==(InetAddr &addr){return _ip == addr.Ip() && _port == addr.Port();}const struct sockaddr_in& GetAddr(){return _addr;}~InetAddr() {}private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;
};
  • LockGuard.hpp文件
# pragma once#include <pthread.h>class LockGuard
{
public:LockGuard(pthread_mutex_t *mutex) : _mutex(mutex){pthread_mutex_lock(_mutex); // 构造加锁}~LockGuard(){pthread_mutex_unlock(_mutex); // 析构解锁}private:pthread_mutex_t *_mutex;
};
  • Log.hpp文件
#pragma once#include <string>
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <stdarg.h>
#include <sys/types.h>
#include "LockGuard.hpp"using namespace std;bool isSave = false; // 默认向显示器打印
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#define FILEPATH "./log.txt"enum level
{DEBUG = 0,INFO,WARNING,ERROR,FATAL
};void SaveToFile(const string &message)
{ofstream out(FILEPATH, ios_base::app);if (!out.is_open())return;out << message;out.close();
}std::string LevelToString(int level)
{switch (level){case DEBUG:return "Debug";case INFO:return "Info";case WARNING:return "Warning";case ERROR:return "Error";case FATAL:return "Fatal";default:return "Unknow";}
}std::string GetTimeString()
{time_t curr_time = time(nullptr);struct tm *format_time = localtime(&curr_time);if (format_time == nullptr)return "None";char buff[1024];snprintf(buff, sizeof(buff), "%d-%d-%d %d:%d:%d",format_time->tm_year + 1900,format_time->tm_mon + 1,format_time->tm_mday,format_time->tm_hour,format_time->tm_min,format_time->tm_sec);return buff;
}void LogMessage(const std::string filename, int line, bool issave, int level, const char *format, ...)
{std::string levelstr = LevelToString(level);std::string timestr = GetTimeString();pid_t pid = getpid();char buff[1024];va_list arg;// int vsnprintf(char *str, size_t size, const char *format, va_list ap); // 使用可变参数va_start(arg, format);vsnprintf(buff, sizeof(buff), format, arg);va_end(arg);LockGuard lock(&mutex);std::string message = "[" + timestr + "]" + "[" + levelstr + "]" + "[pid:" + std::to_string(pid) + "]" + "[" + filename + "]" + "[" + std::to_string(line) + "] " + buff + '\n';if (issave == false)std::cout << message;elseSaveToFile(message);
}// 固定文件名和行数
#define LOG(level, format, ...)                                               \do                                                                        \{                                                                         \LogMessage(__FILE__, __LINE__, isSave, level, format, ##__VA_ARGS__); \} while (0)#define EnableScreen()  \do                  \{                   \isSave = false; \} while (0)#define EnableFile()   \do                 \{                  \isSave = true; \} while (0)void Test(int num, ...)
{va_list arg;va_start(arg, num);while (num--){int data = va_arg(arg, int);std::cout << data << " ";}std::cout << std::endl;va_end(arg);
}
  • Main.cc文件
#include <iostream>
#include <memory>
#include "TcpServer.hpp"void Usage()
{// printf("./udp_server serverip serverport\n");printf("Usage : ./udp_server serverport\n"); // ip 已经设置为0
}int main(int argc, char *argv[])
{// if (argc != 3)if (argc != 2){Usage();exit(USAGE_ERROR);}uint16_t serverport = std::stoi(argv[1]);std::unique_ptr<TcpServer> tsvr = std::make_unique<TcpServer>(serverport);tsvr->InitServer();tsvr->Start();return 0;
}
  • Makefile文件
.PHONY:all
all:tcp_client tcp_servertcp_client:TcpClient.ccg++ -o $@ $^ -std=c++14 -lpthread
tcp_server:Main.ccg++ -o $@ $^ -std=c++14 -lpthread.PHONY:clean
clean:rm -f tcp_server tcp_client
  • TcpServer.hpp文件
#pragma once#include <sys/types.h> /* See NOTES */
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>#include <error.h>
#include <string.h>
#include <pthread.h>
#include <functional>#include "Log.hpp"
#include "InetAddr.hpp"
#include "Comm.hpp"
#include "Threadpool.hpp"const int defaultsockfd = -1;
int gbacklog = 16; // 暂时先用using task_t = std::function<void()>;// 声明
class TcpServer;class ThreadData
{
public:ThreadData(int sockfd, InetAddr addr, TcpServer *self): _sockfd(sockfd), _addr(addr), _self(self) {}~ThreadData() = default;public:int _sockfd;InetAddr _addr;TcpServer *_self;
};class TcpServer
{
public:TcpServer(uint16_t port) : _port(port), _listensock(defaultsockfd), _isrunning(false){}void InitServer(){// 创建_listensock = socket(AF_INET, SOCK_STREAM, 0); // 这个就是文件描述符if (_listensock < 0){LOG(FATAL, "create sockfd error, error code : %d, error string : %s", errno, strerror(errno));exit(CREATE_ERROR);}LOG(INFO, "create sockfd success");struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;// 绑定int n = ::bind(_listensock, CONV(&local), sizeof(local));if (n < 0){LOG(FATAL, "bind sockfd error, error code : %d, error string : %s", errno, strerror(errno));exit(BIND_ERROR);}LOG(INFO, "bind sockfd success");}void Service(int sockfd, InetAddr client){while (true){// TCP是字节流(可以使用write和read接口),UDP是数据报char buff[1024];// 接收消息int n = ::read(sockfd, buff, sizeof(buff)); // bug,接收数据可能收到的不完整,比如1+100,可能先收到1+1,再收到00 -- 按序到达std::string clientAddr = CombineIpAndPort(client);if (n > 0){buff[n] = 0;std::string message = clientAddr + buff;LOG(INFO, "get message : \n %s", message.c_str());// 发送消息int m = ::write(sockfd, buff, strlen(buff)); if (m < 0){LOG(FATAL, "send message error ,error code : %d , error string : %s", errno, strerror(errno));exit(SEND_ERROR);}}else if (n == 0){// 发送端不发送数据了LOG(INFO, "%s quit", clientAddr.c_str());break;}else{LOG(FATAL, "recv message error ,error code : %d , error string : %s", errno, strerror(errno));exit(RECV_ERROR);}}::close(sockfd); // 服务结束,关闭文件描述符,避免文件描述符泄漏}static void *HandlerService(void *args){pthread_detach(pthread_self()); // 分离线程ThreadData *td = static_cast<ThreadData *>(args);td->_self->Service(td->_sockfd, td->_addr);delete td;return nullptr;}void Start(){_isrunning = true;while (_isrunning){// 监听int ret = ::listen(_listensock, gbacklog);if (ret < 0){LOG(FATAL, "listen error, error code : %d , error string : %s", errno, strerror(errno));exit(LISTEN_ERROR);}LOG(INFO, "listen success!");struct sockaddr_in peer;socklen_t len = sizeof(peer);// 获取新连接int sockfd = accept(_listensock, CONV(&peer), &len); // 建立连接成功,创建新文件描述符进行通信if (sockfd < 0){LOG(WARNING, "accept error, error code : %d , error string : %s", errno, strerror(errno));continue;}LOG(INFO, "accept success! new sockfd : %d", sockfd);InetAddr addr(peer); // 给后面提供传入的ip、port// 服务 -- 发送和接收数据// V0 -- 单进程// Service(sockfd, addr); // 这里是while死循环,没有运行完就一直运行,下一个请求来的时候得这个while退出才能执行// v1 -- 多进程// int id = fork();// if (id == 0)// {//     // 子进程//     ::close(_listensock); // 子进程对监听文件描述符不关心//     if (fork() > 0)//         exit(0);           // 子进程创建进程后退出,孙子进程被系统领养,不用等待//     Service(sockfd, addr); // 孙子进程执行任务//     exit(0);// }// else if (id > 0)// {//     ::close(sockfd); // 这里每次关闭的文件描述符都是4,使得每次accept创建的文件描述符都是4,这个4是留个各个子进程(子进程再给孙子进程)的(互不影响)//     // 父进程//     pid_t rid = waitpid(id, nullptr, 0); // 虽然是阻塞等待,但是子进程是刚创建就退出来了,让孙子进程(孤儿进程)执行任务,接下来继续监听和建立连接//     if (rid == id)//     {//         LOG(INFO, "wait child process success");//     }// }// else// {//     // 异常//     LOG(FATAL, "fork error ,error code : %d , error string : %s", errno, strerror(errno));//     exit(FORK_ERROR);// }// v2 -- 多线程// pthread_t tid;// ThreadData *td = new ThreadData(sockfd, addr, this); // 传指针// pthread_create(&tid, nullptr, HandlerService, td); // 这里创建线程后,线程去做执行任务,主线程继续向下执行 , 并且线程不能关闭sockf,线程和进程共享文件描述符表// v3 -- 线程池task_t t = std::bind(&TcpServer::Service, this, sockfd, addr);Threadpool<task_t>::GetInstance()->Enqueue(t);// v4 -- 进程池 -- 不推荐,需要传递文件描述符!}_isrunning = false;}~TcpServer(){if (_listensock > defaultsockfd)::close(_listensock); // 不用了关闭监听}private:uint16_t _port;int _listensock;bool _isrunning;
};
  • Thread.hpp文件
#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>using namespace std;// 封装Linux线程
namespace ThreadModule
{using func_t = function<void(string &)>;class Thread{public:// /* ThreadData* */Thread(func_t<T> func, T data, const string& name = "default name") : _func(func), _data(data), _threadname(name), _stop(true) {}Thread(func_t func, const string &name = "default name") : _func(func), _threadname(name), _stop(true) {}void Execute(){_func(_threadname);// _func(_data);}//  隐含thisstatic void *threadroutine(void *arg){Thread *self = static_cast<Thread *>(arg);self->Execute(); // static 访问不了成员变量return nullptr;}bool Start(){int n = pthread_create(&_tid, nullptr, threadroutine, this);if (!n){_stop = false;return true;}else{return false;}}void Detach(){if (!_stop){pthread_detach(_tid);}}void Join(){if (!_stop){pthread_join(_tid, nullptr);}}string name(){return _threadname;}void Stop(){_stop = true;}// ~Thread() {}private:pthread_t _tid;string _threadname;func_t _func;bool _stop;};} // namespace ThreadModule#endif
  • Threadpool.hpp文件
#pragma once#include <vector>
#include <queue>
#include <queue>
#include "Thread.hpp"
#include <pthread.h>
#include "LockGuard.hpp"using namespace ThreadModule;const int NUM = 3;template <typename T>
class Threadpool
{void LockQueue(pthread_mutex_t &mutex){pthread_mutex_lock(&mutex);}void UnLockQueue(pthread_mutex_t &mutex){pthread_mutex_unlock(&mutex);}void SleepThread(pthread_cond_t &cond, pthread_mutex_t &mutex){pthread_cond_wait(&cond, &mutex);}void WakeUpThread(pthread_cond_t &cond){pthread_cond_signal(&cond);}void WakeUpAll(pthread_cond_t &cond){pthread_cond_broadcast(&_cond);}Threadpool(const int threadnum = NUM) : _threadnum(threadnum), _waitnum(0), _isrunning(false){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);LOG(INFO, "Threadpool Constructor successful ! ");}void TaskHandler(string &name){// sleep(1);// cout  << name << " : hh " << endl;// sleep(1);LOG(DEBUG, "%s is running", name.c_str());while (true){LockQueue(_mutex);while (_task_queue.empty() && _isrunning){// 等待++_waitnum;SleepThread(_cond, _mutex);--_waitnum;}// 此时一定大于一个线程没有休眠if (_task_queue.empty() && !_isrunning){// 此时任务队列已经没有内容,且此时线程池已经停止UnLockQueue(_mutex);cout << name << " quit ... " << endl;break;}LOG(DEBUG, "%s get task sucessful !", name.c_str());//  其他情况就得处理任务T t = _task_queue.front();_task_queue.pop();UnLockQueue(_mutex);// 处理任务t();// cout << name << " : " << t.stringResult() << endl;// LOG(DEBUG, "%s handler task sucessful ! Result is %s", name.c_str(), t.stringResult().c_str());sleep(1);}}void InitThreadPool(){for (int i = 0; i < _threadnum; ++i){string name = "Thread - " + to_string(i + 1);_threads.emplace_back(bind(&Threadpool::TaskHandler, this, placeholders::_1), name);}_isrunning = true;LOG(INFO, "Init Threadpool successful !");}public:static Threadpool<T> *GetInstance(int threadnum = NUM){if (_instance == nullptr){LockGuard lockguard(&_lock);if (_instance == nullptr){// pthread_mutex_lock(&_lock);// 第一次创建线程池_instance = new Threadpool<T>(threadnum);_instance->InitThreadPool();_instance->Start();LOG(DEBUG, "第一次创建线程池");// pthread_mutex_unlock(&_lock);return _instance;}}LOG(DEBUG, "获取线程池");return _instance;}bool Enqueue(const T &in){bool ret = false;LockQueue(_mutex);if (_isrunning){_task_queue.push(in);if (_waitnum > 0)WakeUpThread(_cond);LOG(DEBUG, "enqueue sucessful...");ret = true;}UnLockQueue(_mutex);return ret;}void Stop(){LockQueue(_mutex);_isrunning = false;if (_waitnum > 0)WakeUpAll(_cond);UnLockQueue(_mutex);}void Start(){for (auto &thread : _threads){thread.Start();LOG(INFO, "%s is start sucessful...", thread.name().c_str());}}void Wait(){for (auto &thread : _threads){thread.Join();LOG(INFO, "%s is quit...", thread.name().c_str());}}~Threadpool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);LOG(INFO, "delete mutex sucessful !");}private:vector<Thread> _threads;queue<T> _task_queue;int _threadnum;int _waitnum;pthread_mutex_t _mutex; // 互斥访问任务队列pthread_cond_t _cond;bool _isrunning;// 懒汉模式static Threadpool<T> *_instance;static pthread_mutex_t _lock;
};template <typename T>
Threadpool<T> *Threadpool<T>::_instance = nullptr;
template <typename T>
pthread_mutex_t Threadpool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;

运行结果:


OKOK,TCP客户端connect短线重连就到这里,如果你对Linux和C++也感兴趣的话,可以看看我的主页哦。下面是我的github主页,里面记录了我的学习代码和leetcode的一些题的题解,有兴趣的可以看看。

Xpccccc的github主页

相关文章:

TCP客户端connect断线重连

文章目录 TCP客户端connect断线重连1、为什么要断线重连2、实现代码 TCP客户端connect断线重连 1、为什么要断线重连 客户端会面临服务器崩溃的情况&#xff0c;我们可以试着写一个客户端重连的代码&#xff0c;模拟并理解一些客户端行为&#xff0c;比如游戏客户端等. 考虑到…...

细说MCU用DMA改变DAC输出信号频率和改善输出波形质量的方法

目录 一、参考硬件 二、修改定时器参数改变输出波形频率 三、改善波形质量 四、代码修改 五、查看结果 一、参考硬件 本项目的软件硬件工程参考作者的其他文章&#xff1a;细说MCU用DMA实现DAC输出的方法-CSDN博客 https://wenchm.blog.csdn.net/article/details/14065…...

Java高级面试题

文章目录 Java高级特性Java 8 中的新特性有哪些&#xff1f;Lambda 表达式的用途是什么&#xff1f;Stream API 的工作原理是什么&#xff1f;Optional 类的作用是什么&#xff1f;什么是函数式接口&#xff1f;Java 9 之后的新特性有哪些&#xff1f; 并发编程Java中的线程池是…...

USART串口理论知识总结

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 USART串口理论知识总结 1、通讯的串行和并行1.串口采用发送数据代码并用printf重代码 1、通讯的串行和并行 1.串口采用发送数据代码并用printf重代码 #include <stdint.h…...

基于 HTML+ECharts 实现智慧景区数据可视化大屏(含源码)

构建智慧景区数据可视化大屏&#xff1a;基于 HTML 和 ECharts 的实现 随着旅游业的蓬勃发展&#xff0c;智慧景区的概念逐渐深入人心。通过数据可视化&#xff0c;景区管理者可以实时监控游客流量、设施使用情况以及环境状况&#xff0c;从而提升游客体验和管理效率。本文将详…...

vxe-table——实现切换页码时排序状态的回显问题(ant-design+elementUi中table排序不同时回显的bug)——js技能提升

之前写的后台管理系统&#xff0c;都是用的antdelement&#xff0c;table组件中的【排序】问题是有一定的缺陷的。 想要实现的效果&#xff1a; antv——table组件一次只支持一个参数的排序 如下图&#xff1a; 就算是可以自行将排序字段拼接到列表接口的入参中&#xff0c…...

SQL

SQL全称 Structured Query Language&#xff0c;结构化查询语言。操作关系型数据库的编程语言&#xff0c;定义了一套操作关系型数据库统一标准 。 SQL通用语法 SQL语句可以单行或多行书写&#xff0c;以分号结尾。SQL语句可以使用空格/缩进来增强语句的可读性。MySQL数据库的…...

maven archetype

1.简介 maven脚手架是为了创建一个项目模板&#xff0c;以后新建项目都能够复用该模板 maven中模板引擎使用的是velocity,在文件中可以使用它的语法获取变量等操作 2.实现 单模块脚手架实现 pom.xml <?xml version"1.0" encoding"UTF-8"?> &…...

浏览器打开抽奖系统html

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>在线抽奖 随机选取 自动挑选</title> <script src"https://libs.baidu.com/jquery/1.10.2/jquery.min.js"></script> <style> body {…...

微信小程序-使用Component方法代替Page方法构造页面

一.使用Component方法的前提条件 在小程序js文件里使用Component方法代替Page方法需要在json文件里面定义usingComponents属性 {"usingComponents": {} }二.注意事项 1.在page页面里使用的钩子函数和事件监听方法都需要写在methods对象里面 methods:{update(){thi…...

Spark SQL----DISTRIBUTE BY子句

Spark SQL----DISTRIBUTE BY子句 一、描述二、语法三、参数四、例子 一、描述 DISTRIBUTE BY子句用于根据输入表达式对数据进行重新分区。与CLUSTER BY子句不同&#xff0c;这不会对每个分区内的数据进行排序。 二、语法 DISTRIBUTE BY { expression [ , ... ] }三、参数 e…...

HTML5-canvas1

1、canvas&#xff1a;创建画布 <canvas id"canvas"></canvas>2、画一条直线 var canvasdocument.getElementById(cancas&#xff09;; canvas.width800; canvas.height800; var contextcanvas.getContext(2d); //获得2d绘图上下文环境 //画一条直线 c…...

【NOI-题解】1009 - 数组逆序1162 - 数组元素的删除1211 - 数组元素的插入1161. 元素插入有序数组1159. 数组元素的移动

文章目录 一、前言二、问题问题&#xff1a;1009 - 数组逆序问题&#xff1a;1162 - 数组元素的删除问题&#xff1a;1211 - 数组元素的插入问题&#xff1a;1161. 元素插入有序数组问题&#xff1a;1159. 数组元素的移动 三、感谢 一、前言 本章节主要对数组问题中数组元素移…...

新电脑如何设置 npm 源及查看源、安装 cnpm、pnpm 和 yarn 的详细教程

当你获得一台新电脑&#xff0c;或需要在现有电脑上优化 JavaScript 和 Node.js 的开发环境时&#xff0c;正确配置 npm 是一步不可少的过程。本教程将详细指导你如何设置 npm 源&#xff0c;查看当前源&#xff0c;以及如何安装 cnpm、pnpm 和 yarn。 1. 设置 npm 源 npm (N…...

完全移动huggingface模型仓库(不是简单mv)

Linux中移动huggingface模型仓库 参考链接 先在bashrc中配置&#xff1a; export HF_DATASETS_CACHE"/your/path/dataset" export HF_HOME"/your/path/" export HUGGINGFACE_HUB_CACHE"/your/path/hub" export TRANSFORMERS_CACHE"/your…...

手机空号过滤批量查询的意义及方法

手机空号过滤批量查询是现代营销和通信管理中常用的技术手段&#xff0c;旨在通过批量处理手机号码&#xff0c;筛选出活跃号码和空号等无效号码&#xff0c;以提高营销效率和减少不必要的通信成本。以下是关于手机空号过滤批量查询的详细解答&#xff1a; 一、手机空号过滤批…...

Dockerfile制作部署wordpress-6.6

目录 一. 环境准备 二. 准备对应的配置文件 三. 编写Dockerfile 四. 构建镜像 五. 配置MySQL 六. 安装wordpress 七. 扩展 一. 环境准备 localhost192.168.226.25 rocky_linux9.4 Docker version 27.0.3 关闭防火墙和selinux&#xff0c;进行时间同步。 安装docker…...

项目的纪要

ai客服项目中发现的问题: 可以在控制台看到我们存储的cookie: 可以看到是这样的, 但是我们通过getCookie方法专门获取这个字段, 然后在控制台打印后 const userName getCookie(SA_USER_NICK_NAME); console.log(userName, userName); 输出结果是: 然后我们尝试通过de…...

ubuntu 更新源

前言 实现一键替换在线源 一键更新源 ubuntu 全球镜像站以下支持现有ubuntu 20&#xff0c;22&#xff0c;24 echo "Delete the default source" rm -rf /etc/apt/sources.listecho "Build a new source" cat <<EOF>>/etc/apt/sources.li…...

XGBoost、RF随机森林算法MATLAB实现

% 加载并预处理训练数据 opts1 = detectImportOptions(附件一AE.xlsx, PreserveVariableNames, true); train_data = readtable(附件一AE.xlsx, opts1); train_data.Time = datetime(train_data.time, InputFormat, yyyy-MM-dd HH:mm:ss); % 特征提取和标签准备 windowSize…...

WPF 解决: DataGrid 已定义列,但是还是会显示模型的所有属性的问题

AutoGenerateColumns 属性 AutoGenerateColumns&#xff1a;这个属性决定 DataGrid 是否根据数据源中的属性自动生成列。如果设置为 true&#xff0c;DataGrid 会根据数据源中的属性自动生成列。如果设置为 false&#xff0c;则 DataGrid 不会自动生成列&#xff0c;开发者需要…...

【ai】Easy-RAG : ImportError: cannot import name ‘BaseModel‘ from ‘pydantic‘

[Bug]: cannot import name ‘RootModel’ from ‘pydantic’ #1237 版本不匹配导致 ImportError: cannot import name ‘BaseModel’ from ‘pydantic’ /home/zhangbin/miniconda3/envs/Easy-RAG/bin/python /home/zhangbin/proj/06_rag/02_Easy-RAG/webui.py /home/zhangbi…...

WebKit简介

WebKit是一个开源的浏览器引擎&#xff0c;最初由苹果公司开发&#xff0c;用于Safari浏览器。它是基于KDE项目的KHTML引擎进行开发&#xff0c;并在改进和扩展中形成了WebKit引擎。 WebKit的工作流程可以分为以下几个步骤&#xff1a; 1.解析HTML&#xff1a;当浏览器加载一个…...

笔记 | Python环境下的GUI编程常用包

前言 Python的使用频率和范围越来越大&#xff0c;在一些开发工作中由于需要可视化的图形界面&#xff0c;常常需要进行图形用户界面&#xff08;Graphic User Interface, GUI&#xff09;开发。例如&#xff0c;目前最火热的大模型应用&#xff0c;常常是以一个网页界面进行操…...

mysql 数据库空间统计sql

mysql 数据库空间统计 文章目录 mysql 数据库空间统计说明一、数据库存储代码二、查询某个数据库的所有表的 代码总结 说明 INFORMATION_SCHEMA Table Reference 表参考 information_schema是‌MySQL中的一个特殊数据库&#xff0c;它存储了关于所有其他数据库的元数据信息。…...

【Linux】线程——线程池、线程池的实现、线程安全的线程池、单例模式的概念、饿汉和懒汉模式、互斥锁、条件变量、信号量、自旋锁、读写锁

文章目录 Linux线程7. 线程池7.1 线程池介绍7.2 线程池的实现7.3 线程安全的线程池7.3.1 单例模式的概念7.3.2 饿汉和懒汉模式 8. 常见锁使用汇总8.1 互斥锁&#xff08;Mutex&#xff09;8.2 条件变量&#xff08;Condition Variable&#xff09;8.3 信号量&#xff08;Semaph…...

stm32入门-----TIM定时器(PWM输出比较——下)

目录 前言 一、硬件元器件介绍 1.舵机 2.直流电机驱动 二、C语言编程步骤 1.开启时钟 2.配置输出的GPIO口 3.配置时基单元 4.初始化输出比较通道 5.开启定时器 三、实践项目 1.PWM驱动LED呼吸灯 2.PWM驱动舵机 3.PWM驱动直流电机 前言 本期我们就开始去进行TIM定时…...

css实现线条中间高亮,左右两边模糊(linear-gradient的运用)

效果&#xff1a; <div class"line"></div> .line {height: 1px;background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, #a9c2ff 50%, rgba(255, 255, 255, 0) 100%);border-radius: 4px 4px 4px 4px; } CSS实现边框底部渐变色的方法:(最简单…...

【数据结构】建堆算法复杂度分析及TOP-K问题

【数据结构】建堆算法复杂度分析及TOP-K问题 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;数据结构 文章目录 【数据结构】建堆算法复杂度分析及TOP-K问题前言一.复杂度分析1.1向下建堆复杂度1.2向上建堆复杂度1.3堆排序复杂度 二.TOP-K问…...

Thinkphp5实现前后端通过接口通讯基本操作方法

在ThinkPHP5框架中&#xff0c;实现前后端通过接口通讯是一个常见的需求&#xff0c;尤其是在开发RESTful API时。下面是一个基本的步骤指南&#xff0c;用于设置ThinkPHP5来创建API接口&#xff0c;并使前端能够通过HTTP请求与后端进行通讯。 1. 创建API模块 首先&#xff0…...

Go 语言任务编排 WaitGroup

WaitGroup 是常用的 Go 同步原语之一,用来做任务编排。它要解决的就是并发-等待的问题: 现在有一个 goroutine A 在检查点 ( checkpoint ) 等待一组 goroutine 全部完成它们的任务,如果这些 goroutine 还没全部完成任务,那么 goroutine A 就会被阻塞在检查点,直到所有的 …...

星环科技推出知识库产品 AI PC时代数据交互方式变革

随着企业业务的快速发展&#xff0c;数据量呈爆炸式增长&#xff0c;有效的知识管理成为企业面临的重要问题。企业遇到的普遍问题是大量的结构化、半结构化数据存储在不同的系统中&#xff0c;需要用多种计算机语言进行检索。而大模型彻底改变了人们和数据的交互方式&#xff0…...

10道JVM经典面试题

1、 JVM中&#xff0c;new出来的对象是在哪个区&#xff1f; 2、 说说类加载有哪些步骤&#xff1f; 3、 JMM是什么&#xff1f; 4、 说说JVM内存结构&#xff1f; 5、 MinorGC和FullGC有什么区别&#xff1f; 6、 什么是STW? 7、 什么情况下会发生堆/栈溢出&#xff1f…...

Redisson常用的数据结构及应用场景

Redisson 提供了一系列高级数据结构&#xff0c;这些数据结构封装了 Redis 的原生数据类型&#xff0c;提供了 Java API 的便利性和分布式特性。以下是 Redisson 中一些常用的数据结构&#xff0c;场景还在不断完善中&#xff1a; RBucket&#xff1a;这是一个简单的键值对存储…...

【实现100个unity特效之8】使用ShaderGraph实现2d贴图中指定部分局部发光效果

最终效果 寒冰法师 火焰法师 文章目录 最终效果寒冰法师火焰法师 素材一、功能分析实现方法基本思路Unity的Bloom后处理为什么关键部位白色&#xff1f;最终结果 二、 新建URP项目三、合并图片四、使用PS制作黑白图片方法一 手动涂鸦方法二 魔棒工具1. 拖入图片进PS&#xff0…...

Ubuntu 24.04 LTS Noble安装Docker Desktop简单教程

Docker 为用户提供了在 Ubuntu Linux 上快速创建虚拟容器的能力。但是&#xff0c;那些不想使用命令行管理容器的人可以在 Ubuntu 24.04 LTS 上安装 Docker Desktop GUI&#xff0c;本教程将提供用于设置 Docker 图形用户界面的命令…… Docker Desktop 是一个易于使用的集成容…...

XML 和 SimpleXML 入门教程

XML 和 SimpleXML 入门教程 XML&#xff08;可扩展标记语言&#xff09;是一种用于存储和传输数据的标记语言。它是一种自我描述的语言&#xff0c;允许用户定义自己的标签来表示数据。SimpleXML 是 PHP 中的一个扩展&#xff0c;用于解析和操作 XML 数据。本文将介绍 XML 和 …...

leetcode--链表类题目总结

本文作为刷题时对链表类题目的总结. 常见技巧: 引入虚拟头节点 便于处理边界情况便于对链表操作快慢双指针(判环,找环的入口等)链表逆序(推荐使用 虚拟头节点 头插法 进行逆序) 链表逆序( 头插法 虚拟头节点):链表内指定区间反转_牛客题霸_牛客网 虚拟节点:合并…...

打卡第22天------回溯算法

开始学习了,希望我可以尽快成功上岸! 一、回溯理论基础 什么是回溯法?回溯法也可以叫做回溯搜索法,它是一种搜索的方式。 回溯是递归的副产品,只要有递归就会有回溯。 回溯法的效率回溯法的本质是穷举,穷举所有可能,然后找出我们想要的答案。如果想让回溯法高效一些,可…...

Ubuntu对比两个文件内容有什么区别?

在Ubuntu&#xff08;或任何基于Linux的系统&#xff09;中&#xff0c;你可以使用多种命令行工具来比较两个文件的内容差异。以下是一些常用的方法&#xff1a; 1. **diff 命令**&#xff1a; diff 是Linux中用于比较两个文件差异的标准工具。它逐行比较文件&#xff0c;并显示…...

python:本机摄像头目标检测实时推理(使用YOLOv8n模型)

本文将介绍如何使用本机摄像头进行目标检测实时推理的python代码。 文章目录 一、下载YOLO权重文件二、环境配置三、完整代码 一、下载YOLO权重文件 https://github.com/ultralytics/ultralytics?tabreadme-ov-file 拉到网页最下面&#xff0c;选择适合的模型&#xff0c;下…...

Spark实时(四):Strctured Streaming简单应用

文章目录 Strctured Streaming简单应用 一、Output Modes输出模式 二、Streaming Table API 三、​​​​​​​​​​​​​​Triggers 1、​​​​​​​unspecified&#xff08;默认模式&#xff09; 2、​​​​​​​​​​​​​​Fixed interval micro-batches&am…...

SpringBoot上传超大文件导致OOM,完美问题解决办法

问题描述 报错: Caused by: java.lang.OutOfMemoryError at java.io.ByteArrayOutputStream.hugeCapacity(ByteArrayOutputStream.java:123) ~[?:1.8.0_381] at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:117) ~[?:1.8.0_381] at java.…...

PyTorch 的各个核心模块和它们的功能

1. torch 核心功能 张量操作&#xff1a;PyTorch 的张量是一个多维数组&#xff0c;类似于 NumPy 的 ndarray&#xff0c;但支持 GPU 加速。数学运算&#xff1a;提供了各种数学运算&#xff0c;包括线性代数操作、随机数生成等。自动微分&#xff1a;torch.autograd 模块用于…...

Java开发之LinkedList源码分析

#来自ゾフィー&#xff08;佐菲&#xff09; 1 简介 LinkedList 的底层数据结构是双向链表。可以当作链表、栈、队列、双端队列来使用。有以下特点&#xff1a; 在插入或删除数据时&#xff0c;性能好&#xff1b;允许有 null 值&#xff1b;查询效率不高&#xff1b;线程不安…...

外卖霸王餐系统架构怎么选?

在当今日益繁荣的外卖市场中&#xff0c;外卖霸王餐作为一种独特的营销策略&#xff0c;受到了众多商家的青睐。然而&#xff0c;要想成功实施外卖霸王餐活动&#xff0c;一个安全、稳定且高效的架构选择至关重要。本文将深入探讨外卖霸王餐架构的选择&#xff0c;以期为商家提…...

AV1技术学习:Transform Coding

对预测残差进行变换编码&#xff0c;去除潜在的空间相关性。VP9 采用统一的变换块大小设计&#xff0c;编码块中的所有的块共享相同的变换大小。VP9 支持 4 4、8 8、16 16、32 32 四种正方形变换大小。根据预测模式选择由一维离散余弦变换 (DCT) 和非对称离散正弦变换 (ADS…...

Git操作指令

Git操作指令 一、安装git 1、设置配置信息&#xff1a; # global全局配置 git config --global user.name "Your username" git config --global user.email "Your email"2、查看git版本号 git -v # or git --version3、查看配置信息&#xff1a; git…...

CSS 创建:从入门到精通

CSS 创建:从入门到精通 CSS(层叠样式表)是网页设计中不可或缺的一部分,它用于控制网页的布局和样式。本文将详细介绍CSS的创建过程,包括基本概念、语法结构、选择器、样式属性以及如何将CSS应用到HTML中。无论您是初学者还是有经验的开发者,本文都将为您提供宝贵的信息。…...

Windows 11 系统对磁盘进行分区保姆级教程

Windows 11磁盘分区 磁盘分区是将硬盘驱动器划分为多个逻辑部分的过程&#xff0c;每个逻辑部分都可以独立使用和管理。在Windows 11操作系统中进行磁盘分区主要有以下几个作用和意义&#xff1a; 组织和管理数据&#xff1a;分区可以帮助用户更好地组织他们的数据&#xff0c…...