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

Linux篇:网络基础1

一、网络基础:网络本质就是在获取和传输数据,而系统的本质是在加工和处理数据。

1、应用问题:
①如何处理发来的数据?—https/http/ftp/smtp
②长距离传输的数据丢失的问题?——TCP协议
③如何定位的主机的问题?——IP协议
④怎么保证数据能准确到达下一个设备?——数据链路层

2、协议:
①协议是双方的约定,最终的表现形式就是具有特定字段的结构体对象。
②协议分层:软件分层最典型的表现是:在结构体或类的设计上,做数据结构层面的分离/用回调函数的方式,在软件的逻辑上进行分层。
③通过高内聚,低耦合,大大降低软件维护成本。

3、
①OSI七层模型(略)。
②TCP/IP五层(或四层)模型:(物理层、)数据链路层、网络层、传输层、应用层(像这样的网络协议栈命名为TCP/IP协议栈)。
③其中,传输层和网络层是Linux内核中的模块,也就是操作系统。
④网络通信的本质就是贯穿协议栈的过程。
⑤网络协议栈的层状结构中,每一层都有协议。

4、报文=报头+有效载荷
①通信的过程,本质就是不断的封装和解包的过程!
②扩展(为封装和解包提供前提,是大部分协议的共性,未来我们学习具体协议的时候都会围绕这两个问题):
a.几乎任何层的协议都要提供一种能将报头和有效载荷分离的能力。
b.几乎任何层的协议都要在报头中提供决定将自己的有效载荷交付给上层的哪一个协议的能力(报文的分用)。

5、以太网通信(每台主机在局域网上都要有一个自己唯一的编号):
①以太网(局域网)通信原理。
②Mac地址:能够标识特定一台主机在局域网中的唯一性。
③以太网发生数据碰撞问题,发送主机都要执行的碰撞避免算法(延迟发送,错峰处理)(碰撞域)。
④网卡的工作模式:正常模式、混杂模式(抓包原理)。
⑤交换机:划分碰撞域,防止报文在更大的局域网中扩散,有效降低数据在局域网中碰撞的概率。
⑥如何看待局域网:一个时刻只允许一个主机向局域网中投递数据。使用这个共享资源(临界资源)时,要保证该共享资源的互斥访问。

6、IP地址:
①IP地址存在的意义:能够标识特定一台主机在全网当中的唯一性。能够指导我们进行路径规划。
②网络可以根据目的IP中当前主机和目标主机是否在同一网段来决定是否将数据交给路由器(本质就是局域网通信)。
③IP报文在全球子网自由通信的原因:路由器会根据目标子网重新解包封装一个符合当前局域网的新报头。
④IP协议通过工作在IP层的路由器屏蔽了底层网络的差异化。
⑤IP实现了全球主机的软件虚拟层,一切皆是IP报文。
⑥IP地址和MAC地址的区别:
IP地址,尤其是目的IP,一般都是不会改变的,协助我们进行路径选择。
MAC地址,出局域网之后,源和目都要被丢弃,让路由器重新封装。
⑦在应用层一般把数据包叫做请求与响应,在传输层一般叫做数据段/数据包,在网络层一般叫做数据报,在链路层一般叫做数据帧。

7、网络通信:
①网络协议中的下三层主要解决的是数据安全可靠的送到远端机器。
②用户使用应用层软件,完成数据发送和接受。
③我们日常网络通信的本质就是进程间通信。
④通过网络协议栈,利用网络资源,让不同的进程看到同一份资源。

8、端口号:
①端口号:无论对于client和server,都能唯一的标识该主机上的一个网络应用层的进程。
②在公网上,IP地址能表示唯一的一台主机。
③所以,IP:Port能够标识全网唯一的一个进程。
④源IP:源端口   client_ip:client_port
目的IP:目的端口   server_ip:server_port
这种基于IP加端口的方式称为socket。
⑤端口号vs进程PID(PID已经能够标识一台主机上进程的唯一性了,为什么还要搞一个端口号?):
a.不是所有的进程都要网络通信,但是所有进程都要有PID。
b.系统网络功能解耦。
⑥我们的客户端如何知道服务器的端口号是多少?
每一个服务的端口号必须是众所周知,精心设计,被客户端知晓的。
⑦一个进程可以绑定多个端口号吗?可以。
一个端口号可以被多个进程绑定吗?不行。

9、
TCP协议(传输控制协议):
①传输层协议。
②有连接。
③可靠传输。
④面向字节流。

UDP协议(用户数据报协议):
①传输层协议。
②无连接。
③不可靠传输。
④面向数据报。

二、Socket编程接口:

套接字编程的种类:
①域间套接字编程:另一个主机内。
②原始套接字编程:网络工具。
③网络套接字编程:用户间的网络通信。
想将网络接口统一抽象化(参数的类型必须是统一的)。

三、UDP协议实现:

