TCP客户端connect断线重连
文章目录
- TCP客户端connect断线重连
- 1、为什么要断线重连
- 2、实现代码

TCP客户端connect断线重连
1、为什么要断线重连
客户端会面临服务器崩溃的情况,我们可以试着写一个客户端重连的代码,模拟并理解一些客户端行为,比如游戏客户端等.
考虑到下面2种情况:
- 服务器故障突然断开连接,过几秒又恢复连接,那么在这等待的几秒,客户端可以重新发起连接。
- 客户端wifi突然断了,也就是没网,然后过几秒又恢复了,客户端又可以重新发起连接了。
2、实现代码
代码主要实现细节:
枚举类型
Status
:
- 用于表示连接状态,包括
NEW
,CONNECTED
,CONNECTING
,DISCONNECTED
,CLOSE
。全局常量:
defaultsockfd
,默认套接字文件描述符为-1。
retryinterval
,重连间隔时间为1秒。
retryamxtimes
,重连最大次数为5次。
Connection
类:
构造函数初始化服务器IP、端口、状态、重连间隔和最大重连次数。
ConnectStatus()
方法返回当前连接状态。
Connect()
方法负责建立连接,并设置连接状态。
Process()
方法负责发送和接收数据,处理通信过程中的各种状态变化。
ReConnect()
方法处理重连逻辑,尝试在连接失败时进行多次重连。
DisConnect()
方法负责断开连接并重置套接字文件描述符。
TcpClient
类:
构造函数初始化连接对象。
Execute()
方法根据连接状态执行相应操作,包括建立连接、处理数据、重连和断开连接。
Usage()
函数:
- 用于输出使用说明。
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、为什么要断线重连 客户端会面临服务器崩溃的情况,我们可以试着写一个客户端重连的代码,模拟并理解一些客户端行为,比如游戏客户端等. 考虑到…...

细说MCU用DMA改变DAC输出信号频率和改善输出波形质量的方法
目录 一、参考硬件 二、修改定时器参数改变输出波形频率 三、改善波形质量 四、代码修改 五、查看结果 一、参考硬件 本项目的软件硬件工程参考作者的其他文章:细说MCU用DMA实现DAC输出的方法-CSDN博客 https://wenchm.blog.csdn.net/article/details/14065…...
Java高级面试题
文章目录 Java高级特性Java 8 中的新特性有哪些?Lambda 表达式的用途是什么?Stream API 的工作原理是什么?Optional 类的作用是什么?什么是函数式接口?Java 9 之后的新特性有哪些? 并发编程Java中的线程池是…...

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

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

vxe-table——实现切换页码时排序状态的回显问题(ant-design+elementUi中table排序不同时回显的bug)——js技能提升
之前写的后台管理系统,都是用的antdelement,table组件中的【排序】问题是有一定的缺陷的。 想要实现的效果: antv——table组件一次只支持一个参数的排序 如下图: 就算是可以自行将排序字段拼接到列表接口的入参中,…...

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

maven archetype
1.简介 maven脚手架是为了创建一个项目模板,以后新建项目都能够复用该模板 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子句不同,这不会对每个分区内的数据进行排序。 二、语法 DISTRIBUTE BY { expression [ , ... ] }三、参数 e…...

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

【NOI-题解】1009 - 数组逆序1162 - 数组元素的删除1211 - 数组元素的插入1161. 元素插入有序数组1159. 数组元素的移动
文章目录 一、前言二、问题问题:1009 - 数组逆序问题:1162 - 数组元素的删除问题:1211 - 数组元素的插入问题:1161. 元素插入有序数组问题:1159. 数组元素的移动 三、感谢 一、前言 本章节主要对数组问题中数组元素移…...
新电脑如何设置 npm 源及查看源、安装 cnpm、pnpm 和 yarn 的详细教程
当你获得一台新电脑,或需要在现有电脑上优化 JavaScript 和 Node.js 的开发环境时,正确配置 npm 是一步不可少的过程。本教程将详细指导你如何设置 npm 源,查看当前源,以及如何安装 cnpm、pnpm 和 yarn。 1. 设置 npm 源 npm (N…...

完全移动huggingface模型仓库(不是简单mv)
Linux中移动huggingface模型仓库 参考链接 先在bashrc中配置: export HF_DATASETS_CACHE"/your/path/dataset" export HF_HOME"/your/path/" export HUGGINGFACE_HUB_CACHE"/your/path/hub" export TRANSFORMERS_CACHE"/your…...

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

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

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

ubuntu 更新源
前言 实现一键替换在线源 一键更新源 ubuntu 全球镜像站以下支持现有ubuntu 20,22,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…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...

Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...