【計算機網絡】基于UDP進行socket編程——實現服務端與客戶端業務

🔥個人主頁🔥:孤寂大仙V
🌈收錄專欄🌈:Linux
🌹往期回顧🌹: 【Linux筆記】——網絡基礎
🔖流水不爭,爭的是滔滔不息


  • 一、UDPsocket編程
    • UDPsocket編程的一些基本接口
    • 封裝sockaddr_in
  • 二、單線程網絡聊天室
    • Udpserver.hpp服務端
    • Udpclient.cc
  • 三、多線程網絡聊天室
  • 四、網絡字典

一、UDPsocket編程

UDP(User Datagram Protocol)是一種無連接的傳輸層協議,提供快速但不可靠的數據傳輸。與TCP不同,UDP不保證數據包的順序、可靠性或重復性,適用于實時性要求高的場景(如視頻流、游戲)。

UDPsocket編程的一些基本接口

創建套接字

int socket(int domain, int type, int protocol);

創建UDP套接字,第一個參數是選擇ipv4還是ipv6,第二個參數是選擇UDP還是TCP,第三個參數為0就可以。
ipv4寫AF_INET,ipv6寫 AF_INET6 。UDP寫SOCK_DGRAM,TCP寫SOCK_STREAM 。

套接字綁定本地地址和端口(服務端用)

int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

第一個參數是套接字,第二個參數是指向 struct sockaddr 的指針,用于指定本地地址(需要強制類型轉換),第三個參數是這個sockaddr_in的結構體。

向指定目標發送數據

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

第一個參數套接字,第二個參數是存儲發送信息的buffer,第二個是這個buffer的大小,第三個標志位填0就行,第四個發送數據那端的struct sockaddr的指針,第五個是struct sockaddr的大小。

接收數據

 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

第一個參數套接字,第二個參數接收buffer,第三個是這個buffer的大小,第四個是標準位寫0就行,第五個是struct sockaddr的指針,第六個是struct sockaddr的大小的指針

關閉套接字

close()

和關閉文件描述符一樣

封裝sockaddr_in

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>using namespace std;class InetAddr
{
public:InetAddr(struct sockaddr_in& Addr) //從網絡中也就是客戶端獲取的信息:_Addr(Addr){_ip=inet_ntoa(_Addr.sin_addr);_port=ntohs(_Addr.sin_port);}string Ip(){return _ip;}uint16_t Port(){return _port;}string StringAddr() const{return _ip+":"+to_string(_port);}const struct sockaddr_in& NAddr(){return _Addr;}bool operator==(const InetAddr& Addr){return Addr._ip==_ip && Addr._port==_port;}~InetAddr(){}private:struct sockaddr_in _Addr;string _ip;uint16_t _port;
};

每次寫服務端或者是客戶端的時候都要寫struct sockaddr_in那一套太麻煩了,所以進行封裝。比如說上面的這個構造,就是從客戶端中傳來的信息轉化為主機地址。

注意啊,ip和端口號,網絡到主機需要轉換序列,主機到網絡也需要轉換序列。

二、單線程網絡聊天室

一個網絡聊天室需要客戶端和服務端,客戶端用戶使用,然后把信息推送給服務端服務器接收到信息把信息路由給所有用戶這時網絡聊天室就形成了。所以客戶端需要有發送的功能,服務器有接收的功能。

Udpserver.hpp服務端


#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "Log.hpp"
#include "InetAddr.hpp"using namespace std;
using namespace LogModule;
using func_t=function<void(int ,const string&,InetAddr&)>; 
class Udpserver
{
public:Udpserver(uint16_t port,func_t func):_port(port),_sockfd(0),_isrunning(false),_func(func){}void Init(){_sockfd =socket(AF_INET, SOCK_DGRAM,0);if(_sockfd <0){LOG(LogLevel::FATAL)<<"創建套接字失敗";exit(1);}LOG(LogLevel::INFO)<<"創建套接字成功";struct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_addr.s_addr=INADDR_ANY;local.sin_port=htons(_port);int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n<0){LOG(LogLevel::FATAL)<<"綁定失敗";exit(1);}LOG(LogLevel::INFO)<<"綁定成功";}void Start(){_isrunning=true;while(true){char buffer[128];struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t n=recvfrom(_sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,&len);if(n>0){InetAddr client(peer);buffer[n]=0;_func(_sockfd,buffer,client);}}}~Udpserver(){}
private:int _sockfd;uint16_t _port;bool _isrunning;func_t _func;};