##Makefile.PHONY:all
all:udpserver udpclient
udpserver:Main.ccg++ -o $@ $^ -std=c++11
udpclient:UdpClient.ccg++ -o $@ $^ -lpthread -std=c++11
.PHONY:clean
clean:rm -f udpserver udpclient
// Main.cc#include "UdpServer.hpp"
#include <memory>
#include <cstdio>
#include <vector>// "123.60.161.111" 点分十进制字符串风格的IP地址void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}// std::string Handler(const std::string &info, const std::string &clientip, uint16_t clientport)
// {
//     std::cout << "[" << clientip << ":" << clientport << "]#" << info << std::endl;
//     std::string res = "Server get a message: ";
//     res += info;
//     std::cout << res << std::endl;//     return res;
// }// bool SafeCheck(const std::string &cmd)
// {
//     int safe = false;
//     std::vector<std::string> key_word = {
//         "rm",
//         "mv",
//         "cp",
//         "kill",
//         "sudo",
//         "unlink",
//         "uninstall",
//         "yum",
//         "top",
//         "while"
//     };//     for(auto &word : key_word)
//     {
//         auto pos = cmd.find(word);
//         if(pos != std::string::npos) return false;
//     }
//     return true;
// }// std::string ExcuteCommand(const std::string &cmd)
// {
//     std::cout << "get a request cmd: " << cmd << std::endl;
//     if(!SafeCheck(cmd)) return "Bad man";//     FILE *fp = popen(cmd.c_str(), "r");
//     if(nullptr == fp)
//     {
//         perror("popen");
//         return "error";
//     }//     std::string result;
//     char buffer[4096];
//     while(true)
//     {
//         char *ok = fgets(buffer, sizeof(buffer), fp);
//         if(ok == nullptr) break;
//         result += buffer;
//     }
//     pclose(fp);//     return result;
// }// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);// std::unique_ptr<UdpServer> svr(new UdpServer(8080, "123.60.161.111")); // 如果是虚拟机,这段代码是可以执行的。但是云服务器禁止bind公网IP。bind(IP:0),凡是发给我这台主机的数据,我们都要根据端口号向上交付(任意地址bind)。// std::unique_ptr<UdpServer> svr(new UdpServer(8080));std::unique_ptr<UdpServer> svr(new UdpServer(port, "127.0.0.1")); // 本地环回地址,只能用于本地进程间通信,通常用它来进行cs的测试。svr->Init();svr->Run();return 0;
}
// UdpServer.hpp#pragma once#include <iostream>
#include <string>
#include <strings.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include <unordered_map>
#include "Log.hpp"// using func_t = std::function<std::string(const std::string&)>;
typedef std::function<std::string(const std::string &, const std::string &, uint16_t)> func_t;Log lg;enum{SOCKET_ERR=1,BIND_ERR
};uint16_t defaultport = 8080;
std::string defaultip = "0.0.0.0";
const int size = 1024;class UdpServer
{
public:UdpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip):sockfd_(0), port_(port), ip_(ip), isrunning_(false){}void Init(){// 1、创建udp socket(Udp的socket是全双工的,允许同时被读写)sockfd_ = socket(AF_INET, SOCK_DGRAM, 0); // PF_INETif(sockfd_ < 0){lg(Fatal, "socket create error, sockfd: %d", sockfd_);exit(SOCKET_ERR);}lg(Info, "socket create success, sockfd: %d", sockfd_);// 2、bind socketstruct sockaddr_in local; // 结构提可以用{}初始化,不能用{}赋值bzero(&local, sizeof(local)); // 将一段内存初始化为0local.sin_family = AF_INET; // 表明自身结构体类型local.sin_port = htons(port_); // 需要保证该端口号是网络字节序列,因为该端口号是要给对方发送的// local.sin_addr.s_addr = inet_addr(ip_.c_str()); // 1、string -> uint32_t 2、uint32_t必须是网络序列的local.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(sockfd_, (const struct sockaddr *)&local, sizeof(local)) < 0){lg(Fatal, "bind errno , error: %d, err string: %s", errno, strerror(errno));exit(BIND_ERR);}lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));}void CheckUser(const struct sockaddr_in &client, const std::string clientip, uint16_t port){auto iter = online_user_.find(clientip);if(iter == online_user_.end()){online_user_.insert({clientip, client});std::cout << "[" << clientip << ":" << port << "] add to online user." << std::endl;}}void Broadcast(const std::string &info, const std::string clientip, uint16_t clientport){for(const auto &user : online_user_){std::string message = "[";message += clientip;message += ":";message += std::to_string(clientport);message += "]#";message += info;socklen_t len = sizeof(user.second);sendto(sockfd_, message.c_str(), message.size(), 0, (struct sockaddr*)(&user.second), len);}}// void Run(func_t func) // 对代码进行分层void Run(){isrunning_ = true;char inbuffer[size];while(isrunning_){struct sockaddr_in client;socklen_t len = sizeof(client);ssize_t n = recvfrom(sockfd_, inbuffer, sizeof(inbuffer) - 1, 0, (struct sockaddr*)&client, &len);if(n < 0){lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}uint16_t clientport = ntohs(client.sin_port);std::string clientip = inet_ntoa(client.sin_addr);CheckUser(client, clientip, clientport);std::string info = inbuffer;Broadcast(info, clientip, clientport);}}~UdpServer(){if(sockfd_ > 0) close(sockfd_);}
private:int sockfd_; // 网络文件描述符std::string ip_; // 任意地址binduint16_t port_; // 表明服务器进程的端口号bool isrunning_;std::unordered_map<std::string, struct sockaddr_in> online_user_;
};
// UdpClient.cc#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " serverip serverport\n" << std::endl;
}struct ThreadData
{struct sockaddr_in server;int sockfd;std::string serverip;
};void *recv_message(void *args)
{ThreadData *td = static_cast<ThreadData *>(args);char buffer[1024];while(true){struct sockaddr_in temp;socklen_t len = sizeof(temp);ssize_t s = recvfrom(td->sockfd, buffer, 1023, 0, (struct sockaddr*)&temp, &len);if(s > 0){buffer[s] = 0;std::cerr << buffer << std::endl;}}
}void *send_message(void *args)
{ThreadData *td = static_cast<ThreadData *>(args);std::string message;socklen_t len = sizeof(td->server);std::string welcome = td->serverip;welcome += " coming...";sendto(td->sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&td->server, len);while(true){std::cout << "Please Enter@ ";std::getline(std::cin, message);// std::cout << message << std::endl;// 1、数据 2、给谁发sendto(td->sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&td->server, len);}
}// 多线程
// ./udpclient serverip serverport
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 ThreadData td;bzero(&td.server, sizeof(td.server)); // 将一段内存初始化为0td.server.sin_family = AF_INET;td.server.sin_port = htons(serverport); td.server.sin_addr.s_addr = inet_addr(serverip.c_str());td.sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(td.sockfd < 0){std::cout << "socket error" << std::endl;return 1;}td.serverip = serverip;pthread_t recvr, sender;pthread_create(&recvr, nullptr, recv_message, &td);pthread_create(&sender, nullptr, send_message, &td);// client也要bind,只不过不需要用户显示bind。一般由OS自由随机选择。// 一个端口号只能被一个进程bind,对server是如此,对于client,也是如此。// 其实client的port是多少,其实不重要,只要能保证主机上的唯一性就可以。// 系统在首次发送数据的时候bind。pthread_join(recvr, nullptr);pthread_join(sender, nullptr);close(td.sockfd);return 0;
}
// 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;
};

 四、TCP协议实现:

