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

TCP客户端服务器端通信(线程池版)

1、什么是监听套接字,和UDP相比,TCP为什么文件描述符变多了?

       在网络编程中,TCP和UDP是两种常见的传输协议,它们之间最大的不同之一在于连接的管理方式。为了更好地理解这个区别,我们可以用一个生动的比喻来说明。

        假设你在一家餐馆工作,其中张三负责把客人带到座位上,而李四和王五是服务员,他们负责为客人点菜和服务。张三的角色类似于TCP连接中的监听套接字。他的工作是确保当客户到达时,他们能够被快速、有效地分配给一个合适的服务员。

        在TCP协议中,监听套接字(就像张三)是独特的:它负责创建监听端口,通过这个端口,服务器可以接收到请求。当有新的连接请求时,该监听套接字的角色是“接客”,将请求转交给一个新的文件描述符(类似李四或王五),这个新的文件描述符负责具体的数据传输和处理。因此,虽然张三(监听套接字)数量是固定的,但却需要多个“服务员”来处理多个客户连接,即多个文件描述符。相比之下,UDP协议不需要这种复杂的连接管理机制,因为它是无连接的,不需要维护任何长时间的连接状态,因此它的文件描述符相对少。

2、如果现在还没写客户端,只写了服务器端,怎么测试当前服务器通信的时候会有别人来连我呢?工具:telnet 127.0.0.1 8888(底层默认tcp)

当你正在开发一个服务器应用程序,但还没有编写客户端时,你可能会想测试服务器的连接和通信功能。这时,我们可以使用一个简单的工具——telnet。假设你的服务器正在本地计算机上运行,并监听8888端口,你可以在命令行中输入以下命令来测试连接:

要检查当前活跃的网络连接,可以使用以下命令:

netstat -nltp 

服务器端代码写好了,直接用工具来测试。

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"
#include <string>
#include <arpa/inet.h>
#include <cstring>
#include <netinet/in.h>const int defaultfd=-1;
const std::string defaultip="0.0.0.0";
const int backlog=10;//一般不要设置的太大,后面解释
Log lg;
enum{SOCKET_ERR=2,BIND_ERR,Listen_ERR
};
class TcpServer
{
public:TcpServer(const uint16_t &port,const std::string &ip=defaultip):listensock_(defaultfd),port_(port),ip_(ip){};void InitServer(){listensock_=socket(AF_INET,SOCK_STREAM,0);if(listensock_<0){lg(Fatal,"errno:%d,errrstring:%s",errno,strerror(errno));exit(SOCKET_ERR);}lg(Info,"create socket success,listensock_:%d",listensock_);struct sockaddr_in local; //服务器信息memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(port_); //用户在构建服务器时就需要告诉我inet_aton(ip_.c_str(),&(local.sin_addr));local.sin_addr.s_addr=INADDR_ANY; //或者写成0if(bind(listensock_,(const sockaddr*)&local,sizeof(local))<0){lg(Fatal,"bind error,errno:%d,errrstring:%s",errno,strerror(errno));exit(BIND_ERR);}lg(Info,"bind success");//和UDP不一样的地方,设为监听状态 Tcp是面向链接的,服务器一般是比较被动的。服务器一直处于一种,一直在等待连接到来的状态if(listen(listensock_,backlog)<0){lg(Fatal,"errno:%d,errrstring:%s",errno,strerror(errno));exit(Listen_ERR);}}void Start(){lg(Info, "tcpserver is running....");while (true){// 1.获取新连接struct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);if (sockfd < 0){lg(Warning, "errno:%d,errrstring:%s", errno, strerror(errno));//获取连接失败,就直接再去获取continue;}//到底是谁连接的我uint16_t clientport=ntohs(client.sin_port); //网络转主机char clientip[32];std::string ip=inet_ntop(AF_INET,&(client.sin_addr),clientip,sizeof(clientip));//把客户端的ip存到ipstr中//2.根据新连接进行通信// lg(Info,"get a new Link...,sockfd:%d,client ip:%s,client port:%d",sockfd,clientip,clientport);//version 1Service(sockfd,ip,clientport);//拿到客户端的端口和ip}}void Service(int sockfd,const std::string &clientip,uint16_t &clientport){char buffer[4096];//测试代码:你给我发什么我就给你响应什么while(true){ssize_t n=read(sockfd,buffer,sizeof(buffer));if(n>0){buffer[n]=0;std::cout<<"client say#"<<buffer<<std::endl;std::string echo_string="tpserver echo#";echo_string+=buffer;write(sockfd,echo_string.c_str(),echo_string.size()); //再写回去}}}~TcpServer(){}; 
private:int listensock_;uint16_t port_;std::string ip_;
};

测试:ctrl+] 进入    ctrl+] quit退出

 

发现客户端发的aaaaa,服务器端收到了。

问题1:要是客户端退出,服务器会怎么办?