這里要引入一個結構體,這個結構體是存儲本地ip地址和端口信息的結構體變量。
**struct sockaddr_in local; 是在進行 IPv4 網絡編程時,定義的一個用于存儲本地 IP 地址和端口信息的結構體變量,屬于 IPv4 地址族的套接字地址結構體。**這是一個用于存儲 IPv4 地址信息 的結構體,定義在頭文件 <netinet/in.h> 中。它是給 bind()、connect()、sendto()、recvfrom() 等函數傳參用的,通常你寫服務器或客戶端都要用到它。

void Init(){_sockfd =socket(AF_INET, SOCK_DGRAM,0);if(_sockfd <0){LOG(LogLevel::FATAL)<<"創建套接字失敗";exit(1);}LOG(LogLevel::INFO)<<"創建套接字成功";struct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_addr.s_addr=INADDR_ANY;local.sin_port=htons(_port);int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));if(n<0){LOG(LogLevel::FATAL)<<"綁定失敗";exit(1);}LOG(LogLevel::INFO)<<"綁定成功";}

初始化,創建套接字,綁定本地的地址和端口號,這是socket編程必須寫的。在bind之前創建了套接字地址結構體,用來存儲本地服務器的ip和端口號,注意這個ip我們設置成了INADDR_ANY,因為這里是服務器不要設置成固定的ip。寫成INADDR_ANY保證ip是動態的。


void Start(){_isrunning=true;while(true){char buffer[128];struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t n=recvfrom(_sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,&len);if(n>0){InetAddr client(peer);buffer[n]=0;_func(_sockfd,buffer,client);}}}

服務端接收信息,這里的socket_in peer 是客戶端傳來的套接字地址結構體。用recvfrom接收信息如果能夠接收到信息,返回值大于0,執行這個回調函數。回調到udpserver.cc。

#include "Udpserver.hpp"
#include "Route.hpp"
#include <memory>using namespace std;
using namespace LogModule;int main(int argc, char *argv[])
{uint16_t port = stoi(argv[1]);Enable_Console_Log_Strategy(); // 啟用控制臺輸出Route r; // 服務器路由unique_ptr<Udpserver> usvr = make_unique<Udpserver>(port,[&r](int _sockfd, const string &messages, InetAddr &peer){ r.MessageRoute(_sockfd, messages, peer); });usvr->Init();usvr->Start();return 0;
}

Udpserver 作為底層網絡接收模塊,不關心具體如何處理數據,它只負責接收,然后通過你傳入的 回調函數,把接收到的數據“上傳”給上層(這里是 Route::MessageRoute())去決定如何處理 —— 這就是典型的 解耦設計。回調到這里進行路由,路由的對象已經實例化好了,到這里直接在lambda表達式中直接對路由對象的函數進行調用就可以了。下面是路由的方法。

#pragma once#include <iostream>
#include <string>
#include <vector>
#include "Log.hpp"
#include "InetAddr.hpp"
#include "Udpserver.hpp"using namespace std;
using namespace LogModule;
using namespace MutexModule;
class Route
{
public:Route(){}bool Exist(InetAddr &peer){for (auto &user : _online_user){if (user == peer){return true;}}return false;}void Adduser(InetAddr &peer){LOG(LogLevel::INFO) << "新增了一個在線用戶" <<peer.StringAddr();_online_user.push_back(peer);}void DeleteUser(InetAddr &peer){for (auto iter = _online_user.begin(); iter != _online_user.end(); iter++){if (*iter == peer){LOG(LogLevel::INFO) << "刪除了一個在線用戶" <<peer.StringAddr();_online_user.erase(iter);break;}}}void MessageRoute(int sockfd, const std::string &message, InetAddr &peer) // 路由功能{LockGuard lockguard(_mutex);  // 加鎖if (!Exist(peer)){Adduser(peer);}string send_messages = peer.StringAddr() + "#" + message; // 發過來的信息for (auto &user : _online_user){sendto(sockfd, send_messages.c_str(), send_messages.size(), 0, (struct sockaddr *)&(user.NAddr()), sizeof(user.NAddr()));}// 這個用戶一定已經在線了if (message == "QUIT"){LOG(LogLevel::INFO) << "刪除一個在線用戶: " << peer.StringAddr();DeleteUser(peer);}}~Route(){}private:vector<InetAddr> _online_user;Mutex _mutex;
};