// Makefile.PHONY:all
all:tcpserverd tcpclient
tcpserverd:Main.ccg++ -o $@ $^ -std=c++11 -lpthread
tcpclient:TcpClient.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f tcpserverd tcpclient
// Main.cc#include "TcpServer.hpp"
#include <iostream>
#include <memory>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(UsageError);}uint16_t port = std::stoi(argv[1]);lg.Enable(Classfile);std::unique_ptr<TcpServer> tcp_svr(new TcpServer(port));tcp_svr->InitServer();tcp_svr->Start();return 0;
}
// TcpServer.hpp#pragma once#include <iostream>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 10; // 但是一般不要设置的太大
extern Log lg;enum
{UsageError = 1,SocketError,BindError,ListenError,
};class TcpServer;class ThreadData
{
public:ThreadData(int fd, const std::string &ip, const uint16_t &p, TcpServer *t): sockfd(fd), clientip(ip), clientport(p), 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, "socket create error, listensock: %d", listensock_);exit(SocketError);}lg(Info, "socket create success, : %d", listensock_);int opt = 1;setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt)); // 防止偶发性的服务器无法进行立即重启struct sockaddr_in local;bzero(&local, 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;if(bind(listensock_, (struct sockaddr*)&local, sizeof(local)) < 0){lg(Fatal, "bind errno , error: %d, err string: %s", errno, strerror(errno));exit(BindError);}lg(Info, "bind socket success, listensock: %d", listensock_);// Tcp是面向连接的,服务器一般是比较“被动的”,服务器一直处于等待连接的状态if(listen(listensock_, backlog) < 0){lg(Fatal, "listen errno , error: %d, err string: %s", errno, strerror(errno));exit(ListenError);}lg(Info, "listen socket success, listensock: %d", listensock_);}// static void *Routine(void *args)// {//     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(){Daemon();// signal(SIGPIPE, SIG_IGN);ThreadPool<Task>::GetInstance()->Start();// signal(SIGCHLD, SIG_IGN);lg(Info, "tcpServer is running...");for(;;){// 1. 获取新连接struct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = accept(listensock_, (struct sockaddr*)&client, &len);if(sockfd < 0){lg(Warning, "accept errno , error: %d, err string: %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));// 2. 根据新连接来进行通信lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d\n", sockfd, clientip, clientport);std::cout << "hello world" << std::endl;// version 1 -- 单进程版// Service(sockfd, clientip, clientport);// close(sockfd);// version 2 -- 多进程版// pid_t id = fork();// if(id == 0)// {//     // child//     close(listensock_);//     if(fork() > 0) exit(0); // 子进程立刻退出//     Service(sockfd, clientip, clientport); // 孙子进程由系统领养//     close(sockfd);//     exit(0);// }// close(sockfd);// // father// pid_t rid = waitpid(id, nullptr, 0);// (void)rid;// version 3 -- 多线程版// ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);// pthread_t tid;// pthread_create(&tid, nullptr, Routine, td);// version 4 -- 线程池版Task t(sockfd, clientip, clientport);ThreadPool<Task>::GetInstance()->Push(t);}}// void Service(int sockfd, const std::string &clientip, const 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 = "tcpserver 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.c_str(), sockfd, clientport);//             break;//         }//         else//         {//             lg(Warning, "read error, sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);//             break;//         }//     }// }~TcpServer(){}
private:int listensock_; // 监听套接字std::string ip_;uint16_t port_; 
};
// TcpClient.cc#include <iostream>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>void Usage(const std::string &proc)
{std::cout << "\n\rUsage: " << proc << " serverip serverport\n" << std::endl;
}// ./tcpclient serverip serport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);inet_pton(AF_INET, serverip.c_str(), &(server.sin_addr));// local.sin_addr.s_addr = INADDR_ANY;while (true){int cnt = 20;int isreconnect = false;int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;return 1;}do{// tcp客户端要bind,但不需要显示bind。// 客户端发起connect的时候,进行自动随机bind。int n = connect(sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){isreconnect = true;cnt--;std::cerr << "connect error..., reconnect: " << cnt << std::endl;sleep(1);}else{break;}} while (cnt && isreconnect);if (cnt == 0){std::cerr << "user offline..." << std::endl;break;}// while (true)// {std::string message;std::cout << "Please Enter# ";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;}// else{//     break;// }// }close(sockfd);}return 0;
}
// Task.hpp#pragma once
#include <iostream>
#include <string>
#include "Log.hpp"
#include "Init.hpp"extern Log lg;
Init init;class Task
{
public:Task(int sockfd, const std::string &clientip, const uint16_t &clientport): sockfd_(sockfd), clientip_(clientip), clientport_(clientport){}Task(){}void run(){// 测试代码char buffer[4096];// Tcp是面向字节流的,你怎么保证,你读取上来的数据,是"一个" "完整" 的报文呢?ssize_t n = read(sockfd_, buffer, sizeof(buffer)); // BUG?if (n > 0){buffer[n] = 0;std::cout << "client key# " << buffer << std::endl;std::string echo_string = init.translation(buffer);// sleep(5);// // close(sockfd_);// lg(Warning, "close sockfd %d done", sockfd_);// sleep(2);n = write(sockfd_, echo_string.c_str(), echo_string.size()); // 100 fd 不存在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_.c_str(), clientport_, sockfd_);}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_;
};
// 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;
// Init.hpp#pragma once#include <iostream>
#include <string.h>
#include <fstream>
#include <unordered_map>
#include "Log.hpp"const std::string dictname = "./dict.txt";
const std::string sep = ":";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);return true;
}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 "Unknown";else return iter->second;}
private:std::unordered_map<std::string, std::string> dict;
};

