SOCKET编程(4):SOCKET实战

SOCKET实战

Writen()、Readn()函数

send()函数存在需要发送的字符数len小于函数返回的已发送的字符数(ssize_t)的问题

recv()函数存在需要接收的字符数len小于函数返回的已接收的字符数(ssize_t)的问题

解决上述问题通过Writen()Readn()函数实现

//buff是数据存储地址,n是待读取长度
bool Readn(const int sockfd, char *buffer, const size_t n)
{int nLeft, nread, idx;nLeft = n;idx = 0;while (nLeft > 0){if ((nread = recv(sockfd, buffer + idx, nLeft, 0)) <= 0)return false;idx += nread;nLeft -= nread;}return true;
}bool Writen(const int sockfd, const char *buffer, const size_t n)
{int nLeft, idx, nwritten;nLeft = n;idx = 0;while (nLeft > 0){if ((nwritten = send(sockfd, buffer + idx, nLeft, 0)) <= 0)return false;nLeft -= nwritten;idx += nwritten;}return true;
}

TcpWrite()、TcpRead()函数

在这里插入图片描述

💡 解决TCP报文分包和粘包的问题

bool TcpRead(const int sockfd, char *buffer, int *ibuflen, const int itimeout)
{if (sockfd == -1)return false;//连接超时if (itimeout > 0){fd_set tmpfd;FD_ZERO(&tmpfd);FD_SET(sockfd, &tmpfd);struct timeval timeout;timeout.tv_sec = itimeout;timeout.tv_usec = 0;int i;if ((i = select(sockfd + 1, &tmpfd, 0, 0, &timeout)) <= 0)return false;}(*ibuflen) = 0; //既然重新赋值为0,那么是否有参数传入的必要if (Readn(sockfd, (char *)ibuflen, 4) == false)return false;(*ibuflen) = ntohl(*ibuflen); //网络字节序转换为主机字节序if (Readn(sockfd, buffer, (*ibuflen)) == false)return false;return true;
}bool TcpWrite(const int sockfd, const char *buffer, const int ibuflen)
{if (sockfd == -1)return false;fd_set tmpfd;FD_ZERO(&tmpfd);FD_SET(sockfd, &tmpfd);struct timeval timeout;timeout.tv_sec = 5;timeout.tv_usec = 0;if (select(sockfd + 1, 0, &tmpfd, 0, &timeout) <= 0)return false;int ilen = 0;// ibuf==0默认为字符数组,否则要给定大小if (ibuflen == 0)ilen = strlen(buffer);elseilen = ibuflen;int ilenn = htonl(ilen); //主机字节序转换为网络字节序char strTBuffer[ilen + 4];memset(strTBuffer, 0, sizeof(strTBuffer));memcpy(strTBuffer, &ilenn, 4);  //用memcpy能够完成任何数据类型的拷贝memcpy(strTBuffer + 4, buffer, ilen);if (Writen(sockfd, strTBuffer, ilen + 4) == false)return false;return true;
}

多进程网络服务端框架

socket服务端封装为CTcpServer类

// socket通信的服务端类
class CTcpServer
{
private:int m_socklen;                    // 结构体struct sockaddr_in的大小。struct sockaddr_in m_clientaddr;  // 客户端的地址信息。struct sockaddr_in m_servaddr;    // 服务端的地址信息。
public:int  m_listenfd;   // 服务端用于监听的socket。int  m_connfd;     // 客户端连接上来的socket。bool m_btimeout;   // 调用Read和Write方法时,失败的原因是否是超时:true-超时,false-未超时。int  m_buflen;     // 调用Read方法后,接收到的报文的大小,单位:字节。CTcpServer();  // 构造函数。// 服务端初始化。// port:指定服务端用于监听的端口。// 返回值:true-成功;false-失败,一般情况下,只要port设置正确,没有被占用,初始化都会成功。bool InitServer(const unsigned int port); // 阻塞等待客户端的连接请求。// 返回值:true-有新的客户端已连接上来,false-失败,Accept被中断,如果Accept失败,可以重新Accept。bool Accept();// 获取客户端的ip地址。// 返回值:客户端的ip地址,如"192.168.1.100"。char *GetIP();// 接收客户端发送过来的数据。// buffer:接收数据缓冲区的地址,数据的长度存放在m_buflen成员变量中。// itimeout:等待数据的超时时间,单位:秒,缺省值是0-无限等待。// 返回值:true-成功;false-失败,失败有两种情况:1)等待超时,成员变量m_btimeout的值被设置为true;2)socket连接已不可用。bool Read(char *buffer,const int itimeout=0);// 向客户端发送数据。// buffer:待发送数据缓冲区的地址。// ibuflen:待发送数据的大小,单位:字节,缺省值为0,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。bool Write(const char *buffer,const int ibuflen=0);// 关闭监听的socket,即m_listenfd,常用于多进程服务程序的子进程代码中。void CloseListen();// 关闭客户端的socket,即m_connfd,常用于多进程服务程序的父进程代码中。void CloseClient();~CTcpServer();  // 析构函数自动关闭socket,释放资源。
};

