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

UDP客户端服务器通信

        在这篇博客中,我们将探索 UDP(用户数据报协议) 通信,简要地说,UDP 是一种无连接、快速但不可靠的通信协议,适用于需要快速数据传输但对丢包容忍的场景,比如视频流和在线游戏。就像《我是如此相信》的图片所表达的那样,UDP 没有过多的保障机制,它直接、简单、快捷,就像信念一样,直接而坚定。 

1、网络协议中的下三层主要解决的是,数据安全可靠得发送到远端机器

2、用户使用应用层软件,完成数据的发送和接收,先把软件启动起来,那就是进程,因此网络通信的本质就是进程间的通信。

一、认识端口号

端口号无论是客户端还是服务器,都能唯一的标识该主机上的一个特定应用层的进程,ip地址唯一的标识一个主机,端口号只能用来标识该主机上的唯一进程。IP+Port能够标识全网唯一的进程。

二、套接字

客户端和服务器通信的时候就好像插头和插座的关系

端口号VS进程pid,但是pid已经可以唯一标识一个进程,为什么还要端口号?

1、不是所有的进程都要网络通信

2、pid属于操作系统中的进程管理,但是要是系统出问题,网络部分也要改,牵一发而动全身,因此要解耦。

我们的客户端怎么知道服务器的端口号?每个服务器的端口必须是众所周知的,被客户端知道。

一个进程可以绑定多个端口号,一个端口号不可以被多个进程绑定。经过哈希找的进程就不一样了

2、  套接字的种类:域间(本地通信)、原始(编写网络工具,抓包)和网络套接字

三、UDP和TCP

TCP(Transmission Control Protocol,传输控制协议)网络必须连通

  • 面向连接:在数据传输之前,发送端和接收端需要通过三次握手建立连接。这一过程确保了双方都准备好进行数据传输,并且知道对方的存在和状态,为可靠的数据传输奠定基础。例如,当你使用浏览器访问一个网站时,浏览器与网站服务器之间会先建立 TCP 连接,然后才开始传输网页数据.
  • 可靠性高:TCP 协议通过多种机制来确保数据的准确无误传输。它使用确认机制,接收方在收到数据后会向发送方发送确认消息,发送方只有在收到确认后才会继续发送下一部分数据,否则会重传未确认的数据。同时,TCP 还具备重传机制,对于在传输过程中丢失或损坏的数据,发送方会自动重新发送,直到数据被正确接收。此外,流量控制和拥塞控制也是 TCP 保证可靠性的重要手段,它可以根据网络状况和接收方的处理能力,动态调整数据传输的速率,避免网络拥塞导致数据丢失.
  • 数据有序:TCP 会对数据进行编号和排序,确保数据按照发送的顺序到达接收端。即使数据在网络中经过不同的路径传输,到达接收端时也会被正确地重组,保证应用程序能够接收到完整、有序的数据。例如,在文件传输过程中,文件的各个部分会按照顺序依次传输并在接收端正确拼接。
  • 适用于多种应用:由于其可靠性高,TCP 适用于对数据准确性要求严格的应用场景,如文件传输(FTP)、电子邮件(SMTP、POP3 等)、远程登录(Telnet)、网页浏览(HTTP、HTTPS)等,这些应用需要确保数据的完整性和准确性,不容许数据丢失或出错。