守护进程化:

// Daemon.hpp#pragma once#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>const std::string nullfile = "/dev/null";void Daemon(const std::string &cwd = "")
{// 1、忽略其他异常信号signal(SIGCLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);signal(SIGSTOP, SIG_IGN);// 2、将自己变成独立的回话if(fork() > 0) exit(0);setsid();// 3、更改当前调用进程的工作目录if(!cwd.empty()) chdir(cwd.c_str());// 4、标准输入、标准输出、标准错误重定向至/dev/nullint fd = open(nullfile.c_str(), O_RDWR);if(fd > 0){dup2(fd, 0);dup2(fd, 1);dup2(fd, 2);close(fd);}
}
// 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;
};Log lg;

相关文章:

Linux篇:网络基础1

一、网络基础&#xff1a;网络本质就是在获取和传输数据&#xff0c;而系统的本质是在加工和处理数据。 1、应用问题&#xff1a; ①如何处理发来的数据&#xff1f;—https/http/ftp/smtp ②长距离传输的数据丢失的问题&#xff1f;——TCP协议 ③如何定位的主机的问题&#…...

RK3568笔记十七:LVGL v8.2移植

若该文为原创文章&#xff0c;转载请注明原文出处。 本文介绍嵌入式轻量化图形库LVGL 8.2移植到Linux开发板ATK-RK3568上的步骤。 主要是参考大佬博客&#xff1a; LVGL v8.2移植到IMX6ULL开发板_lvgl移植到linux-CSDN博客 一、环境 1、平台&#xff1a;rk3568 2、开发板:…...