路由服務,就是把服務器收到了信息,分發給所有客戶端的用戶。加鎖線程安全,如果這個用戶不存在就在存儲用戶的數組中新加進去,遍歷整個數組把信息都發回去。如果這個用戶已經在線了就刪除這個用戶。

本項目采用回調機制實現業務邏輯與底層網絡模塊的解耦。Udpserver 僅負責網絡收發,而具體的處理邏輯通過 lambda 回調函數注冊在 main() 中,實現了業務的靈活注入和高擴展性。


Udpclient.cc

客戶端要有發送信息的能力,也有有收到應答的能力,服務器有路由功能客戶端要接收路由的信息。

#include <iostream>
#include <string>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Thread.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"using namespace std;
using namespace LogModule;
using namespace ThreadModule;int _sockfd = 0;
string server_ip;
uint16_t server_port = 0;
pthread_t id;void Recv()
{while (true){char buffer[128];struct sockaddr_in peer;socklen_t len=sizeof(peer);int n = recvfrom(_sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer, &len);if (n > 0){buffer[n] = 0;cout << buffer << endl;}}
}void Send()
{struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(server_ip.c_str());server.sin_port = htons(server_port);while (true){string input;cout << "請輸入" << endl;getline(cin, input);int n = sendto(_sockfd, input.c_str(), input.size(), 0, (struct sockaddr *)&server, sizeof(server));if (n < 0){LOG(LogLevel::FATAL) << "客戶端發送信息失敗";}if (input == "QUIT"){pthread_cancel(id);break;}}
}int main(int argc, char *argv[])
{server_ip = argv[1];server_port = stoi(argv[2]);_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "創建套接字失敗";}LOG(LogLevel::INFO) << "創建套接字成功";// 2. 創建線程Thread recver(Recv);Thread sender(Send);recver.Start();sender.Start();recver.Join();sender.Join();return 0;
}

這里設計的是單線程的,客戶端創建的時候,創建兩個線程一個收一個發。收方法,就還是老一套recvfrom。發方法還是sendto。

單線程版網絡聊天室:源碼

在這里插入圖片描述

三、多線程網絡聊天室

Udpserver.hpp

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "ThreadPool.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"using namespace std;
using namespace LogModule;using func_t =function<void (int sockfd,const string&,InetAddr&)>;const int defaultfd = -1;class Udpserver
{
public:Udpserver(uint16_t port,func_t func):_port(port),_sockfd(defaultfd),_isrunning(false),_func(func){}void Inet() //初始化{_sockfd=socket(AF_INET,SOCK_DGRAM,0);//創建套接字if(_sockfd<0){LOG(LogLevel::FATAL)<<"socket false";exit(1);}LOG(LogLevel::INFO)<<"socket success";struct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(_port);local.sin_addr.s_addr=INADDR_ANY;int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local)); //綁定if(n<0){LOG(LogLevel::FATAL)<<"bind false";exit(2);}LOG(LogLevel::INFO)<<"bind success";}void Start(){_isrunning=true;while (true){char buffer[1024]; //存收到的信息struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t n=recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);//收if(n>0){buffer[n]=0;InetAddr cli(peer);_func(_sockfd,buffer,cli);}}}~Udpserver(){}
private:bool _isrunning;int _sockfd;uint16_t _port;func_t _func;
};

服務器與單線程版本一模一樣。

Udpserver.cc