UDP(User Datagram Protocol,用户数据报协议)

  • 无连接:UDP 在发送数据之前不需要与接收方建立连接,发送方可以随时向目标地址发送数据报。这使得 UDP 的传输过程更加简单、快速,减少了连接建立和拆除的开销1.
  • 不可靠传输:UDP 不提供数据传输的可靠性保证,它不会对数据进行确认、重传或排序等操作。数据报在网络中可能会出现丢失、重复或乱序的情况,并且 UDP 本身不会对这些问题进行处理,而是由应用层来负责处理数据的可靠性问题。不过,在一些对实时性要求较高但对数据准确性要求相对较低的应用中,这种不可靠性是可以接受的,例如在线游戏中的玩家位置信息更新、视频会议中的语音和视频数据传输等,偶尔丢失一两个数据包不会对整体效果产生太大影响1.
  • 低延迟:由于不需要建立连接和进行复杂的可靠性控制,UDP 的传输延迟相对较低,能够快速地将数据发送到目标地址。这对于实时性要求高的应用非常有利,如音频流、视频流等实时多媒体应用,可以在保证一定流畅度的前提下,尽可能减少延迟1.
  • 高效性:UDP 协议的头部开销较小,只有 8 个字节,相比 TCP 的 20 个字节头部,UDP 可以在相同的网络带宽下传输更多的数据,提高了数据传输的效率1.
  • 适用于特定应用:UDP 常用于一些对实时性和效率要求较高,但对数据可靠性要求相对较低的应用场景,如网络视频会议、在线游戏、音频流播放、实时监控等。例如,在网络视频会议中,即使偶尔丢失一些视频帧,也不会对会议的进行产生严重影响,而较低的延迟可以保证参会者之间的交互更加流畅。

 四、网络字节序 

1、判断是小端还是大端 小权重放到小地址就是小端(小小小)

大端:数据的高位字节存于低地址,低位字节存于高地址。 

程序:判断机器是大端还是小端?

#include <stdio.h>
int main() {int num = 0x12345678;char *ptr = (char *)&num;if (*ptr == 0x78) {printf("Little - Endian\n");} else if (*ptr == 0x12) {printf("Big - Endian\n");} else {printf("Error\n");}return 0;
}

2、网络规定都是大端 ,低地址高字节

五、UDP客户端服务器通信 

步骤:1、创建UDP套接字 下面为创建套接字的接口 socket         

第一个参数表示的是使用的协议家族,域到底是什么,第二个表示定义的套接字类型,流,还是数据报,第三个参数表示协议参数,填0就行

返回值 On success, a file descriptor for the new socket is returned.  On error, -1 is returned, and errno is set appropriately. 

           2、绑定 bind

插入:如何快速地将整数ip转为字符串? 比如12345667转为0.0.36.123 ,我们经常用的就是点分十进制,但是我们要知道如何转换。有接口inet_addr

【0,255】.【0,255】.【0,255】.【0,255】

struct ip
{uint8_t part1;  //占一个字节uint8_t part2;uint8_t part3;uint8_t part4;
}
int src_ip=12345678;
struct ip *p=(struct ip*)src_ip; //由于 src_ip 是一个 32 位的整数,而 struct ip 是一个包含四个 uint8_t 类型字段的结构体,直接把 src_ip 赋值给 struct ip 类型的变量是不可能的(类型不兼容)。但是,通过指针,我们可以将 src_ip 的内存解释为 struct ip 类型,并按字节访问它。
to_string(p->part1)+"."+to_string(p->part2)+"."+to_string(p->part3)+"."+to_string(p->part4);就把四字节转换为字符串风格的ip
结果是0.0.3.182

字符串转整数 ”192.168.50.100“  ”192“ ”168“”50“”100“

//192.168.50.100 转换为 123455556
uint32_t Ip; 4个字节
struct ip* x=(struct ip*)&Ip;
x->part1=stoi("192");
x->part2=stoi("168");
x->part3=stoi("50");
x->part4=stoi("100");

查看服务器启动的命令:netstat -nau

 一个关于ip

云服务器禁止直接bind公网ip,一个机器可能有多个ip,bind(IP:0),IP为0叫做任意地址绑定凡是发给我这台主机的数据,我们都要根据端口号向上交付。 

 一个关于port

【0,1023】属于系统内定的端口号,一般都要有固定的应用层协议使用 http:80 https:443 mysql:3306 我们绑定1024以上的

结论:IP地址绑定0,端口号绑定1024以上的