socket客户端封装为CTcpClient类

// socket通信的客户端类
class CTcpClient
{
public:int  m_sockfd;    // 客户端的socket.char m_ip[21];    // 服务端的ip地址。int  m_port;      // 与服务端通信的端口。bool m_btimeout;  // 调用Read和Write方法时,失败的原因是否是超时:true-超时,false-未超时。int  m_buflen;    // 调用Read方法后,接收到的报文的大小,单位:字节。CTcpClient();  // 构造函数。// 向服务端发起连接请求。// ip:服务端的ip地址。// port:服务端监听的端口。// 返回值:true-成功;false-失败。bool ConnectToServer(const char *ip,const int port);// 接收服务端发送过来的数据。// buffer:接收数据缓冲区的地址,数据的长度存放在m_buflen成员变量中。// itimeout:等待数据的超时时间,单位:秒,缺省值是0-无限等待。// 返回值:true-成功;false-失败,失败有两种情况:1)等待超时,成员变量m_btimeout的值被设置为true;2)socket连接已不可用。bool Read(char *buffer,const int itimeout=0);// 向服务端发送数据。// buffer:待发送数据缓冲区的地址。// ibuflen:待发送数据的大小,单位:字节,缺省值为0,如果发送的是ascii字符串,ibuflen取0,如果是二进制流数据,ibuflen为二进制数据块的大小。// 返回值:true-成功;false-失败,如果失败,表示socket连接已不可用。bool Write(const char *buffer,const int ibuflen=0);// 断开与服务端的连接void Close();~CTcpClient();  // 析构函数自动关闭socket,释放资源。
};

mpserver.cpp

  • mpserver.cpp

    /**  程序名:mpserver.cpp,此程序演示采用freecplus框架的CTcpServer类实现socket通信多进程的服务端。*  作者:C语言技术网(www.freecplus.net) 日期:20190525
    */
    #include "_freecplus.h"CLogFile logfile;       // 服务程序的运行日志。
    CTcpServer TcpServer;   // 创建服务端对象。// 程序退出时调用的函数
    void FathEXIT(int sig);   // 父进程退出函数。
    void ChldEXIT(int sig);   // 子进程退出函数。int main(int argc,char *argv[])
    {if (argc!=3){printf("Using:./mpserver port logfile\nExample:./mpserver 5005 /tmp/mpserver.log\n\n"); return -1;}// 关闭全部的信号for (int ii=0;ii<100;ii++) signal(ii,SIG_IGN);// 打开日志文件。if (logfile.Open(argv[2],"a+")==false) { printf("logfile.Open(%s) failed.\n",argv[2]); return -1;}// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程// 但请不要用 "kill -9 +进程号" 强行终止signal(SIGINT,FathEXIT); signal(SIGTERM,FathEXIT);if (TcpServer.InitServer(atoi(argv[1]))==false) // 初始化TcpServer的通信端口。{logfile.Write("TcpServer.InitServer(%s) failed.\n",argv[1]); FathEXIT(-1);}while (true){if (TcpServer.Accept()==false)   // 等待客户端连接。{logfile.Write("TcpServer.Accept() failed.\n"); continue;}if (fork()>0) { TcpServer.CloseClient(); continue; } // 父进程返回到循环首部。// 子进程重新设置退出信号。signal(SIGINT,ChldEXIT); signal(SIGTERM,ChldEXIT);TcpServer.CloseListen();// 以下是子进程,负责与客户端通信。logfile.Write("客户端(%s)已连接。\n",TcpServer.GetIP());char strbuffer[1024];  // 存放数据的缓冲区。while (true){memset(strbuffer,0,sizeof(strbuffer));if (TcpServer.Read(strbuffer,50)==false) break; // 接收客户端发过来的请求报文。logfile.Write("接收:%s\n",strbuffer);strcat(strbuffer,"ok");      // 在客户端的报文后加上"ok"。logfile.Write("发送:%s\n",strbuffer);if (TcpServer.Write(strbuffer)==false) break;     // 向客户端回应报文。}logfile.Write("客户端已断开。\n");    // 程序直接退出,析构函数会释放资源。ChldEXIT(-1);  // 通信完成后,子进程退出。}
    }// 父进程退出时调用的函数
    void FathEXIT(int sig)
    {if (sig > 0){signal(sig,SIG_IGN); signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);}kill(0,15);  // 通知其它的子进程退出。logfile.Write("父进程退出。\n");// 编写善后代码(释放资源、提交或回滚事务)TcpServer.CloseClient();exit(0);
    }// 子进程退出时调用的函数
    void ChldEXIT(int sig)
    {if (sig > 0){signal(sig,SIG_IGN); signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);}logfile.Write("子进程退出。\n");// 编写善后代码(释放资源、提交或回滚事务)TcpServer.CloseClient();exit(0);
    }
    
  • 利用fork()函数创建子进程,父进程负责监听工作,子进程负责与客户端的连接

  • 父进程关闭connectfd,子进程关闭listenfd,避免占用系统资源