C#系列-C#访问MongoDB+redis+kafka(7)

目录 一、 C#中访问MongoDB. 二、 C#访问redis. 三、 C#访问kafka. C#中访问MongoDB 在C#中访问MongoDB&#xff0c;你通常会使用MongoDB官方提供的MongoDB C#/.NET Driver。这个驱动提供了丰富的API来执行CRUD&#xff08;创建、读取、更新、删除&#x…...

(12)Hive调优——count distinct去重优化

离线数仓开发过程中经常会对数据去重后聚合统计&#xff0c;count distinct使得map端无法预聚合&#xff0c;容易引发reduce端长尾&#xff0c;以下是count distinct去重调优的几种方式。 解决方案一&#xff1a;group by 替代 原sql 如下&#xff1a; #7日、14日的app点击的…...

记录 | 验证pytorch-cuda是否安装成功

检测程序如下&#xff1a; import torchprint(torch.__version__) print(torch.cuda.is_available()) 或者用终端 Shell&#xff0c;运行情况如下...

LeetCode 239.滑动窗口的最大值 Hot100 单调栈

给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,-1,-3,5,3,6,7], k 3 输…...

463. Island Perimeter(岛屿的周长)

问题描述 给定一个 row x col 的二维网格地图 grid &#xff0c;其中&#xff1a;grid[i][j] 1 表示陆地&#xff0c; grid[i][j] 0 表示水域。 网格中的格子 水平和垂直 方向相连&#xff08;对角线方向不相连&#xff09;。整个网格被水完全包围&#xff0c;但其中恰好有…...

如何解决缓存和数据库的数据不一致问题

数据不一致问题是操作数据库和操作缓存值的过程中&#xff0c;其中一个操作失败的情况。实际上&#xff0c;即使这两个操作第一次执行时都没有失败&#xff0c;当有大量并发请求时&#xff0c;应用还是有可能读到不一致的数据。 如何更新缓存 更新缓存的步骤就两步&#xff0…...

linux系统下vscode portable版本的python环境搭建003:venv

这里写自定义目录标题 python安装方案一. 使用源码安装&#xff08;有[构建工具](https://blog.csdn.net/ResumeProject/article/details/136095629)的情况下&#xff09;方案二.使用系统包管理器 虚拟环境安装TESTCG 本文目的&#xff1a;希望在获得一个新的系统之后&#xff…...

使用TinyXML-2解析XML文件

一、XML介绍 当我们想要在不同的程序、系统或平台之间共享信息时&#xff0c;就需要一种统一的方式来组织和表示数据。XML&#xff08;EXtensible Markup Language&#xff0c;即可扩展标记语言&#xff09;是一种用于描述数据的标记语言&#xff0c;它让数据以一种结构化的方…...

Linux:docker在线仓库(docker hub 阿里云)基础操作

把镜像放到公网仓库&#xff0c;这样可以方便大家一起使用&#xff0c;当需要时直接在网上拉取镜像&#xff0c;并且你可以随时管理自己的镜像——删除添加或者修改。 1.docker hub仓库 2.阿里云加速 3.阿里云仓库 由于docker hub是国外的网站&#xff0c;国内的对数据的把控…...

C语言程序设计(第四版)—习题7程序设计题

目录 1.选择法排序。 2.求一批整数中出现最多的数字。 3.判断上三角矩阵。 4.求矩阵各行元素之和。 5.求鞍点。 6.统计大写辅音字母。 7.字符串替换。 8.字符串转换成十进制整数。 1.选择法排序。 输入一个正整数n&#xff08;1&#xff1c;n≤10&#xff09;&#xf…...

ZCC6982-同步升压充双节锂电池充电芯片

特性 ■高达 2A 的可调充电电流&#xff08;受实际散热和输入功率限制&#xff09; ■支持 8.4V、8.6V、8.7V、8.8V 的充满电压&#xff08;限QFN&#xff09; ■高达 28V 的输入耐压保护 ■高达 28V 的电池端耐压保护 ■宽输入工作电压范围&#xff1a;3.0V~6.5V ■峰值…...

定时器(基本定时器、通用定时器、高级定时器)

目录 一、基本定时器 二、通用定时器 三、高级定时器 一、基本定时器 1、作用&#xff1a;计时和计数。 二、通用定时器 1、除了有基本定时器的计时和计数功能外&#xff0c;主要有输入捕获和输出比较的功能&#xff0c;硬件主要由六大部分组成&#xff1a; ① 时钟源 ② 控…...

009集——磁盘详解——电脑数据如何存储在磁盘