六、程序实现

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 logmessage(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\n", leftbuffer, rightbuffer);//     // printf("%s", logtxt); // 暂时打印//     printLog(level, logtxt);// }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;
};// int sum(int n, ...)
// {
//     va_list s; // char*
//     va_start(s, n);//     int sum = 0;
//     while(n)
//     {
//         sum += va_arg(s, int); // printf("hello %d, hello %s, hello %c, hello %d,", 1, "hello", 'c', 123);
//         n--;
//     }//     va_end(s); //s = NULL
//     return sum;
// }
Main.cc
#include "UdpServer.hpp"
#include <memory>
#include "Log.hpp"
Log log;void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<"proc[1024+]"<<std::endl;
}
//./udpserver port
//"192.168.1.123"点分十进制字符串风格的ip地址,一共占了13个字节
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());svr->Init();svr->Run();return 0;
}
Makefile
.PHONY:all
all:Udpserver Udpclient
Udpserver:Main.ccg++ -o $@ $^ -std=c++11
Udpclient:UdpClient.ccg++ -o $@ $^ -std=c++11
.PHONY:
clean: rm -f Udpserver Udpclient
Udpclient
#include<iostream>
#include<unistd.h>
#include<cstdlib>
#include<sys/types.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<"serverip serverport"<<std::endl;
}
// ./udpclient serverip serverport 启动客户端的时候,必须要是ip和端口号是是什么
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;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());socklen_t len = sizeof(server);int sockfd=socket(AF_INET,SOCK_DGRAM,0);//client要绑定,要有自己的IP和端口,只不过不需要用户显示的bind,一般由os随机选择!肯定是客户端先向服务器发起请求,端口号能唯一标识客户端就行,随机分配,但是服务器的端口号必须要准确知道,不然客户端绑定哪一个//系统什么时候给我Bind,首次放松数据的时候就绑定,sendtostring message;char buffer[1024];//接收时候的bufferwhile(1){cout<<"Please Enter@";std::getline(cin,message);cout<<message<<endl;//1.数据 2.给谁发 &server 发送给serversendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,len);struct sockaddr_in temp;socklen_t len=sizeof(temp);//服务器发消息处理了之后,再给客户端发回来,客户端就要去接收ssize_t s=recvfrom(sockfd,buffer,1023,0,(struct sockaddr*)&temp,&len);  //倒数第二个是谁发的if(s>0){buffer[s]=0;}cout<<buffer<<endl;}close(sockfd);return 0;
};
Udpserver
#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<string>
#include<netinet/in.h>
#include<arpa/inet.h>
#include "Log.hpp"
extern Log log;
enum{SOCKET_ERR=1,BIND_ERR=2
};
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):port_(port),sockfd_(0),ip_(ip),isrunning_(false)//一个服务器要启动起来,端口号要有,Ip地址也要有{}void Init(){sockfd_=socket(AF_INET,SOCK_DGRAM,0);//创建套接字if(sockfd_<0){log(Fatal,"socket create error,sockfd:%d",sockfd_);exit(SOCKET_ERR);}printf("socket create success,sockfd:%d\n",sockfd_);log(Info,"socket create success,sockfd:%d",sockfd_);struct sockaddr_in local;bzero(&local,sizeof(local));//把结构体里面的内容全部清零 ,第二个参数是缓冲区的大小local.sin_family=AF_INET;local.sin_port=htons(port_); //我要给你发数据,你必须知道我的端口号,我要把我的端口号发给你,因此端口号是需要在网络中传输的,必须是网络字节序列//local.sin_addr.s_addr=inet_addr(ip_.c_str());  //1.string->uint32_t   将string转char* string.c_str()local.sin_addr.s_addr=INADDR_ANY;//0,任意绑定,因为是全0,主机转网络不需要做bind(sockfd_,(const struct sockaddr *)&local,sizeof(local));}//服务器应该周而复始的运行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,(sockaddr*)&client,&len);if(n<0){log(Warning,"recvform error");continue;}inbuffer[n]=0;//对字符串进行加工std::string info=inbuffer;std::string echo_string="server echo#"+info;//再把数据发送回给对方 string的长度  string.size//你本地的套接字;发送的字符串和长度;默认为0;输入性参数,我知道对方是谁,因为当时对方给我发过消息sendto(sockfd_,echo_string.c_str(),echo_string.size(),0,(const sockaddr*)&client,len);}}~UdpServer(){if(sockfd_>0)  close(sockfd_);}
private:int sockfd_;//网络文件描述符uint16_t port_; //端口号是两个字节,16个比特位,服务器要有自己的端口号std::string ip_;//任意地址绑定,填为0bool isrunning_;
};