#include "Udpserver.hpp"
#include "Route.hpp"
#include <memory>using namespace std;
using namespace ThreadPoolModule;using task_t = std::function<void()>;int main(int argc,char* argv[])
{Enable_Console_Log_Strategy(); // 啟用控制臺輸出uint16_t port=stoi(argv[1]);Route r;auto tp = ThreadPool<task_t>::GetInstance();// unique_ptr<Udpserver> usvr = make_unique<Udpserver>(port, //     [&r](int _sockfd, const string &messages, InetAddr &peer)//     { r.MessageRoute(_sockfd, messages, peer); });unique_ptr<Udpserver> u= make_unique<Udpserver>(port,[&r,&tp](int _sockfd, const string &messages, InetAddr &peer){task_t t=bind(&Route::MessageRoute,&r,_sockfd,messages,peer);tp->Enqueue(t);});u->Inet();u->Start();return 0;
}

我們做的是一個多線程 UDP 網絡聊天室服務端。為了提升并發能力,我們引入了一個線程池(ThreadPool)。網絡部分(接收數據)使用的是 Udpserver,它在接收到消息后通過 回調機制 把業務邏輯“丟”出去。回調函數用的是 lambda 表達式,其中 bind 了 Route::MessageRoute() 和收到的參數,形成一個任務。這個任務再被投遞到 線程池 中執行,實現了網絡收發和邏輯處理解耦 + 多線程并發處理。

Udpclient.cc

#include <iostream>
#include <string>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Thread.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"using namespace std;
using namespace LogModule;
using namespace ThreadModule;int sockfd = 0;
string server_ip;
uint16_t server_port = 0;
pthread_t id;void Recv()
{while(true){char buffer[1024];struct sockaddr_in peer;socklen_t len=sizeof(peer);ssize_t n=recvfrom(sockfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);if(n>0){buffer[n]=0;cout<<buffer<<endl;}}
}void Send()
{struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family=AF_INET;server.sin_port=htons(server_port);server.sin_addr.s_addr=inet_addr(server_ip.c_str());const std::string online = "inline";sendto(sockfd, online.c_str(), online.size(), 0, (struct sockaddr *)&server, sizeof(server));while(true){string input;cout<<"請輸入"<<endl;getline(cin,input);ssize_t n=sendto(sockfd,input.c_str(),input.size(),0,(struct sockaddr*)&server,sizeof(server));if (n < 0){LOG(LogLevel::FATAL) << "客戶端發送信息失敗";}if (input == "QUIT"){pthread_cancel(id);break;}}}int main(int argc,char* argv[])
{server_ip=argv[1];server_port=stoi(argv[2]);sockfd=socket(AF_INET,SOCK_DGRAM,0);if(sockfd<0){LOG(LogLevel::FATAL)<<"socket false";}LOG(LogLevel::INFO)<<"socket success";Thread recver(Recv);Thread sender(Send);recver.Start();sender.Start();id = recver.Id();recver.Join();sender.Join();return 0;
}

基本也與單線程版本一樣。

注意:
這里引入線程池是服務器高并發接收和處理客戶端消息,比如多個客戶端同時發消息,需要并發處理。客戶端還是需要分別創建了兩個線程分別是收線程和發線程。即使服務端用了線程池,客戶端也要至少兩個線程:一個發一個收,這樣聊天室才能像樣地用起來。客戶端的多線程不是為了并發處理請求,是為了讓用戶能“邊說邊聽”。

在這里插入圖片描述
在這里插入圖片描述
多線程網絡聊天室:源碼

四、網絡字典

Udpserver.hpp