if (TcpServer.Accept() == false) // 等待客户端连接。
{logfile.Write("TcpServer.Accept() failed.\n");continue;
}if (fork() > 0)
{TcpServer.CloseClient();continue;
} // 父进程返回到循环首部。// 子进程重新设置退出信号。
signal(SIGINT, ChldEXIT);
signal(SIGTERM, ChldEXIT);TcpServer.CloseListen();

💡 考虑用sigaction()函数代替signal()函数进行优化

避免僵尸进程

在这里插入图片描述

  1. 忽略子进程的退出信号
  2. wait()函数阻塞式等待,影响主进程工作,可利用waitpid()函数实现无阻塞等待
// 关闭全部的信号
for (int ii = 0; ii < 100; ii++)
{signal(ii, SIG_IGN);
}

💡 关闭全部信号意味着同时忽略子进程的退出信号

多进程服务程序的退出和资源释放

  • 实际开发中在终端按Ctrl+C退出是不专业的
  • 信号来通知和实现进程的退出
  • 一般而言父进程退出则子进程也退出,而子进程单独退出则不受影响
// 父进程退出时调用的函数
void FathEXIT(int sig)
{if (sig > 0){signal(sig,SIG_IGN); signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);logfile.Write("catching the signal(%d).\n",sig);}kill(0,15);  // 通知其它的子进程退出。logfile.Write("父进程退出。\n");// 编写善后代码(释放资源、提交或回滚事务)TcpServer.CloseClient();exit(0);
}// 子进程退出时调用的函数
void ChldEXIT(int sig)
{if (sig > 0){signal(sig,SIG_IGN); signal(SIGINT,SIG_IGN); signal(SIGTERM,SIG_IGN);}logfile.Write("子进程退出。\n");// 编写善后代码(释放资源、提交或回滚事务)TcpServer.CloseClient();exit(0);
}

TCP短连接与长连接

client与server建立连接进行通信,通信完成后释放连接,建立连接时需要3次握手,释放连接需要4次挥手,连接的建立和释放都需要时间,server还有创建新进程或线程的开销

短连接:

client/server间只进行一次或连续多次通信,通信完成后马上断开了。管理起来比较简单,不需要额外的控制手段

长连接:

client/serverl间需要多次通信,通信的频率和次数不确定,所以client和server需要保持这个连接

增加心跳机制

心跳机制是针对长连接而言的

如果client与server采用长连接,在连接空闲时,client每若干秒向server发送一个心跳报文,server也回复一个心跳报文,确认连接继续生效中
如果server在约定的时间内没有收到client的任何报文,则认为客户端已掉线,就主动断开连接,释放资源

在这里插入图片描述

💡 心跳报文建议在60秒以内,不要超过120秒
可以将心跳报文的发送放在子线程

多线程网络服务端框架