改进版:

Main.cc
#include "UdpServer.hpp"
#include <memory>
#include <cstdio>
#include "Log.hpp"
Log log;void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<"proc[1024+]"<<std::endl;
}
std::string Handler(const std::string &str)
{std::string res="Server get a message:";res+=str;return res;//服务器收到消息,回调式的调用你传进来的方法,把数进行加工,加工处理完后返回,最后把这个结果发出去
}
std::string ExecuteCommand(const std::string &cmd)
{// SafeCheck(cmd);FILE* fp=popen(cmd.c_str(),"r");if(nullptr==fp){perror("popen");return "error";}std::string result;//从fp中把命令执行结果都拿出来char buffer[4096];while(true){char * res=fgets(buffer,4096,fp);//读到一行if(nullptr==res) break;result+=buffer;}return result;pclose(fp);
}
//./udpserver port
//"192.168.1.123"点分十进制字符串风格的ip地址,一共占了13个字节
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());svr->Init();svr->Run(ExecuteCommand);return 0;
}
Udpserver.hpp
#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<string>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <functional>
#include "Log.hpp"
using func_t=std::function<std::string(const std::string&)>; //返回值为string,参数为const std::string&的函数传递给Udpserver
//等价于typedef std::function<std::string(const std::string&)> func_t;
extern Log log;
enum{SOCKET_ERR=1,BIND_ERR=2
};
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):port_(port),sockfd_(0),ip_(ip),isrunning_(false)//一个服务器要启动起来,端口号要有,Ip地址也要有{}void Init(){sockfd_=socket(AF_INET,SOCK_DGRAM,0);//创建套接字if(sockfd_<0){log(Fatal,"socket create error,sockfd:%d",sockfd_);exit(SOCKET_ERR);}printf("socket create success,sockfd:%d\n",sockfd_);log(Info,"socket create success,sockfd:%d",sockfd_);struct sockaddr_in local;bzero(&local,sizeof(local));//把结构体里面的内容全部清零 ,第二个参数是缓冲区的大小local.sin_family=AF_INET;local.sin_port=htons(port_); //我要给你发数据,你必须知道我的端口号,我要把我的端口号发给你,因此端口号是需要在网络中传输的,必须是网络字节序列//local.sin_addr.s_addr=inet_addr(ip_.c_str());  //1.string->uint32_t   将string转char* string.c_str()local.sin_addr.s_addr=INADDR_ANY;//0,任意绑定,因为是全0,主机转网络不需要做bind(sockfd_,(const struct sockaddr *)&local,sizeof(local));}//服务器应该周而复始的运行//这样的好处是实现代码的分层void Run(func_t func)//运行的时候需要用户传进来一个怎么处理数据的方法{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,(sockaddr*)&client,&len);if(n<0){log(Warning,"recvform error");continue;}inbuffer[n]=0;std::string info=inbuffer;std::string echo_string =func(info);sendto(sockfd_,echo_string.c_str(),echo_string.size(),0,(const sockaddr*)&client,len);}}~UdpServer(){if(sockfd_>0)  close(sockfd_);}
private:int sockfd_;//网络文件描述符uint16_t port_; //端口号是两个字节,16个比特位,服务器要有自己的端口号std::string ip_;//任意地址绑定,填为0bool isrunning_;
};

相关文章:

UDP客户端服务器通信

在这篇博客中&#xff0c;我们将探索 UDP&#xff08;用户数据报协议&#xff09; 通信&#xff0c;简要地说&#xff0c;UDP 是一种无连接、快速但不可靠的通信协议&#xff0c;适用于需要快速数据传输但对丢包容忍的场景&#xff0c;比如视频流和在线游戏。就像《我是如此相信…...