#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "Log.hpp"
#include "InetAddr.hpp"
#include "Dict.hpp"
using namespace LogModule;
using namespace std;const int num = -1;
using func_t = function<string(const string&,InetAddr&)>;//class Udpserver
{
public:Udpserver(uint16_t port,func_t func): _sockfd(num), _port(port), _isrunning(false),_func(func){}void Init(){_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::ERROR) << "套接字創建失敗";}LOG(LogLevel::INFO) << "套接字創建成功";struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(LogLevel::ERROR) << "綁定失敗";}LOG(LogLevel::INFO) << "綁定成功";}void Start(){_isrunning=true;while(true){char buffer[128];struct sockaddr_in peer;memset(&peer,0,sizeof(peer));socklen_t len=sizeof(peer);//收客戶端傳來的信息ssize_t n=recvfrom(_sockfd,buffer,sizeof(buffer),0,(struct sockaddr*)&peer,&len);if(n>0){buffer[n]=0;InetAddr client(peer);string result= _func(buffer,client);//這里設計為收完就發sendto(_sockfd,result.c_str(),result.size(),0,(struct sockaddr*)&peer,len);}}}~Udpserver(){}private:int _sockfd;uint16_t _port;bool _isrunning;func_t _func;
};

都是相同的操作,注意一下收到客戶端傳來的信息,回調去翻譯,然后做出應答翻譯完發回去。

Dict.hpp翻譯模塊

#pragma once
#include <iostream>
#include <string>
#include <map>
#include <fstream>
#include "Udpserver.hpp"
#include "InetAddr.hpp"const string dpath = "./dictionary.txt";
const string sep = ": ";using namespace LogModule;
using namespace std;class Dict
{
public:Dict(string path=dpath):_dict_path(path){}bool LoadDict(){  ifstream in(_dict_path);if(!in.is_open()){LOG(LogLevel::WARNINC)<<"字典打開失敗";return false;}string line;while(getline(in,line)){auto pos=line.find(sep);if(pos==string::npos){LOG(LogLevel::WARNINC)<<"解析"<<line<<"失敗";continue;}string english=line.substr(0,pos);string chinese=line.substr(pos + sep.size());if(english.empty() || chinese .empty()){LOG(LogLevel::WARNINC) << "沒有有效內容: " << line;continue;}_dict.insert(make_pair(english,chinese));}in.close();return true;}string Translate(const string& word,InetAddr& client){auto iter=_dict.find(word);if(iter == _dict.end()){LOG(LogLevel::DEBUG)<<"進入翻譯模塊"<<"["<<client.Ip()<<":"<<client.Port()<<"]"<<word<<"->None";return "None";}LOG(LogLevel::DEBUG)<<"進入翻譯模塊"<<"["<<client.Ip()<<":"<<client.Port()<<"]"<<word<<"->iter->sencond";return iter->second;}~Dict(){}private:string _dict_path;unordered_map<string,string> _dict;
};

用文件流的方式打開英漢文本,判斷是否打開成功,從文件流中逐行讀取,找到中文和英文中間的:,如果沒有找到:解釋失敗。截取一行中的中文和英文,把中文和英文插入map中。上面說的可以算是初始化字典。下面Translate是真正的翻譯模塊,在字典中查找要查的單詞,判斷是否有這個單詞,有單詞返回map中的sencod不就是對應的翻譯了嗎。

Udpserver.cc

#include "Udpserver.hpp"
#include "Dict.hpp"
#include <memory>
int main(int argc,char* argv[])
{Enable_Console_Log_Strategy(); // 啟用控制臺輸出uint16_t port=stoi(argv[1]);Dict dict;dict.LoadDict();unique_ptr<Udpserver> u=make_unique<Udpserver>(port,[&dict](const string& word,InetAddr& cli)->string{return dict.Translate(word,cli);});u->Init();u->Start();return 0;
}

創建dict對象,LoadDict初始化字典,這里是udpserver.hpp中回調函數到這里進行翻譯,lambda表達式這里來執行翻譯的操作。

在這里插入圖片描述
udp網絡字典:源碼


通過上面的單線程網絡聊天室、多線程網絡聊天室、網絡字典、我們發現都用到了回調函數,來對不同部分進行模塊化,解耦合。網絡設計本身就是基于之前我們說過分層模型設計的,網絡回調機制與協議分層理念是一脈相承的 —— 都在追求模塊化、解耦、職責單一。回調機制本質上是 事件驅動 + 分層解耦 的產物。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/82247.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/82247.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/82247.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ae卡通打架煙霧特效

1、創建一個合成&#xff08;合成1&#xff09;&#xff0c;右鍵創建形狀圖層&#xff0c;使用橢圓工具&#xff0c;長按shift鍵拖動鼠標左鍵畫出圓形&#xff0c;同時按ctrlalthome三個鍵使圓形中心錨點對齊圓心&#xff0c;關閉描邊&#xff0c;圓形圖層填充白色。 2、選擇形…

UE5 Va Res發送請求、處理請求、json使用

文章目錄 介紹發送一個Get請求發送Post請求設置請求頭請求體帶添json發送請求完整的發送藍圖 處理收到的數據常用的json處理節點 介紹 UE5 自帶的Http插件&#xff0c;插件內自帶json解析功能 發送一個Get請求 只能寫在事件圖表里 發送Post請求 只能寫在事件圖表里 設置…

SQL 結構化模型設計與現代技術融合深度解讀

摘要 本文系統展示了基于 JSON Schema 的 SQL 結構化模型設計&#xff0c;包括通用定義、四大基本操作&#xff08;SELECT、INSERT、UPDATE、DELETE&#xff09;的模型規范&#xff0c;以及面向現代場景的設計擴展。重點結合數據權限控制、樂觀鎖并發控制、表單自動化、自定義…

el-dialog 組件 多層嵌套 被遮罩問題

<el-dialog title"提示" :visible.sync"dialogBindUserVisible" width"30%" append-to-body :before-close"handleClose"> <span>這是一段信息</span> <span slot"footer" class"dialog-footer&q…

【KWDB 2025 創作者計劃】_KWDB時序數據庫特性及跨模查詢

一、概述 數據庫的類型多種多樣&#xff0c;關系型數據庫、時序型數據庫、非關系型數據庫、內存數據庫、分布式數據庫、圖數據庫等等&#xff0c;每種類型都有其特定的使用場景和優勢&#xff0c;KaiwuDB 是一款面向 AIoT 場景的分布式、多模融合、支持原生 AI 的數據庫…

學習心得(12-13)HTML 是什么 abort函數and自定義異常

一. abort函數 將后端的數據給到前端 二. 自定義異常 要結合abort函數使用 1.編寫的時候都在abort的函數這個文件里面 錯誤信息在前端頁面的展示&#xff1a; 如果想要在出現異常的時候返回一個頁面&#xff1a; 1. 新建一個HTML文件 例如命名為404 2.將圖庫里的圖片拖入…

理解全景圖像拼接

1 3D到2D透視投影 三維空間上點 p 投影到二維空間 q 有兩種方式&#xff1a;1&#xff09;正交投影&#xff0c;2&#xff09;透視投影。 正交投影直接舍去 z 軸信息&#xff0c;該模型僅在遠心鏡頭上是合理的&#xff0c;或者對于物體深度遠小于其到攝像機距離時的近似模型。…

Linux基本指令篇 —— whoami指令

whoami 是 Linux 和 Unix 系統中一個簡單但實用的命令&#xff0c;全稱 Who Am I&#xff08;我是誰&#xff09;。它的功能是顯示當前登錄用戶的用戶名。以下是關于 whoami 的詳細解析&#xff1a; 目錄 1. 基本用法 2. 命令特點 3. 實際應用場景 場景 1&#xff1a;腳本中…

華為OD機試真題——仿LISP運算(2025B卷:200分)Java/python/JavaScript/C/C++/GO最佳實現

2025 B卷 200分 題型 本專欄內全部題目均提供Java、python、JavaScript、C、C++、GO六種語言的最佳實現方式; 并且每種語言均涵蓋詳細的問題分析、解題思路、代碼實現、代碼詳解、3個測試用例以及綜合分析; 本文收錄于專欄:《2025華為OD真題目錄+全流程解析+備考攻略+經驗分…

創建dummy

訪客_dc1fc4 class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int left0;int right0;int n nums.size(); int sum0;int ans100001; for(right0;right<n;right) { sumnums[right]; //每次更新右端點之后&#xff0c;立即向右移動…

面向惡劣條件的道路交通目標檢測----大創自用(當然你也可以在里面學到很多東西)

全部內容梳理 目標檢測的兩個任務&#xff1a; 預測標簽 邊界框 語義分割 實力分割 一個是類別 一個是實例級別 分類任務把每個圖像當作一張圖片看待 所有解決方法是先生成候選區域 再進行分類 置信度&#xff1a; 包括對類別和邊界框預測的自信程度 輸出分類和IOU分數的…

需求管理工具使用不當,如何優化?

要優化需求管理工具的使用&#xff0c;需從選擇合適工具、規范使用流程、加強用戶培訓、統一數據結構、定期審查與優化使用配置五個方面著手。其中&#xff0c;選擇合適工具是前提。錯誤的工具選擇往往會導致項目溝通效率低、需求追蹤失效甚至造成交付物偏離客戶預期。因此&…

openwrt虛擬機安裝調試

分類 lienol lean immortalwrt 一、獲取固件 &#xff08;1&#xff09;下載地址 1.官網構建下載 OpenWrt Firmware Selector 官網 OpenWrt Firmware Selector 2.第三方構建網站 ImmortalWrt Firmware Selector ImmortalWrt Firmware Selector 3.第三方構建下載 ht…

Apache OFBiz 17.12.01 的遠程命令執行漏洞 -Java 反序列化 + XML-RPC 請求機制

目錄 漏洞原理 &#x1f3af; 一、漏洞背景&#xff08;CVE-2020-9496&#xff09; ?? 二、攻擊原理簡述 &#x1f9f1; 三、完整攻擊流程步驟詳解 &#x1f50e; 1. 信息收集 &#x1f6e0;? 2. 工具準備 &#x1f9ea; 3. 構造初始 payload&#xff1a;下載惡意腳本…

最好用的wordpress外貿主題

產品展示獨立站wordpress主題 橙色的首頁大banner外貿英文wordpress主題&#xff0c;適合用于產品展示型的外貿網站。 https://www.jianzhanpress.com/?p8556 Machine機器wordpress模板 寬屏簡潔實用的wordpress外貿建站模板&#xff0c;適合工業機器生產、加工、制造的外貿…

Q1:Go協程、Channel通道 被close后,讀會帶來什么問題?

在 Go 語言中&#xff0c;Channel&#xff08;通道&#xff09;關閉后讀取的行為是一個常見但需要謹慎處理的問題。以下是詳細的分析和注意事項&#xff1a; 1. 關閉 Channel 后讀取的行為 (1) 讀取已關閉的 Channel 剩余數據仍可讀取&#xff1a; 關閉 Channel 后&#xff0…

【AI Study】第三天,Python基礎 - 同NumPy類似的類庫

學習計劃&#xff1a;AI Study&#xff0c;學習計劃源碼地址&#xff1a;https://github.com/co-n00b/AI-Study.git 2025-05-23 在學習NumPy的過程中&#xff0c;除了了解NumPy之外&#xff0c;我們也對比看看其他類似的類庫都有什么&#xff1f;各自的優缺點是什么&#xff1…

基于aspnet,微信小程序,mysql數據庫,在線微信小程序汽車故障預約系統

詳細視頻:【基于aspnet,微信小程序,mysql數據庫,在線微信小程序汽車故障預約系統。-嗶哩嗶哩】 https://b23.tv/zfqLWPV

人工智能100問?第32問:什么是遷移學習?

目錄 一、通俗解釋 二、專業解析 三、權威參考 遷移學習就是讓AI把在一個任務中學到的本事&#xff0c;拿來加速另一個任務的學習&#xff0c;實現“舉一反三”。 一、通俗解釋 想象你已經學會了打乒乓球&#xff0c;現在去學打網球&#xff0c;是不是會學得更快&#xff…

Linux之概述和安裝vm虛擬機

文章目錄 操作系統概述硬件和軟件操作系統常見操作系統 初識LinuxLinux的誕生Linux內核Linux發行版 虛擬機介紹虛擬機 VMware WorkStation安裝虛擬化軟件VMware WorkStation 安裝查看VM網絡連接設置VM存儲位置 在VMware上安裝Linux(發行版CentOS7)安裝包獲取CentOS7 安裝 Mac系…