很多人也知道数据能够保存是由于设备中有一个叫做「硬盘」的组件存在&#xff0c;但也有很多人不知道硬盘是怎样储存这些数据的。这里给大家讲讲其中的原理。 首先我们要明白的是&#xff0c;计算机中只有0和1&#xff0c;那么我们存入硬盘的数据&#xff0c;实际上也就是一堆0…...

鸿蒙开发-HarmonyOS UI架构

初步布局Index 当我们新建一个工程之后&#xff0c;首先会进入Index页。我们先简单的做一个文章列表的显示 class Article {title?: stringdesc?: stringlink?: string }Entry Component struct Index {State articles: Article[] []build() {Row() {Scroll() {Column() …...

Flutter 动画(显式动画、隐式动画、Hero动画、页面转场动画、交错动画)

前言 当前案例 Flutter SDK版本&#xff1a;3.13.2 显式动画 Tween({this.begin,this.end}) 两个构造参数&#xff0c;分别是 开始值 和 结束值&#xff0c;根据这两个值&#xff0c;提供了控制动画的方法&#xff0c;以下是常用的&#xff1b; controller.forward() : 向前…...

用HTML5 Canvas创造视觉盛宴——动态彩色线条效果

目录 一、程序代码 二、代码原理 三、运行效果 一、程序代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- 声明文档类型为XHTML 1.0 Transitional -…...

云原生介绍与容器的基本概念

云原生介绍 1、云原生的定义 云原生为用户指定了一条低心智负担的、敏捷的、能够以可扩展、可复制的方式最大化地利用云的能力、发挥云的价值的最佳路径。 2、云原生思想两个理论 第一个理论基础是&#xff1a;不可变基础设施。 第二个理论基础是&#xff1a;云应用编排理…...

Flash存储

目录 一、MCU读写擦除Flash步骤 1、写flash步骤&#xff1a; 2、读flash步骤&#xff1a; 3、擦除flash步骤&#xff1a; 4、要注意的地方&#xff1a; 一、MCU读写擦除Flash步骤 1、写flash步骤&#xff1a; (1)解锁 2、读flash步骤&#xff1a; 3、擦除flash步骤&#x…...

Day 44 | 动态规划 完全背包、518. 零钱兑换 II 、 377. 组合总和 Ⅳ

完全背包 题目 文章讲解 视频讲解 完全背包和0-1背包的区别在于&#xff1a;物品是否可以重复使用 思路&#xff1a;对于完全背包问题&#xff0c;内层循环的遍历方式应该是从weight[i]开始一直遍历到V&#xff0c;而不是从V到weight[i]。这样可以确保每种物品可以被选择多次…...

使用PaddleNLP UIE模型提取上市公司PDF公告关键信息

项目地址&#xff1a;使用PaddleNLP UIE模型抽取PDF版上市公司公告 - 飞桨AI Studio星河社区 (baidu.com) 背景介绍 本项目将演示如何通过PDFPlumber库和PaddleNLP UIE模型&#xff0c;抽取公告中的相关信息。本次任务的PDF内容是破产清算的相关公告&#xff0c;目标是获取受理…...

软件工程师,OpenAI Sora驾到,快来围观

概述 近期&#xff0c;OpenAI在其官方网站上公布了Sora文生视频模型的详细信息&#xff0c;展示了其令人印象深刻的能力&#xff0c;包括根据文本输入快速生成长达一分钟的高清视频。Sora的强大之处在于其能够根据文本描述&#xff0c;生成长达60秒的视频&#xff0c;其中包含&…...

【Linux 04】编辑器 vim 详细介绍

文章目录 &#x1f308; Ⅰ 基本概念&#x1f308; Ⅱ 基本操作1. 进入 / 退出 vim2. vim 模式切换 &#x1f308; Ⅲ 命令模式1. 光标的移动2. 复制与粘贴3. 剪切与删除4. 撤销与恢复 &#x1f308; Ⅳ 底行模式1. 保存文件2. 查找字符3. 退出文件4. 替换内容5. 显示行号6. 外…...

KMP算法详解

1. 问题引入 链接&#xff1a;leetcode_28 题目&#xff1a;s1字符串是否包含s2字符串&#xff0c;如果包含返回s1中包含s2的最左开头位置&#xff0c;不包含返回-1 暴力方法就是s1的每个位置都做开头&#xff0c;然后去匹配s2整体&#xff0c;时间复杂度O(n*m) KMP算法可以…...

ubuntu22.04@laptop OpenCV Get Started: 013_contour_detection

ubuntu22.04laptop OpenCV Get Started: 013_contour_detection 1. 源由2. 应用Demo2.1 C应用Demo2.2 Python应用Demo 3. contour_approx应用3.1 读取图像并将其转换为灰度格式3.2 应用二进制阈值过滤算法3.3 查找对象轮廓3.4 绘制对象轮廓3.5 效果3.6 CHAIN_APPROX_SIMPLE v.s…...