适合中小型公司的自动化测试的测试框架,OpenSourceTest

适合中小型公司的自动化测试的测试框架&#xff0c;OpenSourceTest 文档地址&#xff1a; http://docs.opensourcetest.cn/代码仓库&#xff1a; https://github.com/chineseluo/opensourcetest安装方式&#xff1a; pip3 install opensourcetest -i https://pypi.tuna.tsin…...

实现跨语言通信:Rust 和 Thrift 的最佳实践

前言 在分布式系统中&#xff0c;服务之间高效且安全的通信至关重要。Apache Thrift 是一个被广泛应用的跨语言 RPC&#xff08;远程过程调用&#xff09;框架&#xff0c;它支持多种编程语言&#xff0c;包括 Rust。Rust 以其卓越的性能和内存安全保障&#xff0c;成为越来越…...

js判断空对象

1. 使用 Object.keys() 方法 Object.keys(obj) 方法返回一个包含对象可枚举属性名称的数组。如果返回的数组长度为 0&#xff0c;表示对象为空。 const isEmpty (obj) > Object.keys(obj).length 0;// 示例 const emptyObject {}; const nonEmptyObject { key: value …...

visionpro官方示例分析(一) 模板匹配工具 缺陷检测工具

1.需求&#xff1a;找出图像中的这个图形。 2.步骤 使用CogPMAlignTool工具&#xff0c;该工具是模板匹配工具&#xff0c;见名知意&#xff0c;所谓模板匹配工具就是说先使用该工具对一张图像建立模板&#xff0c;然后用这个模板在其他图像上进行匹配&#xff0c;匹配上了就说…...

PyCharm中Python项目打包并运行到服务器的简明指南

目录 一、准备工作 二、创建并设置Python项目 创建新项目 配置项目依赖 安装PyInstaller 三、打包项目 打包为可执行文件 另一种打包方式&#xff08;使用setup.py&#xff09; 四、配置服务器环境 五、上传可执行文件到服务器 六、在服务器上运行项目 配置SSH解释…...

cocos creator 3.8 合成大西瓜Demo 11

界面上的Node节点&#xff1a; 背景 警戒线 三面墙 初始位置节点 水果容器 先分组吧&#xff0c;墙 地板 水果 创建预制体 先挂一个脚本 刚体碰撞器先弄上再说 import { _decorator, Component, Node } from cc; const { ccclass, property } _decorator;ccclass(FruitData) e…...

Vue前端开发-动态插槽

不仅父组件可以通过插槽方式访问并控制子组件传入的数据&#xff0c;而且可以控制传入父组件时插槽的名称&#xff0c;从而使不同的插槽根据名称的不同&#xff0c;使用场景也不同&#xff0c;例如在一个小区详细页中&#xff0c;可以根据小区类型&#xff0c;调用不同名称的详…...

使用easyexcel导出复杂模板,同时使用bean,map,list填充

背景 在使用easyexcel导出时&#xff0c;如果遇到一个模板中同时存在 一部分是实体类中的字段&#xff0c;另外部分是列表的字段&#xff0c;需要特殊处理一下&#xff0c;比如下面的模板&#xff1a; 这里面 user&#xff0c; addr 是实体类&#xff08;或者map&#xff09…...

最大值(Java Python JS C++ C )

题目描述 给定一组整数(非负),重排顺序后输出一个最大的整数。 示例1 输入:[10,9] 输出:910 说明:输出结果可能非常大,所以你需要返回一个字符串而不是整数。 输入描述 数字组合 输出描述 最大的整数 示例1 输入 10 9输出 910解题思路 题目要求 是:给定一…...

17.5k Star,ThingsBoard 一款开源、免费、功能全面的物联网 IoT 平台 -慧知开源充电桩平台

项目介绍 ThingsBoard是一个开源、免费、功能全面、灵活易用的物联网&#xff08;IoT&#xff09;平台&#xff0c;专注于数据收集、处理、可视化以及设备管理。它提供了一个全面的解决方案&#xff0c;用于构建和管理物联网应用。支持从各种设备收集数据&#xff0c;通过内置…...