服务器会读到0,服务中止,服务器端也直接退出

问题2:服务器向一个不存在的fd写入

signal(SIGPIPE,SIG_IGN);//防止出现写入的时候,向一个已经关闭的文件描述符写入的时候,此时连接没有意义,这时写时,os直接SIGPIPE掉

3、代码

TcpServer.hpp#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"
#include <string>
#include <arpa/inet.h>
#include <cstring>
#include <netinet/in.h>
#include <unistd.h>
#include <signal.h>
#include<sys/wait.h>
#include<pthread.h>
#include "ThreadPool.hpp"
#include"Task.hpp"
#include<signal.h>
const int defaultfd=-1;
const std::string defaultip="0.0.0.0";
const int backlog=10;//一般不要设置的太大,后面解释
Log lg;
enum{SOCKET_ERR=2,BIND_ERR,Listen_ERR
};
class TcpServer;
class ThreadData//把TcpServer本身传进来
{
public:ThreadData(int fd,const std::string &ip,const uint16_t &port,TcpServer *t):sockfd(fd),clientip(ip),clientport(port),tsvr(t){}
public:int sockfd;std::string clientip;uint16_t clientport;TcpServer *tsvr;
};
class TcpServer
{
public:TcpServer(const uint16_t &port,const std::string &ip=defaultip):listensock_(defaultfd),port_(port),ip_(ip){};void InitServer(){listensock_=socket(AF_INET,SOCK_STREAM,0);if(listensock_<0){lg(Fatal,"errno:%d,errrstring:%s",errno,strerror(errno));exit(SOCKET_ERR);}lg(Info,"create socket success,listensock_:%d",listensock_);struct sockaddr_in local; //服务器信息memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(port_); //用户在构建服务器时就需要告诉我inet_aton(ip_.c_str(),&(local.sin_addr));local.sin_addr.s_addr=INADDR_ANY; //或者写成0if(bind(listensock_,(const sockaddr*)&local,sizeof(local))<0){lg(Fatal,"bind error,errno:%d,errrstring:%s",errno,strerror(errno));exit(BIND_ERR);}lg(Info,"bind success");//和UDP不一样的地方,设为监听状态 Tcp是面向链接的,服务器一般是比较被动的。服务器一直处于一种,一直在等待连接到来的状态if(listen(listensock_,backlog)<0){lg(Fatal,"errno:%d,errrstring:%s",errno,strerror(errno));exit(Listen_ERR);}}// void Service(int sockfd,const std::string &clientip,uint16_t &clientport)// {//     char buffer[4096];//     //测试代码:你给我发什么我就给你响应什么//     while(true)//     {//         ssize_t n=read(sockfd,buffer,sizeof(buffer));//         if(n>0)//         {//             buffer[n]=0;//             std::cout<<"client say#"<<buffer<<std::endl;//             std::string echo_string="tpserver echo#";//             echo_string+=buffer;//             write(sockfd,echo_string.c_str(),echo_string.size()); //再写回去//         }//         else if(n==0)//         {//             //lg(Info,"%s:%d quit,server close sockfd:%d",clientip,clientport,sockfd);//             break;//         }//         else//         {//             lg(Warning,"read Error...,sockfd:%d,client ip:%s,client port:%d",sockfd,clientip.c_str(),clientport);//         }//     }// }// static void* routine(void *args)//静态函数,把this传进来// {//     pthread_detach(pthread_self());//把自己设置为分离状态,主线程一直再获取新连接,创建出线程就不管了,让新线程进行任务处理//     ThreadData *td=static_cast<ThreadData *>(args);//     td->tsvr->Service(td->sockfd,td->clientip,td->clientport);//由该线程提供服务//     delete td;//提供完服务,申请的堆空间释放掉//     return nullptr;// }void Start(){signal(SIGPIPE,SIG_IGN);//防止出现写入的时候,向一个已经关闭的文件描述符写入的时候,此时连接没有意义,这时写时,os直接SIGPIPE掉ThreadPool<Task>::GetInstance()->Start();//启动线程池lg(Info, "tcpserver is running....");while (true){// 1.获取新连接struct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);if (sockfd < 0){lg(Warning, "errno:%d,errrstring:%s", errno, strerror(errno));//获取连接失败,就直接再去获取continue;}//到底是谁连接的我uint16_t clientport=ntohs(client.sin_port); //网络转主机char clientip[32];inet_ntop(AF_INET,&(client.sin_addr),clientip,sizeof(clientip));//把客户端的ip存到ipstr中//2.根据新连接进行通信//lg(Info,"get a new Link...,sockfd:%d,client ip:%s,client port:%d",sockfd,ip,clientport);//version 1// Service(sockfd,ip,clientport);//拿到客户端的端口和ip// close(sockfd);//version 2 --多进程版,因为创建一个进程成本太高了// pid_t id=fork();// if(id==0)// {//     close(listensock_);//     if(fork()>0) exit(0);//     //子进程 什么都可以看到//     Service(sockfd,ip,clientport);//孙子进程,wait立马返回,父进程和孙子进程并发访问,不用管孙子,不用等孙子,儿子已经挂了,孙子会被系统领养//     close(sockfd);//     exit(0);// }// close(sockfd);// //父进程继续获取新连接 打开文件描述符,打开之后交给子进程,自己就关掉了,如果不关,系统中会有非常多的文件没有关闭// pid_t rid=waitpid(id,nullptr,0);// (void)rid;//version 3 多线程版本// ThreadData *td=new ThreadData(sockfd,ip,clientport,this);//this把当前对象传进来// pthread_t tid;// pthread_create(&tid,nullptr,routine,td);//version 4 线程池版Task t(sockfd,clientip,clientport);ThreadPool<Task>::GetInstance()->Push(t);}}~TcpServer(){}; 
private:int listensock_;uint16_t port_;std::string ip_;
};
ThreadPool.hpp
#pragma once#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <pthread.h>
#include <unistd.h>struct ThreadInfo
{pthread_t tid;std::string name;
};static const int defalutnum = 10;template <class T>
class ThreadPool
{
public:void Lock(){pthread_mutex_lock(&mutex_);}void Unlock(){pthread_mutex_unlock(&mutex_);}void Wakeup(){pthread_cond_signal(&cond_);}void ThreadSleep(){pthread_cond_wait(&cond_, &mutex_);}bool IsQueueEmpty(){return tasks_.empty();}std::string GetThreadName(pthread_t tid){for (const auto &ti : threads_){if (ti.tid == tid)return ti.name;}return "None";}public:static void *HandlerTask(void *args){ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);std::string name = tp->GetThreadName(pthread_self());while (true){tp->Lock();while (tp->IsQueueEmpty()){tp->ThreadSleep();}T t = tp->Pop();tp->Unlock();t();}}void Start(){int num = threads_.size();for (int i = 0; i < num; i++){threads_[i].name = "thread-" + std::to_string(i + 1);pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);}}T Pop(){T t = tasks_.front();tasks_.pop();return t;}void Push(const T &t){Lock();tasks_.push(t);Wakeup();Unlock();}static ThreadPool<T> *GetInstance(){if (nullptr == tp_) // ???{pthread_mutex_lock(&lock_);if (nullptr == tp_){std::cout << "log: singleton create done first!" << std::endl;tp_ = new ThreadPool<T>();}pthread_mutex_unlock(&lock_);}return tp_;}private:ThreadPool(int num = defalutnum) : threads_(num){pthread_mutex_init(&mutex_, nullptr);pthread_cond_init(&cond_, nullptr);}~ThreadPool(){pthread_mutex_destroy(&mutex_);pthread_cond_destroy(&cond_);}ThreadPool(const ThreadPool<T> &) = delete;const ThreadPool<T> &operator=(const ThreadPool<T> &) = delete; // a=b=c
private:std::vector<ThreadInfo> threads_;std::queue<T> tasks_;pthread_mutex_t mutex_;pthread_cond_t cond_;static ThreadPool<T> *tp_;static pthread_mutex_t lock_;
};template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;
TcpClient.cc
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
//./tcpclient serverip serverport
void Usage(const std::string &proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}
int main(int argc,char* argv[])
{if(argc!=3){Usage(argv[0]);exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr)); // 字符串转为in_addrwhile (true)                                              // 每次翻译的时候都要重新建立连接,因为服务器每次只给我提供一次服务{int cnt = 5;int isreconnect = false;int sockfd = 0;sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;return 1;}do{// tcp客户端要不要bind,要不要显示的bind?// 客户端需要绑定,不需要显示的绑定,将来再通信时需要端口号和ip标识自己的唯一性,当发出消息的时候,服务器才能把消息转过来,但是对于客户端来说// 端口号具体是几不重要,唯一就行,由os随机选择,根据你的需要随机选中,UDP首次发送数据的时候// 客户端向服务器发起连接,TCP中客户端发起connect时,进行自动随机bind,这个函数最后几个参数是要知道服务器的ipint n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){std::cerr << "connect error" << std::endl;isreconnect=true;cnt--;return 2;}else{break;}}while(cnt&&isreconnect);if(cnt==0){std::cerr<<"user offline..."<<std::endl;break;}//上面的while循环是连接,下面的while是提供服务// while(true)// {std::string message;// 连接成功,可以直接发消息std::cout << "Please enter@" << std::endl;std::getline(std::cin, message);int n = write(sockfd, message.c_str(), message.size());if (n < 0){std::cerr << "write error" << std::endl;break;}// 收到服务器消息char inbuffer[4096];n = read(sockfd, inbuffer, sizeof(inbuffer));if (n > 0){inbuffer[n] = 0;std::cout << inbuffer << std::endl;break;}close(sockfd);}// }return 0;
}
Main.cc
#include"TcpServer.hpp"
#include<iostream>
#include<memory>
#include <pthread.h>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
};
//./tcpserver 8080
int main(int argc,char** argv)
{if(argc!=2){Usage(argv[0]);exit(1);}uint16_t port=std::stoi(argv[1]);std::unique_ptr<TcpServer>tcp_svr(new TcpServer(port));tcp_svr->InitServer();tcp_svr->Start();return 0;
}
Task.hpp
#pragma once
#include <iostream>
#include <string>
#include<unistd.h>
#include <unordered_map>
#include"Log.hpp"
#include "Init.hpp"
extern Log lg;
Init init;
class Task
{
public:Task(int sockfd, const std::string &clientip, uint16_t &clientport) : sockfd_(sockfd), clientip_(clientip), clientport_(clientport){}void run(){char buffer[4096];ssize_t n = read(sockfd_, buffer, sizeof(buffer));if (n > 0){buffer[n] = 0;std::cout << "client key#" << buffer << std::endl;std::string echo_string=init.translation(buffer);write(sockfd_, echo_string.c_str(), echo_string.size()); // 再写回去if(n<0){lg(Warning,"write error,errno:%d,errstring:%s",errno,strerror(errno));}}else if (n == 0){// lg(Info,"%s:%d quit,server close sockfd:%d",clientip,clientport,sockfd);//break;}else{lg(Warning, "read Error...,sockfd:%d,client ip:%s,client port:%d", sockfd_, clientip_.c_str(), clientport_);}//只处理一次close(sockfd_);}void operator ()(){run();}~Task(){}private:int sockfd_;std::string clientip_;uint16_t clientport_;
};
Log.hpp
#pragma once#include <iostream>
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>#define SIZE 1024#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4#define Screen 1
#define Onefile 2
#define Classfile 3#define LogFile "log.txt"class Log
{
public:Log(){printMethod = Screen;path = "./log/";}void Enable(int method){printMethod = method;}std::string levelToString(int level){switch (level){case Info:return "Info";case Debug:return "Debug";case Warning:return "Warning";case Error:return "Error";case Fatal:return "Fatal";default:return "None";}}void printLog(int level, const std::string &logtxt){switch (printMethod){case Screen:std::cout << logtxt << std::endl;break;case Onefile:printOneFile(LogFile, logtxt);break;case Classfile:printClassFile(level, logtxt);break;default:break;}}void printOneFile(const std::string &logname, const std::string &logtxt){std::string _logname = path + logname;int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666); // "log.txt"if (fd < 0)return;write(fd, logtxt.c_str(), logtxt.size());close(fd);}void printClassFile(int level, const std::string &logtxt){std::string filename = LogFile;filename += ".";filename += levelToString(level); // "log.txt.Debug/Warning/Fatal"printOneFile(filename, logtxt);}~Log(){}void operator()(int level, const char *format, ...){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 格式:默认部分+自定义部分char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);// printf("%s", logtxt); // 暂时打印printLog(level, logtxt);}private:int printMethod;std::string path;
};
Init.hpp
#pragma once
#include<iostream>
#include<string>
#include <unordered_map>
#include <fstream>
#include "Log.hpp"
extern Log lg;
const std::string dictname="./dict.txt";
const std::string sep=":";
//apple:苹果
static bool Split(std::string &s,std::string *part1,std::string *part2)
{auto pos=s.find(sep);if(pos==std::string::npos) return false;*part1=s.substr(0,pos);*part2=s.substr(pos+1);//从pos到结尾
}
class Init
{
public:Init(){std::ifstream in(dictname);if(!in.is_open()){lg(Fatal,"ifstream open %s error",dictname.c_str());exit(1);}std::string line;while(std::getline(in,line)){std::string part1,part2;Split(line,&part1,&part2);dict.insert({part1,part2});}in.close();}std::string translation(const std::string &key){auto iter=dict.find(key);if(iter==dict.end()) return  "Unknow";else return iter->second;}
private:std::unordered_map<std::string,std::string> dict;
};
Makefile
.PHONY:all
all:tcpserver tcpclient
tcpserver:Main.ccg++ -o $@ $^ -std=c++11 -lpthread
tcpclient:TcpClient.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f tcpserver tcpclient

4、理解前台进程和后台进程

在操作系统中,程序可以在两种模式下运行:前台后台

 4.1什么叫前台进程?

        前台进程是与用户直接交互的进程。当你在终端中运行程序时(例如,执行./process),它默认是在前台运行,这意味着它会占用终端并接收来自键盘的输入。这种进程会阻塞终端输入,直到进程结束,所以在它运行期间,像lspwd这样的命令通常不会有反应。

下面是一个简单的C++程序运行在前台的示例:

process.cc
#include<iostream>
#include<string>
#include<unistd.h>
int main()
{while(true){std::cout<<"hello..."<<std::endl;sleep(1);}return 0;
}

 

4.2后台进程

有时,你可能希望程序在后台运行,以便不阻塞你的终端。这时你可以在命令后面加上符号&,例如: 

./process &

这样,程序就会在后台运行,你可以继续使用终端执行其他命令。

4.3切换前后台

如果需要将一个后台进程切换到前台,可以使用fg命令。例如fg 1会将后台任务列表中的第一个任务移至前台。  把后台进程提到前台   fg 1

 所以什么叫做前台,什么叫后台?

谁拥有键盘文件

相关文章:

TCP客户端服务器端通信(线程池版)

1、什么是监听套接字&#xff0c;和UDP相比&#xff0c;TCP为什么文件描述符变多了&#xff1f; 在网络编程中&#xff0c;TCP和UDP是两种常见的传输协议&#xff0c;它们之间最大的不同之一在于连接的管理方式。为了更好地理解这个区别&#xff0c;我们可以用一个生动的比喻来…...

go语言的成神之路-标准库篇-fmt标准库

目录 一、三种类型的输出 print&#xff1a; println&#xff1a; printf&#xff1a; 总结&#xff1a; 代码展示&#xff1a; 二、格式化占位符 %s&#xff1a;用于格式化字符串。 %d&#xff1a;用于格式化整数。 %f&#xff1a;用于格式化浮点数。 %v&#xff1…...

高级java每日一道面试题-2024年12月08日-JVM篇-什么是类加载器?

如果有遗漏,评论区告诉我进行补充 面试官: 什么是类加载器? 我回答: 在Java高级面试中&#xff0c;类加载器&#xff08;ClassLoader&#xff09;是一个重要的概念&#xff0c;它涉及到Java类的加载和初始化机制。以下是对类加载器的详细解释&#xff1a; 定义与作用 类加…...

数据库之连接池Druid

Druid是一个由阿里巴巴开源的高性能数据库连接池组件&#xff0c;它在许多项目中都是首选的连接池实现。Druid不仅提供了高效的连接管理&#xff0c;还具备丰富的监控和统计功能&#xff0c;以及强大的安全特性。 一、功能介绍 1、高效连接管理 Druid采用了高效的连接管理机…...

【如何审稿】Notes on Constructive and Positive Reviewing

学习体会&#xff1a; 用chatgpt翻译的2005年审稿人如何审稿的一篇文章, 学着用审稿人的角度评审自己写的稿件~ 全文摘录: 作为审稿人&#xff0c;您代表的是您的社区&#xff0c;您的审稿意见应当是专业和建设性的。会议的质量取决于审稿的质量。审稿人的任务是选出高质量、创…...

1.文本方块方法(Spacy Text Splitter 方法)Can‘t find model ‘zh_core_web_sm‘

一、概述 执行如下&#xff1a; def split_spacy(text):import spacynlp spacy.load( "zh_core_web_sm" ) doc nlp(text) for s in doc.sents: print(s) # d:\programdata\anaconda3\envs\python310\lib\site-packages if __name__"__main__":text &q…...

IT类项目实施流程

一、项目启动准备阶段 (一)甲方组建项目委员会 **甲方委员会确立:**指定负责人,全权负责整个项目人员、启动及时间调配、需求确定及需求变更、项目节点的验收、项目款项的支付工作。 (二) 乙方组建项目实施工作组 **乙方实施工作组确立:**工作组主要包括项目经理、需…...

数据分析平台业务落地痛点分析

在当今数字化时代&#xff0c;数据已成为企业决策和运营的核心驱动力。数据分析平台作为数据处理和洞察的重要工具&#xff0c;其业务落地过程中的痛点分析对于企业的数据战略至关重要。本文将通过概述、功能点、背景、业务点、底层原理等多个方面&#xff0c;深入剖析数据分析…...

java语言学习(5)

第九章 目录 类变量和类方法 理解main方法语法 代码块 单列设计模式 final关键字 抽象类 接口 类变量/静态变量 该变量最大的特点就是会被本类的所有对象实例共享&#xff0c;类变量可以通过类名来访问 分析代码&#xff0c;探讨内存的分配 class Child{private Str…...

基于SSM框架点餐管理系统(计算机毕业设计)论文,Java

系统合集跳转 源码获取链接 一、系统环境 运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 IDE环境&#xff1a; Eclipse,Myeclipse,IDEA或者Spring Tool Suite都可以 tomcat环境&#xff1a; Tomcat 7.x,8.x,9.x版本均可 操作系统…...

独家首发 | 基于 KAN、KAN卷积的轴承故障诊断模型

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 基于FFT CNN - BiGRU-Attention 时域、频域特征注意力融合的轴承故障识别模型-CSDN博客 基于FFT CNN - Transformer 时域、频域特征融合的轴承故障识别模型-CSDN博客 P…...

nginx-proxy-manager初次登录502 bad gateway

nginx-proxy-manager初次登录502 bad gateway 按照官方docker-compose安装后,页面如下: 默认账户密码: adminexample.com/changeme点击sign in,提示Bad Gateway 打开调试 重装后依然如此,最后查阅githup issue 找到答案 https://github.com/NginxProxyManager/nginx-proxy-…...

棋牌项目Go的日志打印问题

棋牌项目Go的日志打印问题 项目使用 go work 管理模块 github.com/spf13/viper 通过 application.yml 加载配置 $ tree . ├── README.md ├── common │ ├── application.yml │ ├── config │ │ └── config.go │ ├── go.mod │ ├── go.su…...

基于SpringBoot校园台球厅人员与设备管理系统设计与实现

1.1课题背景与意义 在Internet高速发展的今天&#xff0c;计算机的应用几乎完全覆盖我们生活的各个领域&#xff0c;互联网在经济&#xff0c;生活等方面有着举足轻重的地位&#xff0c;成为人们资源共享&#xff0c;信息快速传递的重要渠道。在中国&#xff0c;网上管理的兴起…...

样品前处理工作站自动化操作

样品前处理工作站通过集成多种技术和自动化模块&#xff0c;实现了对样品的高效、精准处理。以下是实现自动化操作的关键步骤和原理&#xff1a; 1、集成多种技术&#xff1a;工作站通常集成了液体处理、固相萃取、离心、过滤等多种技术。这些技术的结合使得工作站能够完成从样…...

在 Vue 3 中实现点击按钮后禁止浏览器前进或后退

在 Vue 3 中实现点击按钮后禁止浏览器前进或后退&#xff0c;我们可以通过 ref 和 watch 来管理状态&#xff0c;同时使用 onBeforeUnmount 来清理事件监听。 使用 Vue 3 实现&#xff1a; <template><div><button click"disableNavigation">点击…...

【Redis集群】使用docker compose创建docker集群,并暴露外部接口

使用 Docker Compose 创建一个 Redis 集群并暴露外部接口需要配置 docker-compose.yml 文件。以下是一个基本的步骤&#xff0c;包括 Redis 集群的创建和外部接口的暴露。 1、创建 docker-compose.yml 首先&#xff0c;您需要创建一个 docker-compose.yml 文件&#xff0c;配…...

RabbitMq 基础

文章目录 一、初识 MQ1.1 同步调用&#xff1a;1.2 异步调用&#xff1a; 二、RabbitMQ三、SpringAMQP3.1 依赖和配置文件3.2 消息发送和接收&#xff1a;3.2.1 消息发送&#xff1a;3.2.2 消息接收&#xff1a; 3.3 WorkQueues 模型&#xff1a;3.4 交换机类型&#xff1a;3.4…...

类文件结构详解

一、引言 Java 类文件是 Java 虚拟机执行的基本单元。它包含了 Java 程序的字节码以及其他重要的元数据信息。了解类文件结构可以帮助我们更好地理解 Java 程序的编译过程、运行机制以及如何进行优化。 二、类文件结构概述 Java 类文件采用一种紧凑的二进制格式&#xff0c;主…...

01_Linux

一.Linux简介 1.1 Linux介绍 Linux是一套免费使用和自由传播的操作系统。说到操作系 统&#xff0c;大家比较熟知的应该就是Windows和MacOS操作系统&#xff0c; 我们今天所学习的Linux也是一款操作系统 1.2 Linux发展历史 时间&#xff1a;1991年 地点&#xff1a;芬兰赫尔…...

区块链与AI结合:驱动Web3的未来发展

Web3作为下一代互联网的核心理念&#xff0c;强调去中心化、用户主权和透明性。而区块链技术和人工智能&#xff08;AI&#xff09;则是推动Web3发展的两大关键力量。两者的结合不仅为Web3带来了新的可能性&#xff0c;还推动了智能化、透明化、自治化等特点的实现。本文将探讨…...

如何解决 docker 容器中 “-bash: ping: command not found” 错误 ?

在 Docker 的世界里&#xff0c;遇到错误是学习曲线的一部分&#xff0c;其中一个常见的错误是&#xff1a; -bash: ping: command not found。当您在 Docker 容器中尝试使用 ping 命令来测试与其他网络机器或服务的连接&#xff0c;但该命令在您的容器环境中不可用时&#xff…...

Leetcode 每日一题 242.有效的字母异位词

目录 问题描述 示例 题目要求 解决方案 算法思路 过题图片 代码实现 算法分析 优化思路 优化后的代码实现 优化后的算法分析 题目链接 问题描述 给定两个字符串 s 和 t&#xff0c;我们需要编写一个函数来判断 t 是否是 s 的字母异位词。字母异位词指的是两个字符串…...

centos7搭建FTP详细讲解

文章目录 1、ftp服务1、工作原理2、工作模式3、身份验证模式4、ftp服务器配置5、ftp客户端操作 1、ftp服务 1、工作原理 ![img](https://img2023.cnblogs.com/blog/3210480/202412/ 3210480-20241208224333047-141845069.png) 是一个cs架构 2个通道&#xff0c;21端口为控制…...

YOLOv8-ultralytics-8.2.103部分代码阅读笔记-build.py

build.py ultralytics\data\build.py 目录 build.py 1.所需的库和模块 2.class InfiniteDataLoader(dataloader.DataLoader): 3.class _RepeatSampler: 4.def seed_worker(worker_id): 5.def build_yolo_dataset(cfg, img_path, batch, data, mode"train"…...

JAVA-动态代理

文章目录 概要两种主要方式JDK 动态代理Proxy 类作用&#xff1a;常用方法&#xff1a; InvocationHandler 接口作用&#xff1a; 目标对象、代理对象、被代理对象JDK 动态代理工作原理 cjlib 动态代理demo 对比 概要 Java 动态代理利用 Java 的反射机制&#xff0c;可以在运行…...

【前端】React_Next.js

定期更新&#xff0c;建议关注、收藏&#xff01; 安装 要使用react的框架&#xff0c;都应当安装node.js conda install nodejs选择Next.js 并创建项目 npx create-next-applatest可以看到目录结构生成如下 作为网页中的一个子路由 想让其嵌入已经写好的项目中&#xf…...

深入理解malloc与vector:内存管理的对比

引言‌ 在编程中&#xff0c;内存管理是一个至关重要的环节。无论是C语言中的malloc函数&#xff0c;还是C标准库中的vector容器&#xff0c;它们都在内存分配和释放上扮演着关键角色。然而&#xff0c;它们的设计理念和用法有着显著的不同。本文将深入探讨malloc和vector的区…...

LeetCode题练习与总结:四数相加 Ⅱ --454

一、题目描述 给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 < i, j, k, l < nnums1[i] nums2[j] nums3[k] nums4[l] 0 示例 1&#xff1a; 输入&#xff1a;n…...

AI绘画设计实战-Day1

AI绘画变现方向&#xff1a; 生成猫咪拟人化&#xff1b;老照片修复&#xff1b;生成奇幻场景&#xff0c;换上客户的脸&#xff1b;生成商品详情模特图&#xff1b;商品宣传图&#xff1b;建筑/室内设计方案&#xff1b; AI视频-变现方向&#xff1a; AI跳舞视频&#xff…...

【Windows】ImmGetContext函数不适合跨进程工作

1. 教授回复 ChatGPT-o1: ImmGetContext() is not designed to work across process boundaries. It is indicates that a thread should not access the input context created by another thread, which implies that the function is intended for use within the same proc…...

【Linux】进程间通信——System V共享内存

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…...

fastAPI快速使用

1. 安装: pip install fastapi 并且安装uvicorn来作为服务器&#xff1a; pip install uvicorn 最简单的 FastAPI 文件可能像下面这样&#xff1a; # main.pyfrom fastapi import FastAPIapp FastAPI()app.get("/") async def root():return {"message&qu…...

ruoyi前后端不分离创建module

ruoyi创建module 1.创建表 2.在若依中创建module 3.注意选择maven 4.命名 5&#xff0e;把module加入到rouyi的pom.xml中 6.student 中加入common依赖 二.生成代码 2.1先创建目录 2&#xff0e;2导入创建的表 2.3 注意各种配置 2.4预览代码 2.5 生成代码 2.6…...

【AI日记】24.12.08 kaggle 比赛 Titanic-11

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】 工作 内容&#xff1a; 学习 kaggle 入门比赛 Titanic - Machine Learning from Disaster学习机器学习&#xff08;pandas&#xff0c;numpy&#xff0c;sklearn&#xff0c;seaborn&#xff0c;matplotlib&a…...

处理后端返回的时间格式问题

今天在做项目的时候&#xff0c;发现后端返回的时间格式是“2024-12-08T06:11:46.00000:00”。 通过查阅资料&#xff0c;我发现这是后端返回的ISO 8601标准时间格式。这种格式在前端展示时可能不符合用户习惯&#xff0c;需要转换为更常见的“yyyy-MM-dd HH:mm:ss”格式&…...

【读书笔记·VLSI电路设计方法解密】问题33:ASIC芯片构建的主要步骤是什么

如问题32所述,ASIC芯片构建的主要步骤包括:RTL编写、功能验证、逻辑综合、布局布线、最终逻辑验证、时序验证、物理验证以及流片(Tapeout)。 RTL编写(RTL Coding) 这一阶段将设计意图从自然语言(如英文或中文)翻译为计算机可模拟的语言,以便进行仿真验证设计意图。此外…...

[机器学习] 监督学习之线性回归与逻辑回归

这里写目录标题 一、监督学习概述二、线性回归&#xff08;一&#xff09;模型表示&#xff08;二&#xff09;损失函数&#xff08;三&#xff09;梯度下降算法导入所需库生成模拟数据&#xff08;可替换为真实数据&#xff09;初始化参数并进行训练可视化损失函数随迭代次数的…...

十五、全局搜索正则表达式

一.grep简介 grep 全程Globally search a Regular Expression and Print&#xff0c;是一种强大的文本搜索工具&#xff0c;它能使用特 定模式匹配&#xff08;包括正则表达式&#xff09;搜索文本&#xff0c;并默认输出匹配行。Unix的grep家族包括grep和egrep 二.grep的工作…...

kali Linux 安装配置教程(图文详解)

目录 一、下载安装VMware二、下载kali镜像三、安装kali到虚拟机 一、下载安装VMware 点我查看 二、下载kali镜像 官网下载&#xff1a;https://www.kali.org/get-kali/#kali-platforms 演示下载的为下图左边的&#xff0c;实际我们直接下载右侧虚拟机的直接使用就行 右侧下…...

QT QTableWidget::setModel”: 无法访问 private成员

//严重性代码说明项目文件行禁止显示状态 //错误C2248 “QTableWidget::setModel”: 无法访问 private 成员(在“QTableWidget”类中声明) QSqlQueryModel* sql_model; ui.tableView_database->setModel(sql_model); //ok ui.tableWidget_database->setModel(sql_model)…...

C#怎么判断电脑是否联网

在 C# 中&#xff0c;可以通过几种方法检测计算机是否联网。以下是几种常用的方式&#xff1a; 1. 使用 System.Net.NetworkInformation.Ping 类 通过发送一个 Ping 请求到公共 DNS 服务器&#xff08;如 Google 的 DNS 8.8.8.8&#xff09;来检测是否联网。这是最常见的一种…...

丢垃圾视频时间检测 -- 基于状态机的实现

文章目录 OverviewKey PointsPseudo-code Overview 需要考虑的方面 状态定义和转换条件时序约束空间约束异常处理 状态机的设计需要考虑的场景&#xff1a; 没有人人进入人携带垃圾人离开但垃圾留下垃圾消失异常情况&#xff08;检测失败、多人多垃圾等&#xff09; Key P…...

EEG2Rep自监督脑电大模型文献阅读

原文网址&#xff1a; EEG2Rep: Enhancing Self-supervised EEG Representation... - Google 学术搜索https://scholar.google.com/scholar?hlzh-CN&as_sdt0%2C5&qEEG2Rep%3AEnhancingSelf-supervisedEEGRepresentationThroughInformativeMaskedInputs&btnG 1.文…...

【前端知识】React简单入门

React语法介绍 概述一、产生背景与发展历程二、主要特点三、技术细节四、应用场景与优势五、学习与实践 JSX语法一、JSX的基本概念二、JSX的基本使用三、JSX中的JavaScript表达式四、JSX的条件渲染五、JSX的列表渲染六、JSX的样式处理七、JSX的其他注意事项 基础语法一、基础概…...

高通---Camera调试流程及常见问题分析

文章目录 一、概述二、Camera配置的整体流程三、Camera的代码架构图四、Camera数据流的传递五、camera debug FAQ 一、概述 在调试camera过程中&#xff0c;经常会遇到各种状况&#xff0c;本篇文章对camera调试的流程进行梳理。对常见问题的提供一些解题思路。 二、Camera配…...

电压电流声音信号采集与分析系统

基于Labview的电压电流与声音信号采集与分析系统可以同时采集&#xff1a;电压、电流与振动信号。该系统部件可以采集传感器的真实数据&#xff0c;而且可以对采集的信号进行时域与频域分析&#xff0c;并可以实时显示历史数据&#xff0c;保存历史数据。具体的功能如下&#x…...

MongoDB语法及MongoTemplate用法

文章目录 概念操作数据库语法数据库及文档操作文档操作 整合springboot的MongoTemplate用法springboot配置插入删除更新查询 概念 1.MongoDB 是一个文档数据库&#xff0c;数据以 BSON 方式存储&#xff08;类似于json&#xff09; 2.文档&#xff08;Document&#xff09;&am…...

【git reset】本地下载特定历史提交哈希值的github文件【未联网服务器】进行git reset操作

本地电脑下载git文件&#xff0c;并进行git reset操作 问题描述&#xff1a;解决方法&#xff1a;方法1&#xff1a;直接下载特定版本的github压缩包。方法二&#xff1a; 在本地windows电脑上安装git工具进行git reset版本回退&#xff0c;之后上传相应版本的压缩包到服务器上…...

【开源安全保护】如何安装JumpServer堡垒机

【开源安全保护】如何安装JumpServer堡垒机 什么是堡垒机 大家好&#xff0c;我是星哥&#xff0c;今天我以前来认识堡垒机 堡垒机&#xff08;Bastion Host&#xff09;&#xff0c;也称为跳板机&#xff08;Jump Server&#xff09;&#xff0c;是指在计算机网络中&#x…...