[ai笔记5] 个人AI资讯助手实战

欢迎来到文思源想的ai空间&#xff0c;这是技术老兵重学ai以及成长思考的第5篇分享&#xff0c;也是把ai场景化应用的第一篇实操内容&#xff01; 既然要充分学习和了解ai&#xff0c;自然少不了要时常看看ai相关资讯&#xff0c;所以今天特地用字节的“扣子”做了一个ai的资讯…...

QT+OSG/osgEarth编译之八十九:osgdb_ply+Qt编译(一套代码、一套框架,跨平台编译,版本:OSG-3.6.5插件库osgdb_ply)

文章目录 一、osgdb_ply介绍二、文件分析三、pro文件四、编译实践一、osgdb_ply介绍 斯坦福三角形格式(Stanford Triangle Format)是一种用于存储三维模型数据的文件格式,也称为 PLY 格式。它最初由斯坦福大学图形实验室开发,用于存储和共享三维扫描和计算机图形数据。 P…...

机器人专题:我国机器人产业园区发展现状、问题、经验及建议

今天分享的是机器人系列深度研究报告&#xff1a;《机器人专题&#xff1a;我国机器人产业园区发展现状、问题、经验及建议》。 &#xff08;报告出品方&#xff1a;赛迪研究院&#xff09; 报告共计&#xff1a;26页 机器人作为推动工业化发展和数字中国建设的重要工具&…...

算法沉淀——哈希算法(leetcode真题剖析)

算法沉淀——哈希算法 01.两数之和02.判定是否互为字符重排03.存在重复元素04.存在重复元素 II05.字母异位词分组 哈希算法&#xff08;Hash Algorithm&#xff09;是一种将任意长度的输入&#xff08;也称为消息&#xff09;映射为固定长度的输出的算法。这个输出通常称为哈希…...

深入理解Redis哨兵原理

哨兵模式介绍 在深入理解Redis主从架构中Redis 的主从架构中&#xff0c;由于主从模式是读写分离的&#xff0c;如果主节点&#xff08;master&#xff09;挂了&#xff0c;那么将没有主节点来服务客户端的写操作请求&#xff0c;也没有主节点给从节点&#xff08;slave&#…...

MySQL-存储过程(PROCEDURE)

文章目录 1. 什么是存储过程&#xff1f;2. 存储过程的优点3. MySQL中的变量3.1 系统变量3.2 用户自定义变量3.3 局部变量 4. 存储过程的相关语法4.1 创建存储过程&#xff08;CREATE&#xff09;4.2 查看存储过程&#xff08;SHOW&#xff09;4.3 修改存储过程&#xff08;ALT…...

linux系统监控工具prometheus的安装以及监控mysql

prometheus 安装服务端客户端监控mysql prometheus浏览器查看 安装 https://prometheus.io/download/下载客户端和服务端以及需要监控的所有的包服务端 官网下载下载prometheustar -xf prometheus-2.47.2.linux-amd64.tar.gz -C /usr/local/ cd /usr/local/ mv prometheus-2.…...

初识tensorflow程序设计模式

文章目录 建立计算图tensorflow placeholdertensorflow数值运算常用的方法 tensorboard启动tensorboard的方法 建立一维与二维张量建立一维张量建立二维张量建立新的二维张量 矩阵的基本运算矩阵的加法矩阵乘法与加法 github地址https://github.com/fz861062923/TensorFlow 建…...

【QT+QGIS跨平台编译】之三十八:【GDAL+Qt跨平台编译】(一套代码、一套框架,跨平台编译)

文章目录 一、gdal介绍二、文件下载三、文件分析四、pro文件五、编译实践一、gdal介绍 GDAL(Geospatial Data Abstraction Library)是一个用于读取、写入和处理地理空间数据的开源库。它支持多种栅格和矢量地理空间数据格式,包括常见的GeoTIFF、Shapefile、NetCDF、HDF5等,…...

黑马鸿蒙教程学习1:Helloworld

今年打算粗略学习下鸿蒙开发&#xff0c;当作兴趣爱好&#xff0c;通过下华为那个鸿蒙开发认证&#xff0c; 发现黑马的课程不错&#xff0c;有视频和完整的代码和课件下载&#xff0c;装个devstudio就行了&#xff0c;建议32G内存。 今年的确是鸿蒙大爆发的一年呀&#xff0c;…...

蓝桥杯每日一题------背包问题(四)

前言 前面讲的都是背包的基础问题&#xff0c;这一节我们进行背包问题的实战&#xff0c;题目来源于一位朋友的询问&#xff0c;其实在这之前很少有题目是我自己独立做的&#xff0c;我一般习惯于先看题解&#xff0c;验证了题解提供的代码是正确的后&#xff0c;再去研究题解…...

OpenAI发布Sora技术报告深度解读!真的太强了!

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&#xff1a;洲与AI。 &#x1f388; 本文专栏&#xff1a;本文收录…...