《C++ 与神经网络:自动微分在反向传播中的高效实现之道》

在深度学习蓬勃发展的今天&#xff0c;神经网络成为了众多领域的核心技术驱动力。而反向传播算法作为训练神经网络的关键手段&#xff0c;其背后的自动微分技术的高效实现尤为重要&#xff0c;特别是在 C 这样追求性能与内存控制极致的编程语言环境下。 神经网络通过大量的参数…...

【CSS】设置文本超出N行省略

文章目录 基本使用 这种方法主要是针对Webkit浏览器&#xff0c;因此可能在一些非Chrome浏览器中不适用。 基本使用 例如&#xff1a;设置文本超出两行显示省略号。 核心代码&#xff1a; .ellipsis-multiline {display: -webkit-box; -webkit-box-orient: vertical; /* 设置…...

open-instruct - 训练开放式指令跟随语言模型

文章目录 关于 open-instruct设置训练微调偏好调整RLVR 污染检查开发中仓库结构 致谢 关于 open-instruct github : https://github.com/allenai/open-instruct 这个仓库是我们对在公共数据集上对流行的预训练语言模型进行指令微调的开放努力。我们发布这个仓库&#xff0c;并…...

DI依赖注入详解

DI依赖注入 声明了一个成员变量&#xff08;对象&#xff09;之后&#xff0c;在该对象上面加上注解AutoWired注解&#xff0c;那么在程序运行时&#xff0c;该对象自动在IOC容器中寻找对应的bean对象&#xff0c;并且将其赋值给成员变量&#xff0c;完成依赖注入。 AutoWire…...

TDengine在debian安装

参考官网文档&#xff1a; 官网安装文档链接 从列表中下载获得 Deb 安装包&#xff1b; TDengine-server-3.3.4.3-Linux-x64.deb (61 M) 进入到安装包所在目录&#xff0c;执行如下的安装命令&#xff1a; sudo dpkg -i TDengine-server-<version>-Linux-x64.debNOTE 当…...

【C#设计模式(15)——命令模式(Command Pattern)】

前言 命令模式的关键通过将请求封装成一个对象&#xff0c;使命令的发送者和接收者解耦。这种方式能更方便地添加新的命令&#xff0c;如执行命令的排队、延迟、撤销和重做等操作。 代码 #region 基础的命令模式 //命令&#xff08;抽象类&#xff09; public abstract class …...

XGBoost库介绍:提升机器学习模型的性能

XGBoost库介绍&#xff1a;提升机器学习模型的性能 在机器学习领域&#xff0c;模型的准确性和训练效率是最为关注的两大因素。特别是在处理大量数据和复杂任务时&#xff0c;传统的机器学习算法可能无法满足高效和准确性的需求。XGBoost&#xff08;eXtreme Gradient Boostin…...

网络安全构成要素

一、防火墙 组织机构内部的网络与互联网相连时&#xff0c;为了避免域内受到非法访问的威胁&#xff0c;往往会设置防火墙。 使用NAT&#xff08;NAPT&#xff09;的情况下&#xff0c;由于限定了可以从外部访问的地址&#xff0c;因此也能起到防火墙的作用。 二、IDS入侵检…...

SpringMVC——SSM整合

SSM整合 创建工程 在pom.xml中导入坐标 <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_…...

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇&#xff0c;在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下&#xff1a; 【Note】&#xff1a;如果你已经完成安装等操作&#xff0c;可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作&#xff0c;重…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)

上一章用到了V2 的概念&#xff0c;其实 Fiori当中还有 V4&#xff0c;咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务)&#xff0c;代理中间件&#xff08;ui5-middleware-simpleproxy&#xff09;-CSDN博客…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

Vite中定义@软链接

在webpack中可以直接通过符号表示src路径&#xff0c;但是vite中默认不可以。 如何实现&#xff1a; vite中提供了resolve.alias&#xff1a;通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...