基于udp协议的cs网络通信代码(echo版+命令行输入版+执行指令版),netstat指令
目录
引入
基础版
服务端
思路
头文件+log类
套接字的初始化
思路
代码
服务器开始运行
思路
代码
注意点 -- ip地址和端口号的来源
ip地址的选择
本地环回地址
端口号
编辑
运行情况
netstat -nlup
客户端
思路
初始化
思路
代码
客户端的运行
思路
代码
运行情况
运行情况
改成命令行输入
服务端
客户端
运行情况
远程让服务器执行命令
引入
分层
代码
运行情况
引入
我们将使用udp协议+ipv4协议来写cs之间的网络通信
- 将服务端运行在我们的云服务器上,其他机器可以运行客户端来向服务端发送消息
- 服务端收到消息后,经过一定处理直接发回给相应的客户端
- 并且,我们将一步一步改善我们的代码,让他变得更加美观+拥有更多功能
基础版
服务端将收到的数据经过包装后直接回传给发送端,类似于echo
服务端
思路
封装成一个类,提供运行接口即可
启动后创建套接字,并保持等待数据的状态
头文件+log类
下面是用到的头文件:
#include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <arpa/inet.h> #include <strings.h> #include <cstring>#include <string> #include <iostream>#include "Log.hpp"
以及用来提示消息的log类
数据格式 -- 时间+消息
#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 INFO 0 #define DEBUG 1 #define WARNING 2 #define ERROR 3 #define FATAL 4 // 致命的错误#define SIZE 1024class Log { public: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\n", logtxt);}~Log(){}private: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";}} };
套接字的初始化
思路
首先创建出套接字文件
- 这里使用udp协议+ipv4网络协议
- 所以,可以确定socket函数中使用的参数为AF_INET, SOCK_DGRAM
然后我们需要构建一个存放了网络地址信息(ip+端口号+其他信息)的结构体
- 传参传的是公共类型的结构体,但实际使用的还是那两个中的一个
- 所以需要手动构建一个
- 初始化可以用memset,也可以用bzero
端口号需要在网络中传输,但它最初是在类内初始化的,是跟随系统字节序列来的
我们无法保证系统一定是大端
所以在将端口号传入结构体中时(网络中使用的端口号就是从这个结构体中存放的),需要转换为网络字节序列
同理,ip地址也需要传输,所以也需要转换为网络序列
(ip地址和端口号的选择在后面讲)
我们虽然使用了系统提供的类型,但这里的变量依然是在用户空间中定义的,并没有和内核中的套接字产生关联
- 套接字需要这个结构体中的数据,将数据传进内核的套接字里,才叫做绑定
- 前面的都只是预备工作,实际的绑定工作由bind函数完成 -- 通过网络文件描述符和结构体对象,将文件信息和网络信息相关联
以上,服务器端的套接字就建立完成
代码
void init(){// 创建套接字文件sockfd_ = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd_ < 0){lg(FATAL, "socket create error, sockfd : %d,%s", sockfd_,strerror(errno));exit(SOCKET_ERR);}// 创建sockaddr结构struct sockaddr_in addr;socklen_t len = sizeof(addr);bzero(&addr, len);addr.sin_addr.s_addr = inet_addr(ip_.c_str());addr.sin_family = AF_INET;addr.sin_port = htons(port_);// 绑定套接字信息int res = bind(sockfd_, reinterpret_cast<const struct sockaddr *>(&addr), len);if (res < 0){lg(FATAL, "bind error, sockfd : %d,%s", sockfd_,strerror(errno));exit(BIND_ERR);}lg(INFO, "bind success, sockfd : %d", sockfd_);}
服务器开始运行
思路
服务端负责获取客户端发送的数据
- 使用recvfrom函数获得数据报形式的数据(也就是定义一个缓冲区)
- 且拿到发送方的网络地址信息(结构体),方便将响应数据发回给对方
拿到数据后,进行处理
- 这里在消息前封装一些标识字段 -- ip+端口号+发送时间
处理完后,就发回给发送方
- 使用sendto函数发送数据报
代码
std::string generate_id(const std::string ip, const uint16_t port){return "[" + ip + ":" + std::to_string(port) + "]";} std::string process_info(const std::string &info){time_t t = time(nullptr);struct tm *ctime = localtime(&t);char time_stamp[SIZE];snprintf(time_stamp, sizeof(time_stamp), "[%d-%d-%d %d:%d:%d]:",ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);std::string id = generate_id(ip_, port_);std::string res = id + time_stamp + info + "\n";return res;}void run(){init();// 开始收发数据char buffer[buff_size];std::string message;while (true){memset(buffer, 0, sizeof(buffer));struct sockaddr_in src_addr;socklen_t src_len = sizeof(src_addr);// 获取数据ssize_t n = recvfrom(sockfd_, buffer, sizeof(buffer) - 1, 0, reinterpret_cast<struct sockaddr *>(&src_addr), &src_len);if (n < 0){lg(WARNING, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}buffer[n] = 0;std::string id = generate_id(inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));message = id + "sever recvfrom success";lg(INFO, message.c_str());// 处理数据std::string echo_info = process_info(buffer);// 响应给发送端sendto(sockfd_, echo_info.c_str(), echo_info.size(), 0, reinterpret_cast<const struct sockaddr *>(&src_addr), src_len);message = id + "sever sendto success";lg(INFO, message.c_str());}}
注意点 -- ip地址和端口号的来源
ip地址的选择
可以使用默认ip,也可以自己手动传入
udp_server(const uint16_t port = 8080,const std::string ip = "0.0.0.0"): ip_(ip), port_(port), sockfd_(0){}
#include "udp_server.hpp"int main() {udp_server s;//udp_server s(8080,"127.0.0.1");s.run();return 0; }
这里如果服务端直接连接云服务器的公有ip,会报错:
- 因为云服务器禁止直接绑定公网ip
- 一台主机上可能会配置多个网卡,你看到的ip只是其中一个
- 如果就固定地绑定了那一个,你的服务器就只能收到那一个ip地址收到的报文,其他ip收到的就看不到了
- 所以,如果我们将ip地址写成0(也就是那个"0.0.0.0"),就可以将发往这台主机(无论是哪张网卡,它会动态识别是否属于该主机)的所有信息都向上交付给对应端口
- 或者用宏 INIDDR_ANY
- 虚拟机是可以直接绑定ip的
本地环回地址
这里的127.0.0.1指的是本地环回地址
- 如果服务器绑定这个ip,就不会将数据发送到网络,而是贯穿网络协议栈后,重新发回本主机
- 也就是本地通信
端口号
所以,我们一般用大一点的数字分配给我们的服务端
运行情况
#include "udp_server.hpp" #include <cstdlib> using namespace std;int main() {udp_server s;// udp_server s(8080,"127.0.0.1");s.run();return 0; }
netstat -nlup
- n表示,把所有可以显示为数字的显示成数字,否则有些会是字符串
- p表示,显示pid信息
用于检查网络状态和网络连接:
每列表示的含义:
- 协议 收发报文的个数 本地ip地址+端口号 可以接收什么客户端的消息
只要可以查到信息,就说明这个服务器已经启动成功了
客户端
思路
和服务端的流程差不多,依然是封装成类,调用启动接口就可以启动客户端
启动后,就等待用户进行输入
初始化
思路
客户端也需要创建自己的套接字,但是不需要手动绑定
- os会为我们自由分配一个端口号(客户端的端口号没有什么要求,只需要保证最基本的唯一性即可),且将本机ip和端口号与创建出的套接字绑定
- os什么时候为我们绑定?
- 首次发送消息的时候,也就是需要网络通信的时候再绑定
因为需要将输入的数据发给服务端,所以就需要提前知道服务端的套接字地址信息
日常生活中,我们一般直接通过网址获得网站提供的服务
网址经过处理后,就是ip地址,而端口号是固定的,一个ip地址就会有对应的固定端口号
所以我们知道网址=客户端知道了ip地址=服务端的套接字被我们获取到
但这里我们写的很简陋,只能手动提供服务端的ip和端口号,然后构建出服务端的网络地址信息(结构体)
(使用的接口还是那些)
代码
struct data {int sockfd_;struct sockaddr_in *paddr_;socklen_t len_; };data *init(){// 创建套接字文件int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){lg(FATAL, "socket create error, sockfd : %d", sockfd);exit(SOCKET_ERR);}// 创建sockaddr结构struct sockaddr_in *svr_paddr = new sockaddr_in;socklen_t svr_len = sizeof(*svr_paddr);bzero(svr_paddr, svr_len);svr_paddr->sin_addr.s_addr = inet_addr(ip_.c_str());svr_paddr->sin_family = AF_INET;svr_paddr->sin_port = htons(port_);return new data({sockfd, svr_paddr, svr_len});}
客户端的运行
思路
就是等待用户输入,然后发送给服务端
然后等待服务端的响应数据
最后将服务端处理后的数据打印出来
代码
void run(){data *d = init();std::string info;char buffer[buff_size];while (true){std::cout << "Please enter:";std::getline(std::cin, info);// 将消息发送给服务器sendto(d->sockfd_, info.c_str(), info.size(), 0, reinterpret_cast<const struct sockaddr *>(d->paddr_), d->len_);info.clear();struct sockaddr_in addr; // 仅用于填充参数,拿到自己的地址信息没啥意义socklen_t len = sizeof(addr);// 获取数据ssize_t n = recvfrom(d->sockfd_, buffer, sizeof(buffer) - 1, 0, reinterpret_cast<struct sockaddr *>(&addr), &len);if (n < 0){lg(WARNING, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}buffer[n] = 0;std::cout << buffer << std::endl;memset(buffer, 0, sizeof(buffer));}}
运行情况
#include "udp_client.hpp" #include <cstdlib> using namespace std;int main() {udp_client c;//udp_client c(8080,"127.0.0.1");c.run();return 0; }
运行情况
这样我们就完成了cs互相通信的过程
改成命令行输入
上面我们是在代码中定义了ip地址和端口号,也可以以命令行的形式,将服务器的ip地址和端口号输入
- 也就是利用main函数的参数
- 只需要改一下主函数代码即可
服务端
#include "udp_server.hpp"
#include <cstdlib>
using namespace std;//./udp_server port
int main(int argc, char *argv[])
{if (argc != 2){std::cout << "./udp_server port(port>=1024)" << std::endl;exit(1);}udp_server s(atoi(argv[1]));s.run();return 0;
}
客户端
#include "udp_client.hpp"
#include <cstdlib>
using namespace std;//./udp_client ip port
int main(int argc, char *argv[])
{while (argc != 3){std::cout << "./udp_client ip port(port>=1024)" << std::endl;exit(1);}udp_client s(atoi(argv[2]), argv[1]); // port,ips.run();return 0;
}
运行情况
如果输入的内容不足,就会提示:
然后cs端都运行起来,并发送消息:
远程让服务器执行命令
引入
除了简单的转发消息,我们也可以发送命令,远程让服务器执行命令,且将执行结果传给客户端,让我们可以看到
- 这不就是我们的shell吗
- 其实之前有模拟过简单的shell(创建子进程,使用exec系列函数,让子进程去执行命令)
或者直接用popen函数
它会在内部帮助我们创建一条管道,且创建子进程+执行命令,然后把执行后的结果写入到管道文件中,以文件指针的形式返回给我们
command
- 要执行的shell命令
mode
- 表示要打开的管道的模式
- 看你想要如何与命令交互:
- 如果是只想要查看命令的输出,就以"r"打开 (ls)
- 如果想要将一些数据发送给它作为输入,就以"w"打开 (cat)
分层
所以我们考虑改变一下代码结构,将处理数据与服务器收到数据解耦
- 也就是在服务端处理数据时,使用自定义的处理方法(以回调的方式使用)
代码
服务端类里面修改的不多
在处理数据那里,我们直接调用参数:
void run(func_t func){init();// 开始收发数据char buffer[buff_size];std::string message;while (true){memset(buffer, 0, sizeof(buffer));struct sockaddr_in src_addr;socklen_t src_len = sizeof(src_addr);// 获取数据ssize_t n = recvfrom(sockfd_, buffer, sizeof(buffer) - 1, 0, reinterpret_cast<struct sockaddr *>(&src_addr), &src_len);if (n < 0){lg(WARNING, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));continue;}buffer[n] = 0;std::string id = generate_id(inet_ntoa(src_addr.sin_addr), ntohs(src_addr.sin_port));message = id + "sever recvfrom success";lg(INFO, message.c_str());// 处理数据std::string echo_info = func(buffer);// 响应给发送端sendto(sockfd_, echo_info.c_str(), echo_info.size(), 0, reinterpret_cast<const struct sockaddr *>(&src_addr), src_len);message = id + "sever sendto success";lg(INFO, message.c_str());}}
因为我们需要将处理函数放在类外,所以多增加了一个接口,用于外部调用
static std::string get_id() //外部使用{udp_server obj;return obj.generate_id(obj.ip_, obj.port_);}private:std::string generate_id(const std::string ip, const uint16_t port){return "[" + ip + ":" + std::to_string(port) + "]";}
主函数这里多修改了一些:
- 分出两个处理函数
- 把获取时间的功能也单拎了出来
#include "udp_server.hpp" #include <cstdlib> #include <cstdio> using namespace std;std::string get_time() {time_t t = time(nullptr);struct tm *ctime = localtime(&t);char time_stamp[SIZE];snprintf(time_stamp, sizeof(time_stamp), "[%d-%d-%d %d:%d:%d]:",ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);return time_stamp; }std::string process_info(const std::string &info) {std::string time_stamp = get_time();std::string id = udp_server::get_id();std::string res = id + time_stamp + info;return res; }std::string process_command(const std::string &cmd) {std::string time_stamp = get_time();std::string id = udp_server::get_id();std::string res;FILE *fp = popen(cmd.c_str(), "r");if (fp == nullptr){lg(ERROR, "popen error, errno: %d, err string: %s", errno, strerror(errno));return "";}char buffer[buff_size];memset(buffer, 0, sizeof(buffer));if (fgets(buffer, sizeof(buffer) - 1, fp) == nullptr){res = id + time_stamp + "command error";return res;}while (true){memset(buffer, 0, sizeof(buffer));if (fgets(buffer, sizeof(buffer) - 1, fp) != nullptr){res += buffer;}else{break;}}return res; } int main() {udp_server s;// udp_server s(8080,"127.0.0.1");s.run(process_command);return 0; }
运行情况
如果输入的不是命令,会提示给用户:
如果是命令,就会返回给我们运行结果:
相关文章:

基于udp协议的cs网络通信代码(echo版+命令行输入版+执行指令版),netstat指令
目录 引入 基础版 服务端 思路 头文件log类 套接字的初始化 思路 代码 服务器开始运行 思路 代码 注意点 -- ip地址和端口号的来源 ip地址的选择 本地环回地址 端口号 编辑 运行情况 netstat -nlup 客户端 思路 初始化 思路 代码 客户端的运行 思…...
centos7网络命令:ping、dig、nsloopup、tcpdump
目录 一、ping1、命令参数:2、示例-将当前的信息打印到一个文件中3、示例-结束进程 二、dig1、安装2、语法格式选项说明 3、示例4、示例-将当前的信息打印到一个文件中 三、nslookup1、安装2、语法格式选项说明 3、示例 四、tcpdump抓包1、安装2、语法格式ÿ…...

Excel判断CD两列在EF两列的列表中是否存在
需求 需要将CD两列的ID和NAME组合起来,查询EF两列的ID和NAME组合起来的列表中是否存在? 比如,判断第二行的“123456ABC”在EF的第二行到第四行中是否存在,若存在则显示Y,不存在则显示N 实现的计算公式 IF(ISNUMBER…...

基于斑翠鸟优化算法(Pied Kingfisher Optimizer ,PKO)的无人机三维路径规划(MATLAB)
一、无人机路径规划模型介绍 二、算法介绍 斑翠鸟优化算法(Pied Kingfisher Optimizer ,PKO),是由Abdelazim Hussien于2024年提出的一种基于群体的新型元启发式算法,它从自然界中观察到的斑翠鸟独特的狩猎行为和共生关系中汲取灵感。PKO 算法围绕三个不同的阶段构建:栖息…...
同程旅行前端面试汇总
一、同程旅行一面 自我介绍技术提问 打开新的tab页、window.open是否共享sessionStorage存储的数据vue、react 源码有没有看过,说一下react17 与 react18区别webpack中loader与plugin,有没有自己写过vuex、reactx 刷新数据丢失 怎么做的持久化 反问 总…...
小美的平衡矩阵_dp思路
小美的平衡矩阵 写在前面: 本博客只是一种解题思路的提供。 小美的平衡矩阵 题目描述: 小美拿到了一个n*n 的矩阵,其中每个元素是 0 或者 1。 小美认为一个矩形区域是完美的,当且仅当该区域内 0 的数量恰好等于 1 的数量。 现在…...

json展示curl 请求接口返回结果
使用curl发送请求并将返回结果以JSON格式展示,通常需要确保请求的响应本身就是JSON格式。可以结合jq这个JSON处理工具来格式化输出。 首先要安装jq 工具。 Linux发行版中,你可以使用包管理器来安装它。 sudo yum install jq # 对于CentOS/RHEL 安装成…...

2024 年排名前 5 名的 Mac 数据恢复软件分享
如果您已经在 Mac 上丢失了数据并且正在寻找恢复数据的方法,那么您来对地方了。互联网上有超过 50 个适用于 Mac 的数据恢复程序。哪个是最好的 Mac 数据恢复软件?不用担心。本文列出了 5 款 Mac 数据恢复软件,可帮助您在 Mac OS 下恢复丢失的…...
请描述一下Spring MVC的工作流程。在Spring MVC中,DispatcherServlet的作用是什么?
请描述一下Spring MVC的工作流程。 Spring MVC 的工作流程是基于请求驱动的,它围绕 Servlet 设计,将请求映射到处理器,处理器处理请求并返回响应。以下是 Spring MVC 的基本工作流程: 发送请求: 客户端(例…...

2023年终总结——跌跌撞撞不断修正
目录 一、回顾1.一月,鼓足信心的开始2.二月,焦躁不安3.三月,路还是要一步一步的走4.四月,平平淡淡的前行5.五月,轰轰烈烈的前行6.六月,看事情更底层透彻了7.七月,设计模式升华月8.八月ÿ…...

OPPO后端二面,凉了!
这篇文章的问题来源于一个读者之前分享的 OPPO 后端凉经,我对比较典型的一些问题进行了分类并给出了详细的参考答案。希望能对正在参加面试的朋友们能够有点帮助! Java String 为什么是不可变的? public final class String implements java.io.Seri…...

Unity3d版白银城地图
将老外之前拼接的Unity3d版白银城地图,导入到国内某手游里,改成它的客户端地图模式,可以体验一把手游的快乐。 人物角色用的是它原版的手游默认的,城内显示效果很好,大家可以仔细看看。 由于前期在导入时遇到重大挫折&…...

【PCL】(二十八)点云超体素分割
(二十九)点云超体素分割 论文:Voxel Cloud Connectivity Segmentation - Supervoxels for Point Clouds supervoxel_clustering.cpp #include <pcl/console/parse.h> #include <pcl/point_cloud.h> #include <pcl/point_ty…...

Socket通信Demo(Unity客户端和C#)
Socket通信基本流程 首先要启动服务器创建Socket,然后要绑定服务器的一个端口这样客户端通过服务器IP端口号就能连接到服务器了服务器接下来会设置监听队列,监听并等待要连接到它的客户端客户端在服务器启动之后也建立自己的Socket,然后使用…...
Lucene 自定义词库
import org.apache.lucene.analysis.hunspell.Dictionary; import org.apache.lucene.analysis.hunspell.HunspellStemFilter; import...

【LeetCode热题100】73. 矩阵置零(矩阵)
一.题目要求 给定一个 m x n 的矩阵,如果一个元素为 0 ,则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 二.题目难度 中等 三.输入样例 示例 1: 输入:matrix [[1,1,1],[1,0,1],[1,1,1]] 输出:[[1,0…...

使用Barrier共享鼠标键盘,通过macos控制ubuntu系统
之前文章写过如何使用barrrier通过windows系统控制ubuntu系统,该文章将详细介绍如何使用barrier通过macos系统控制ubuntu系统 一、macOS安装barrier macOS版本barrier链接 1、双击点开安装包 2、将安装包里的barrier拷贝到macOS的达达->应用程序中 3、在达达…...

c++:类和对象中:拷贝构造和赋值运算符重载详解
c:类和对象 构造函数和析构函数详解 文章目录 c:类和对象构造函数和析构函数详解 前言一、拷贝构造怎么写拷贝构造1.拷贝构造也是构造函数的一种,构造函数没有值.所以拷贝构造也没有返回值**2.拷贝构造只有一个形参,正常这个形参是自定义类型对象的引用.3. 如果我们没有显示写…...

Day33:安全开发-JavaEE应用SQL预编译Filter过滤器Listener监听器访问控制
目录 JavaEE-预编译-SQL JavaEE-过滤器-Filter JavaEE-监听器-Listen 思维导图 Java知识点 功能:数据库操作,文件操作,序列化数据,身份验证,框架开发,第三方库使用等. 框架库:MyBatis&#…...
Log4j如何支持多线程环境?你如何优化Log4j的性能?
Log4j如何支持多线程环境? Log4j 通过其内部设计来支持多线程环境,确保在多线程应用程序中能够安全地使用。以下是 Log4j 支持多线程环境的一些关键方面: 线程安全性: Log4j 的 Logger 类和 Appender 类都是设计为线程安全的。这…...
golang sync.Pool 指针数据覆盖问题
场景 1. sync.Pool设置 var stringPool sync.Pool{New: func() any {return new([]string)}, }func NewString() *[]string {v : stringPool.Get().(*[]string)return v }func PutString(s *[]string) {if s nil {return}if cap(*s) > 2048 {s nil} else {*s (*s)[:0]…...
VUE+内置iframe传值失效问题解决
起因: 公司业务需要计算建筑物截收面积,然后我采用的是openCV来计算,在vue内部引用不了,然后就采用了iframe原生html来完成;功能实现了我想让iframe和vue通信;然后用原有方式试了多次都失败了,i…...

Day31:安全开发-JS应用WebPack打包器第三方库JQuery安装使用安全检测
目录 打包器-WebPack-使用&安全 第三方库-JQuery-使用&安全 思维导图 JS知识点: 功能:登录验证,文件操作,SQL操作,云应用接入,框架开发,打包器使用等 技术:原生开发&…...
Go Zero微服务个人探究之路(十六)回顾api服务和rpc服务的本质
目录 前言 正文 API(Application Programming Interface) RPC(Remote Procedure Call) API 与 RPC 的关系 分布式部署 API 和 RPC 结语 前言 go-zero 是一个基于 Go 语言的微服务框架,它提供了一套简洁的编程模…...

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的夜间车辆检测系统(深度学习代码+UI界面+训练数据集)
摘要:开发夜间车辆检测系统对于自动驾驶技术具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个夜间车辆检测系统,并提供了完整的实现代码。该系统基于强大的YOLOv8算法,并对比了YOLOv7、YOLOv6、YOLOv5,展示了不同模型间…...
Spring体系架构
目录 核心容器(Core Container) 数据访问/集成(Data Access/Integration) Web开发(Web)...

【PLC】现场总线和工业以太网汇总
1、 现场总线 1.1 什么是现场总线 1)非专业描述: 如下图:“人机界面”一般通过以太网连接“控制器(PLC)”,“控制器(PLC)”通过 “现场总线”和现场设备连接。 2)专业描述(维基百科) 现场总线…...

【吊打面试官系列】Java虚拟机JVM篇 - 关于JVM分析
大家好,我是锋哥。今天分享关于JVM分析的JVM面试题,希望对大家有帮助; 查看JVM进程号的命令是什么? 可以使用 ps ‐ef 和 jps ‐v 等等。 怎么查看剩余内存? 比如: free ‐m, free ‐h, top 命令等等。 1000道 互联网大厂Jav…...

Mysql锁与MVCC
文章目录 Mysql锁的类型锁使用MVCC快照读和当前读读视图【Read View】串行化的解决 exlpain字段解析ACID的原理日志引擎整合SpringBoot博客记录 Mysql锁的类型 MySQL中有哪些锁: 乐观锁(Optimistic Locking):假设并发操作时不会发…...

rancher是什么
Rancher Labs是制作Rancher的公司。Rancher Labs成立于2014年,是一家专注于企业级容器管理软件的公司。它的产品设计旨在简化在分布式环境中部署和管理容器的过程,帮助企业轻松地采用容器技术和Kubernetes。Rancher Labs提供的Rancher平台支持Docker容器…...