AJAX——接口文档

1 接口文档 接口文档&#xff1a;描述接口的文章 接口&#xff1a;使用AJAX和服务器通讯时&#xff0c;使用的URL&#xff0c;请求方法&#xff0c;以及参数 传送门&#xff1a;AJAX阶段接口文档 <!DOCTYPE html> <html lang"en"><head><meta c…...

leetcode hot100不同路径

本题可以采用动态规划来解决。还是按照五部曲来做 确定dp数组&#xff1a;dp[i][j]表示走到&#xff08;i&#xff0c;j&#xff09;有多少种路径 确定递推公式&#xff1a;我们这里&#xff0c;只有两个移动方向&#xff0c;比如说我移动到&#xff08;i&#xff0c;j&#x…...

【前端工程化面试题目】webpack 的热更新原理

可以在顺便学习一下 vite 的热更新原理&#xff0c;请参考这篇文章。 首先有几个知识点需要明确 热更新是针对开发过程中的开发服务器的&#xff0c;也就是 webpack-dev-serverwebpack 的热更新不需要额外的插件&#xff0c;但是需要在配置文件中 devServer属性中配置&#x…...

不花一分钱,在 Mac 上跑 Windows(M1/M2 版)

这是在 MacOS M1 上体验最新 Windows11 的效果&#xff1a; VMware Fusion&#xff0c;可以运行 Windows、Linux 系统&#xff0c;个人使用 licence 免费 安装流程见 &#x1f449; https://zhuanlan.zhihu.com/p/452412091 从申请 Fusion licence 到下载镜像&#xff0c;再到…...

Attempt to call an undefined function glutInit

Attempt to call an undefined function glutInit 解决方法&#xff1a; 从这里下载PyOpenGL 的whl安装文件&#xff0c; https://drive.google.com/drive/folders/1mz7faVsrp0e6IKCQh8MyZh-BcCqEGPwx 安装命令举栗 pip install PyOpenGL-3.1.7-cp39-cp39-win_amd64.whl pi…...

AB测试最小样本量

1.AB实验过程 常见的AB实验过程&#xff0c;分流-->实验-->数据分析-->决策&#xff1a;分流&#xff1a;用户被随机均匀的分为不同的组实验&#xff1a;同一组内的用户在实验期间使用相同的策略&#xff0c;不同组的用户使用相同或不同的策略。数据收集&#xff1a;…...

在Spring中事务失效的场景

在Spring框架中&#xff0c;事务管理是通过AOP&#xff08;面向切面编程&#xff09;实现的&#xff0c;主要依赖于Transactional注解。然而&#xff0c;在某些情况下&#xff0c;事务可能会失效。以下是一些可能导致Spring事务失效的常见场景&#xff1a; 非public方法&#…...

Rust 学习笔记 - 变量声明与使用

前言 任何一门编程语言几乎都脱离不了&#xff1a;变量、基本类型、函数、注释、循环、条件判断&#xff0c;这是一门编程语言的语法基础&#xff0c;只有当掌握这些基础语法及概念才能更好的学习 Rust。 变量介绍 Rust 是一种强类型语言&#xff0c;但在声明变量时&#xf…...

windows 下跑起大模型(llama)操作笔记

原贴地址&#xff1a;https://testerhome.com/topics/39091 前言 国内访问 chatgpt 太麻烦了&#xff0c;还是本地自己搭一个比较快&#xff0c;也方便后续修改微调啥的。 之前 llama 刚出来的时候在 mac 上试了下&#xff0c;也在 windows 上用 conda 折腾过&#xff0c;环…...

人工智能专题:基础设施行业智能化的基础设施,自智网络双价值分析

今天分享的是人工智能系列深度研究报告&#xff1a;《人工智能专题&#xff1a;基础设施行业智能化的基础设施&#xff0c;自智网络双价值分析》。 &#xff08;报告出品方&#xff1a;埃森哲&#xff09; 报告共计&#xff1a;32页 自智网络驱动的电信产业变革 经过多年的…...

docker 编译安装redis脚本

在Docker中编译安装Redis通常不是一个常见的做法&#xff0c;因为Redis官方提供了预编译的Docker镜像&#xff0c;这些镜像包含了已经编译好的Redis二进制文件。不过&#xff0c;如果你有特殊需求&#xff0c;想要自己从源代码编译Redis并打包成Docker镜像&#xff0c;你可以使…...

鸿蒙开发系列教程(二十三)--List 列表操作(2)

列表样式 1、设置内容间距 在列表项之间添加间距&#xff0c;可以使用space参数&#xff0c;主轴方向 List({ space: 10 }) { … } 2、添加分隔线 分隔线用来将界面元素隔开&#xff0c;使单个元素更加容易识别。 startMargin和endMargin属性分别用于设置分隔线距离列表侧…...