【计算机网络】基于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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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系…