mtserver_biz.cpp

  • mtserver_biz.cpp

    /**  程序名:mtserver_biz.cpp,此程序演示采用freecplus框架的CTcpServer类实现socket通信多线程的服务端。*  作者:C语言技术网(www.freecplus.net) 日期:20190525
    */
    #include "_freecplus.h"void *pthmain(void *arg);  // 线程主函数。vector<long> vpthid;  // 存放线程id的容器。void mainexit(int sig);   // 信号2和15的处理函数。void pthmainexit(void *arg); // 线程清理函数。CLogFile logfile;       // 服务程序的运行日志。
    CTcpServer TcpServer;   // 创建服务端对象。// 处理业务的主函数。
    bool _main(const char *strrecvbuffer,char *strsendbuffer);// 心跳报文。
    bool biz000(const char *strrecvbuffer,char *strsendbuffer);// 身份验证业务处理函数。
    bool biz001(const char *strrecvbuffer,char *strsendbuffer);// 查询余客业务处理函数。
    bool biz002(const char *strrecvbuffer,char *strsendbuffer);int main(int argc,char *argv[])
    {if (argc!=3){printf("Using:./mtserver_biz port logfile\nExample:./mtserver_biz 5005 /tmp/mtserver_biz.log\n\n"); return -1;}// 关闭全部的信号for (int ii=0;ii<100;ii++) signal(ii,SIG_IGN);// 打开日志文件。if (logfile.Open(argv[2],"a+")==false) { printf("logfile.Open(%s) failed.\n",argv[2]); return -1;}// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程// 但请不要用 "kill -9 +进程号" 强行终止signal(SIGINT,mainexit); signal(SIGTERM,mainexit);if (TcpServer.InitServer(atoi(argv[1]))==false) // 初始化TcpServer的通信端口。{logfile.Write("TcpServer.InitServer(%s) failed.\n",argv[1]); return -1;}while (true){if (TcpServer.Accept()==false)   // 等待客户端连接。{logfile.Write("TcpServer.Accept() failed.\n"); continue;}logfile.Write("客户端(%s)已连接。\n",TcpServer.GetIP());pthread_t pthid;if (pthread_create(&pthid,NULL,pthmain,(void *)(long)TcpServer.m_connfd)!=0){ logfile.Write("pthread_create failed.\n"); return -1; }vpthid.push_back(pthid); // 把线程id保存到vpthid容器中。}return 0;
    }void *pthmain(void *arg)
    {pthread_cleanup_push(pthmainexit,arg);  // 设置线程清理函数。pthread_detach(pthread_self());  // 分离线程。pthread_setcanceltype(PTHREAD_CANCEL_DISABLE,NULL);  // 设置取消方式为立即取消。int sockfd=(int)(long)arg;  // 与客户端的socket连接。int  ibuflen=0;char strrecvbuffer[1024],strsendbuffer[1024];  // 存放数据的缓冲区。while (true){memset(strrecvbuffer,0,sizeof(strrecvbuffer));memset(strsendbuffer,0,sizeof(strsendbuffer));if (TcpRead(sockfd,strrecvbuffer,&ibuflen,50)==false) break; // 接收客户端发过来的请求报文。logfile.Write("接收:%s\n",strrecvbuffer);// 处理业务的主函数。if (_main(strrecvbuffer,strsendbuffer)==false) break;logfile.Write("发送:%s\n",strsendbuffer);if (TcpWrite(sockfd,strsendbuffer)==false) break;     // 向客户端回应报文。}pthread_cleanup_pop(1);pthread_exit(0);
    }// 信号2和15的处理函数。
    void mainexit(int sig)
    {logfile.Write("mainexit begin.\n");// 关闭监听的socket。TcpServer.CloseListen();// 取消全部的线程。for (int ii=0;ii<vpthid.size();ii++){logfile.Write("cancel %ld\n",vpthid[ii]);pthread_cancel(vpthid[ii]);}logfile.Write("mainexit end.\n");exit(0);
    }// 线程清理函数。
    void pthmainexit(void *arg)
    {logfile.Write("pthmainexit begin.\n");// 关闭与客户端的socket。close((int)(long)arg);// 从vpthid中删除本线程的id。for (int ii=0;ii<vpthid.size();ii++){if (vpthid[ii]==pthread_self()){vpthid.erase(vpthid.begin()+ii);}}logfile.Write("pthmainexit end.\n");
    }bool _main(const char *strrecvbuffer,char *strsendbuffer)  // 处理业务的主函数。
    {int ibizcode=-1;GetXMLBuffer(strrecvbuffer,"bizcode",&ibizcode);switch (ibizcode){case 0:  // 心跳biz000(strrecvbuffer,strsendbuffer); break;case 1:  // 身份验证。biz001(strrecvbuffer,strsendbuffer); break;case 2:  // 查询余额。biz002(strrecvbuffer,strsendbuffer); break;default:logfile.Write("非法报文:%s\n",strrecvbuffer); return false;}return true;
    }// 身份验证业务处理函数。
    bool biz001(const char *strrecvbuffer,char *strsendbuffer)
    {char username[51],password[51];memset(username,0,sizeof(username));memset(password,0,sizeof(password));GetXMLBuffer(strrecvbuffer,"username",username,50);GetXMLBuffer(strrecvbuffer,"password",password,50);if ( (strcmp(username,"wucz")==0) && (strcmp(password,"p@ssw0rd")==0) )sprintf(strsendbuffer,"<retcode>0</retcode><message>成功。</message>");elsesprintf(strsendbuffer,"<retcode>-1</retcode><message>用户名或密码不正确。</message>");return true;
    }// 查询余额业务处理函数。
    bool biz002(const char *strrecvbuffer,char *strsendbuffer)
    {char cardid[51];memset(cardid,0,sizeof(cardid));GetXMLBuffer(strrecvbuffer,"cardid",cardid,50);if (strcmp(cardid,"62620000000001")==0)sprintf(strsendbuffer,"<retcode>0</retcode><message>成功。</message><ye>100.50</ye>");elsesprintf(strsendbuffer,"<retcode>-1</retcode><message>卡号不存在。</message>");return true;
    }// 心跳报文
    bool biz000(const char *strrecvbuffer,char *strsendbuffer)
    {sprintf(strsendbuffer,"<retcode>0</retcode><message>成功。</message>");return true;
    }
    

💡 考虑用thread代替pthread实现多线程

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

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

相关文章

资源管理游戏模版进入The Sandbox

我们非常高兴地向您介绍 Game Maker 的最新模板&#xff1a;资源管理游戏&#xff01; 这一全新的模板让您能够深入身临其境的游戏体验中&#xff0c;同时掌握令人兴奋的新机制。通过揭开模板的神秘面纱&#xff0c;您可以锤炼您的游戏设计技能。 什么是资源管理游戏&#xff1…

手机电脑通用便签推荐 好用便签下载

便签软件作为一种日常记录和管理工具&#xff0c;其实用性和便捷性深受用户喜爱。一款优秀的便签软件不仅能帮助我们随时随地记录重要信息&#xff0c;还能有效提高工作效率。然而&#xff0c;市场上很多便签应用仅限于单一平台使用&#xff0c;对于需要在手机和电脑间频繁切换…

条件平差——以水准网平差为例 (python详细过程版)

目录 一、原理概述二、案例分析三、代码实现四、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、原理概述 条件平差的函数模型和随机模型为: A V + W = 0

已解决【nvidia-smi】Failed to initialize NVML: Driver/library version mismatch解决方法

本文摘要&#xff1a;【nvidia-smi】Failed to initialize NVML: Driver/library version mismatch解决方法。 &#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专…

服务器内存占用不足会怎么样,解决方案

在当今数据驱动的时代&#xff0c;服务器对于我们的工作和生活起着举足轻重的作用。而在众多影响服务器性能的关键因素当中&#xff0c;内存扮演着极其重要的角色。 服务器内存&#xff0c;也称RAM&#xff08;Random Access Memory&#xff09;&#xff0c;是服务器核心硬件部…

做视频号小店,怎么找达人合作?这里有详细讲解

大家好&#xff0c;我是电商笨笨熊 做视频号小店是没有自然流量的&#xff0c;这点刚入驻的新玩家还不清楚&#xff1b; 因此很多老电商玩家们还想着继续拿其他平台动销自然流的玩法去做视频号&#xff1b; 只能说这种方式在视频号是完全行不通的&#xff0c;当下想要推广售…

高效电源测试设备助力自动化测试和数据分析

在当今电子产品的研发和生产过程中&#xff0c;电源测试设备的重要性不言而喻。一款优秀的电源测试设备能够显著提升测试效率&#xff0c;确保电源模块的性能达到设计要求。 纳米软件NSAT-8000电源测试系统是一款自动化电源测试设备&#xff0c;在测试电源模块时&#xff0c;通…

C++笔试强训day16

目录 1.字符串替换 2.神奇数 3.DNA序列 1.字符串替换 链接 简单的遍历替换即可&#xff1a; class Solution { public:string formatString(string str, vector<char>& arg) {string ret;int k 0;for (int i 0; i < str.size(); i){if (str[i] %){ret arg…

2023年全国职业院校技能大赛(高职组)“云计算应用”赛项赛卷3(私有云)

#需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包及镜像&#xff09;或有问题的&#xff0c;可私聊博主&#xff01;&#xff01;&#xff01; #需要资源&#xff08;软件包…

Ecoinvent中system model中的 cutoff、consequential、apos的区别:

一&#xff1a;解释 Cutoff (截断)模型:只考虑主要的生产过程,不考虑副产品的进一步影响。比如说牛奶厂在生产牛奶的同时会产生奶酪,cutoff模型只考虑牛奶的影响,不考虑奶酪的后续利用。 Consequential (后果)模型:考虑某个决策或变化会引起的一系列连锁反应和影响。比如说我们…

【Pip】pip 安装第三方包异常:[SSL:CERTIFICATE_VERIFY_FAILED]解决方案

pip 安装第三方包异常:[SSL:CERTIFICATE_VERIFY_FAILED] 大家好 我是寸铁&#x1f44a; 总结了一篇pip 安装第三方包异常:[SSL:CERTIFICATE_VERIFY_FAILED]✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 报错 今天在安装第三方包时报错如下: 解决方案 本质上是需要指定信任的镜像…

云效 Pipeline as Code 来了!这些场景,用好它效率翻倍!

从可视化编排到支持 YAML 编排 云效流水线 Flow 是开箱即用的企业级持续集成和持续交付工具&#xff0c;支持丰富的代码源、构建、自动化测试工具、多种部署类型和部署方式&#xff0c;与阿里云深度集成&#xff0c;还提供多种企业级特性&#xff0c;助力企业高效完成从开发到…

itextpdf 7生成pdf(主要是文字和表格,支持中文)

我们经常会遇到要导出pdf的需求,方式有很多种 今天的教程是采用itextpdf的方式生成pdf itextpdf是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF文档&#xff0c;而且可以将Html文件转化为PDF文件。 这里先展示一下效果图 首先在pom.xml中引入相关依赖 <dep…

Vue3 - 前端项目代码防止被调试/被爬,阻止浏览器F12开发者工具

项目背景 大家都知道浏览器的开发者工具能干啥&#xff0c;正经的用法&#xff1a;开发时调试代码逻辑&#xff0c;修改布局样式&#xff1b;不正经的用法&#xff1a;改改元素骗骗人&#xff0c;找找网站接口写爬虫&#xff0c;逆向js破解加密等等&#xff0c;所以说前端不安…

怎么制作流程图?介绍制作方法

怎么制作流程图&#xff1f;在日常生活和工作中&#xff0c;流程图已经成为我们不可或缺的工具。无论是项目规划、流程优化&#xff0c;还是学习理解复杂系统&#xff0c;流程图都能帮助我们更直观地理解和表达信息。然而&#xff0c;很多人可能并不清楚&#xff0c;其实制作流…

进程间通信(一)

IPC 在之前我们也有涉及到进程间通信的知识点&#xff0c;比如fork或exec或父进程读取子进程的退出码等&#xff0c;但是这种通信方式很有限&#xff0c;今天来学习进程间通信的其他技术——IPC&#xff08;InterProcess Communication&#xff09;。 IPC的方式通常有管道&…

Windows 10 Manager (Win10优化工具),中文破姐版 v3.9.3

01 软件介绍 Windows 10 Manager是一款为Win10操作系统设计的综合优化工具。包含逾40种不同的功能模块&#xff0c;旨在全方位地提升系统性能。其核心效用体现在对Win10的优化、调整、清理、加速和修复方面。能够显著提高系统的运行速度&#xff0c;并有效地排查及解决系统问题…

无管理员权限linux系统手动切换cuda版本

查看当前计算机cudatoolkit的版本 nvcc -V下载 如果想切换到指定版本&#xff0c;则去官网下载&#xff08;10.1版本为例&#xff09;&#xff1a; cuda下载 cudnn下载 将下载好的文件放到服务器的文件夹中 安装 安装cuda sh cuda_10.1.243_418.87.00_linux.run选择接受 …

Python中的数据可视化:阶梯图matplotlib.pyplot.step()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 Python中的数据可视化&#xff1a; 阶梯图 matplotlib.pyplot.step() [太阳]选择题 matplotlib.pyplot.step()的功能是&#xff1f; import matplotlib.pyplot as plt import numpy as…

【向信而行 笃行致远】2024·C3合作伙伴大会即将启幕

向信而行&#xff0c;笃行致远。2024C3合作伙伴大会将于5月17日在南京拉开帷幕。 数字机遇&#xff0c;智能变革。在数智化的澎湃浪潮中&#xff0c;唯有聚合众力&#xff0c;方能乘风破浪&#xff0c;驶向更远的未来。 2023年&#xff0c;我们直面企业数字转型的需求与挑战&…