【Linux】Socket编程—UDP
文章目录
- 1.Echo server
- 2. Dict server
- 3. ChatServer
1.Echo server
简单的回显服务器和客户端代码。
- 服务器代码:
#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include <cerrno>
#include <strings.h>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Log.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"using namespace LogModule;
using namespace InetAddrModule;const static int gsockfd = -1;
const static uint16_t gdefaultport = 8080;class UdpServer
{
public:UdpServer(uint16_t port = gdefaultport): _sockfd(gsockfd),_addr(port),_isrunning(false){}void InitServer(){// 1. 创建socket_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket: " << strerror(errno);Die(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;// 2. bind : 设置进入内核中int n = ::bind(_sockfd, _addr.NetAddr(), _addr.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind: " << strerror(errno);Die(BIND_ERR);}LOG(LogLevel::INFO) << "bind success";}bool Start(){_isrunning = true;while (true){char inbuffer[1024]; // stringstruct sockaddr_in peer;socklen_t len = sizeof(peer); // 必须设定ssize_t n = ::recvfrom(_sockfd, inbuffer, sizeof(inbuffer)-1, 0, CONV(&peer),&len);if(n > 0){inbuffer[n] = 0;InetAddr client(peer);//获取cilent相关信息std::string cilentmessage = client.Ip()+":"+std::to_string(client.Port())+"# "+ inbuffer;LOG(LogLevel::DEBUG)<<cilentmessage;//将获取到的信息写回clientstd::string echo_string = "echo# ";echo_string += inbuffer;::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,client.NetAddr(),client.NetAddrLen());}}_isrunning = false;}~UdpServer(){if (_sockfd > gsockfd)::close(_sockfd);}private:InetAddr _addr; // 服务器地址包括ip和portint _sockfd;bool _isrunning; // 服务器运行状态
};#endif
要使用网络服务器需要使用socket创建套接字,然后将IP和端口号bind进入内核,最后就可以调用recv/sendto接口进行网络发送和接收信息了
服务器使用代码:
#include "UdpServer.hpp"// ./server_udp localport
int main(int argc, char *argv[])
{ENABLE_CONSOLE_LOG_STRATEGY();std::unique_ptr<UdpServer> svr_uptr;if (argc == 2){uint16_t port = std::stoi(argv[1]);svr_uptr = std::make_unique<UdpServer>(port);}elsesvr_uptr = std::make_unique<UdpServer>();svr_uptr->InitServer();svr_uptr->Start();return 0;
}
服务器需要输入端口号,不输入也行,服务器默认端口号为8080
- 客户端代码:
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"using namespace LogModule;
using namespace InetAddrModule;int sockfd = -1;//./udp_client server_ip server_port
int main(int argc, char *argv[])
{if(argc!=3){LOG(LogLevel::ERROR)<<"Usage:" << argv[0] << " serverip serverport" ;Die(ARGV_ERR);}//1.创建sockfdsockfd = ::socket(AF_INET,SOCK_DGRAM,0);if(sockfd < 0){LOG(LogLevel::WARNING)<<"client sockfd fail...";Die(SOCKET_ERR);} LOG(LogLevel::INFO)<<"client sockfd success...";//2.填充服务器信息std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);InetAddr ServerAddr(serverip,serverport);//3.发送请求给服务器while(true){//3.1获取信息std::cout << "Please Enter# ";std::string message;std::getline(std::cin, message);//3.2发送信息给服务器ssize_t n = ::sendto(sockfd,message.c_str(),sizeof(message),0,ServerAddr.NetAddr(),ServerAddr.NetAddrLen());if(n < 0){LOG(LogLevel::ERROR)<<"client sendto fail...";continue;}//3.3从服务器接收信息char buffer[1024];struct sockaddr_in tmp;socklen_t len = sizeof(tmp);ssize_t m = ::recvfrom(sockfd,buffer,sizeof(buffer)-1,0,CONV(&tmp),&len);if(m > 0){buffer[m] = 0;std::cout<<buffer<<std::endl;}else{LOG(LogLevel::ERROR)<<"client recvfrom fail...";}}return 0;
}
同样,客户端要进行网络通信也需要创建套接字,但是不需要bind信息进入内核,因为在接收到网络信息时会自动进行bind
客户端需要输入服务器IP地址和端口号port
- 网络地址类:
因为在进行网络通信时不可避免的需要频繁使用到相关信息,所以我们可以考虑将它们封装成为一个类,设置一些常用的方法
#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Common.hpp"namespace InetAddrModule
{class InetAddr{private:void PortNet2Host() // port网络转主机{_port = ::ntohs(_net_addr.sin_port);}void IpNet2Host() // IP网络转主机{char ipbuffer[64];const char *ip = ::inet_ntop(AF_INET, &_net_addr.sin_addr, ipbuffer, sizeof(ipbuffer));_ip = ip;}public:InetAddr(){}InetAddr(const struct sockaddr_in &addr) : _net_addr(addr) // 获取传来的sockaddr的ip和port{PortNet2Host();IpNet2Host();}InetAddr(uint16_t port) : _port(port), _ip(""){_net_addr.sin_family = AF_INET;_net_addr.sin_port = htons(_port);//主机转网络_net_addr.sin_addr.s_addr = INADDR_ANY;//表示可以介绍任何ip地址}InetAddr(const std::string& ip,uint16_t port) : _port(port), _ip(ip){_net_addr.sin_family = AF_INET;_net_addr.sin_port = htons(_port);//主机转网络_net_addr.sin_addr.s_addr = ::inet_addr(ip.c_str());}struct sockaddr *NetAddr() { return CONV(&_net_addr); }socklen_t NetAddrLen() { return sizeof(_net_addr); }std::string Ip() { return _ip; }uint16_t Port() { return _port; }~InetAddr(){}private:struct sockaddr_in _net_addr;std::string _ip;uint16_t _port;};
}
因为各种机器之间不兼容等例如大端/小端模式,所以在实际进行网络通信时我们需要将发送的ip地址和端口号从主机模式转换为网络模式。
在网络编程中,当一个进程需要绑定一个网络端口以进行通信时,可以使用INADDR_ANY 作为 IP 地址参数。这样做意味着该端口可以接受来自任何 IP 地址的连接请求,无论是本地主机还是远程主机。例如,如果服务器有多个网卡(每个网卡上有不同的 IP 地址),使用 INADDR_ANY 可以省去确定数据是从服务器上具体哪个网卡/IP 地址上面获取的。
结果如下:

2. Dict server
上述echo server仅仅是将收到的消息回显给客户端,其实我们还可以在服务器中加一点业务处理,比如翻译功能。
所以我们可以创建一个Dictionary类,将翻译词典封装起来:
Dict.txt:
apple: 苹果
banana: 香蕉
cat: 猫
dog: 狗
book: 书
pen: 笔
happy: 快乐的
sad: 悲伤的
run: 跑
jump: 跳
teacher: 老师
student: 学生
car: 汽车
bus: 公交车
love: 爱
hate: 恨
hello: 你好
goodbye: 再见
summer: 夏天
winter: 冬天
Dict.hpp:
#pragma once#include <iostream>
#include <fstream>
#include <unordered_map>
#include "Log.hpp"namespace DictionaryModule
{const std::string sep = ": "; // 分割符using namespace LogModule;class Dict{private://将词典内容从Dict.txt中加载进来void DownloadDict(){std::ifstream in(_dictpath);if (!in.is_open()){LOG(LogLevel::WARNING) << "DownloadDict fail...";return;}std::string line;while (getline(in, line)){if (line.empty())continue;// 加入词典size_t pos = line.find(sep);_dict.insert({line.substr(0, pos), line.substr(pos + sep.size())});}}public:Dict(const std::string &dictpath = "./Dict.txt") : _dictpath(dictpath){DownloadDict(); // 加载词典}std::string Translate(const std::string &key){auto iter = _dict.find(key);if (iter == _dict.end())return std::string("Unknown");elsereturn iter->second;}~Dict(){}private:std::string _dictpath;std::unordered_map<std::string, std::string> _dict;};
}
有了翻译的功能后,我们就可以将其嵌入服务器内部使用,所以我们在UdpServer类成员中添加一个回调方法,并在Start函数中使用:
#ifndef __UDP_SERVER_HPP__
#define __UDP_SERVER_HPP__#include <iostream>
#include <string>
#include <memory>
#include <cstring>
#include <cerrno>
#include <strings.h>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Log.hpp"
#include "InetAddr.hpp"
#include "Common.hpp"using namespace LogModule;
using namespace InetAddrModule;using func_t = std::function<std::string(const std::string&)>;
const static int gsockfd = -1;
const static uint16_t gdefaultport = 8080;class UdpServer
{
public:UdpServer(func_t func,uint16_t port = gdefaultport): _sockfd(gsockfd),_addr(port),_isrunning(false),_func(func){}void InitServer(){// 1. 创建socket_sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(LogLevel::FATAL) << "socket: " << strerror(errno);Die(SOCKET_ERR);}LOG(LogLevel::INFO) << "socket success, sockfd is : " << _sockfd;// 2. bind : 设置进入内核中int n = ::bind(_sockfd, _addr.NetAddr(), _addr.NetAddrLen());if (n < 0){LOG(LogLevel::FATAL) << "bind: " << strerror(errno);Die(BIND_ERR);}LOG(LogLevel::INFO) << "bind success";}bool Start(){_isrunning = true;while (true){char inbuffer[1024]; // stringstruct sockaddr_in peer;socklen_t len = sizeof(peer); // 必须设定ssize_t n = ::recvfrom(_sockfd, inbuffer, sizeof(inbuffer)-1, 0, CONV(&peer),&len);if(n > 0){inbuffer[n] = 0;InetAddr client(peer);//获取cilent相关信息std::string cilentmessage = client.Ip()+":"+std::to_string(client.Port())+"# "+ inbuffer;LOG(LogLevel::DEBUG)<<cilentmessage;//调用回调方法处理翻译业务std::string value = _func(inbuffer);//将获取到的信息写回clientstd::string echo_string = "Translate# ";echo_string += value;::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,client.NetAddr(),client.NetAddrLen()); }}_isrunning = false;}~UdpServer(){if (_sockfd > gsockfd)::close(_sockfd);}private:InetAddr _addr; // 服务器地址包括ip和portint _sockfd;bool _isrunning; // 服务器运行状态func_t _func; //回调业务方法
};#endif
最后在定义服务器时使用lambda表达式将Dict类中的Translate方法绑定给UdpServer:
#include "UdpServer.hpp"
#include "Dict.hpp"using namespace DictionaryModule;// ./server_udp localport
int main(int argc, char *argv[])
{ENABLE_CONSOLE_LOG_STRATEGY();Dict dictionary;std::unique_ptr<UdpServer> svr_uptr;if (argc == 2){uint16_t port = std::stoi(argv[1]);svr_uptr = std::make_unique<UdpServer>([&dictionary](const std::string& key){return dictionary.Translate(key);},port);}elsesvr_uptr = std::make_unique<UdpServer>([&dictionary](const std::string& key){return dictionary.Translate(key);});svr_uptr->InitServer();svr_uptr->Start();return 0;
}
客户端函数不需要改变可以直接使用,结果如下:

3. ChatServer
对于聊天室的实现,我们需要对聊天对象进行管理,所以需要新建一个类usermanager以及描述聊天对象的类user:
#pragma once#include <iostream>
#include <list>
#include <memory>
#include <algorithm>
#include <sys/types.h>
#include <sys/socket.h>
#include "InetAddr.hpp"
#include "Log.hpp"
#include "Mutex.hpp"
namespace UserModule
{using namespace InetAddrModule;using namespace LogModule;using namespace MutexModule;class UserInterface{public:virtual ~UserInterface() = default;virtual void SendTo(int sockfd, const std::string &message) = 0; // 纯虚函数virtual bool operator==(const InetAddr &u) const = 0;virtual std::string Id() = 0;};//描述对象class User : public UserInterface{public:User(const InetAddr &id) : _id(id){}void SendTo(int sockfd, const std::string &message) override{// ssize_t n = ::sendto(sockfd, &message, message.size(), 0, _id.NetAddr(), _id.NetAddrLen());错误错误!!!!不能取地址messagessize_t n = ::sendto(sockfd, message.c_str(), message.size(), 0, _id.NetAddr(), _id.NetAddrLen());LOG(LogLevel::DEBUG) << "send message to " << _id.Addr() << " info: " << message;if (n < 0){LOG(LogLevel::WARNING) << "Snedto fail...";return;}}bool operator==(const InetAddr &u) const override{return _id == u;}std::string Id(){return _id.Addr();}~User(){}private:InetAddr _id;};//管理对象class UserManage{public:UserManage(){}void AddUser(InetAddr &id){LockGuard lock(_mutex);//因为要访问公共资源所以要加锁保护// 1.先遍历整个链表查找是否已经添加过了for (auto &user : _online_user){if (*user == id) // User已经重载=={LOG(LogLevel::INFO) << id.Addr() << "用户已经存在...";return;}}// 2.如果是新用户就添加_online_user.push_back(std::make_shared<User>(id));LOG(LogLevel::INFO) << "添加用户: " << id.Addr() << "成功...";}void DelUser(InetAddr &id){LockGuard lock(_mutex);// 1.先遍历整个链表查找是否有该用户auto pos = std::remove_if(_online_user.begin(), _online_user.end(), [&id](std::shared_ptr<UserInterface> &user){ return *user == id; });// 2.如果有就删除_online_user.erase(pos, _online_user.end());}// 路由转发void Router(int sockfd, const std::string &message){LockGuard lock(_mutex);for (auto &user : _online_user){user->SendTo(sockfd, message);}}void PrintUser(){LockGuard lock(_mutex);for (auto user : _online_user){LOG(LogLevel::DEBUG) << "在线用户-> " << user->Id();}}~UserManage(){}private:std::list<std::shared_ptr<UserInterface>> _online_user;Mutex _mutex;};
};
对于描述对象参数我们可以使用之前实现的InetAddr类,对于对象的管理方法主要有添加对象、删除对象以及路由转发(群发)这三个部分;因为后续有多个线程而它们内部实现需要访问公共资源,所以需要加锁保护。
在服务器代码中其他都与前面类似,我们只需要将服务器的Start方法修改一下即可:
bool Start(){_isrunning = true;while (true){char inbuffer[1024]; // stringstruct sockaddr_in peer;socklen_t len = sizeof(peer); // 必须设定ssize_t n = ::recvfrom(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0, CONV(&peer), &len);if (n > 0){inbuffer[n] = 0;InetAddr client(peer); // 1.获取cilent相关信息std::string message = client.Addr() + "# " + inbuffer;LOG(LogLevel::DEBUG) << message;// 2.判断是否为quit信息if (std::strcmp(inbuffer, "quit") == 0){_deluser(client);message = client.Addr() + "# " + "我走了,你们聊!";}else{// 3.添加新用户_adduser(client);}// 3. 构建转发任务,推送给线程池,让线程池进行转发task_t task = std::bind(UdpServer::_router, _sockfd, message);ThreadPool<task_t>::GetInstance()->Enqueue(task);}}_isrunning = false;}
服务器不再是简单的接收信息,还需要对接收的消息进行处理;因为转发任务消耗的时间可能较长,我们可以利用之前实现的线程池来处理多个转发任务,主线程则继续收消息然后往线程池里添加转发任务。
除了Start方法,服务器类也需要添加几个回调方法(在Start方法中使用):
using add_t = std::function<void(InetAddr &id)>;
using del_t = std::function<void(InetAddr &id)>;
using router_t = std::function<void(int sockfd, const std::string &message)>;
using task_t = std::function<void()>;
class UdpServer
{
public:UdpServer(add_t adduser, del_t deluser, router_t router, uint16_t port = gdefaultport): _sockfd(gsockfd),_addr(port),_isrunning(false),_adduser(adduser),_deluser(deluser),_router(router){}private:InetAddr _addr; // 服务器地址包括ip和portint _sockfd;bool _isrunning; // 服务器运行状态add_t _adduser;del_t _deluser;router_t _router;
};#endif
在main函数中使用服务器对象时就需要绑定上述回调方法:
#include "UdpServer.hpp"
#include "User.hpp"
using namespace UserModule;// ./server_udp localport
int main(int argc, char *argv[])
{ENABLE_CONSOLE_LOG_STRATEGY();std::shared_ptr<UserManage> um = std::make_shared<UserManage>();std::unique_ptr<UdpServer> svr_uptr;if (argc == 2){uint16_t port = std::stoi(argv[1]);svr_uptr = std::make_unique<UdpServer>([&um](InetAddr &id){ return um->AddUser(id); },[&um](InetAddr &id){ return um->DelUser(id); },[&um](int sockfd, const std::string &message){ return um->Router(sockfd, message); },port);}elsesvr_uptr = std::make_unique<UdpServer>([&um](InetAddr &id){ return um->AddUser(id); },[&um](InetAddr &id){ return um->DelUser(id); },[&um](int sockfd, const std::string &message){ return um->Router(sockfd, message); });svr_uptr->InitServer();svr_uptr->Start();return 0;
}
对于客户端代码,我们也需要创建两个线程,主线程用来向服务器发送消息,另一个线程则用来接收群发的消息:
#include "Common.hpp"
#include <iostream>
#include <cstring>
#include <string>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <signal.h>int sockfd = -1;
struct sockaddr_in server;void ClientQuit(int signo)
{(void)signo;const std::string quit = "QUIT";int n = ::sendto(sockfd, quit.c_str(), quit.size(), 0, CONV(&server), sizeof(server));exit(0);
}void *Recver(void *args)
{while (true){(void)args;struct sockaddr_in temp;socklen_t len = sizeof(temp);char buffer[1024];int n = ::recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, CONV(&temp), &len);if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl; // 代码没问题,重定向也没问题,管道读写同时打开,才会继续向后运行// fprintf(stderr, "%s\n", buffer);// fflush(stderr);}}
}// CS
// ./client_udp serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " serverip serverport" << std::endl;Die(USAGE_ERR);}signal(2, ClientQuit);std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 1. 创建socketsockfd = ::socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;Die(SOCKET_ERR);}std::cout<<"sockfd: "<<sockfd<<std::endl;// 1.1 填充server信息memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = ::htons(serverport);server.sin_addr.s_addr = ::inet_addr(serverip.c_str());pthread_t tid;pthread_create(&tid, nullptr, Recver, nullptr);// 1.2 启动的时候,给服务器推送消息即可const std::string online = " ... 来了哈!";int n = ::sendto(sockfd, online.c_str(), online.size(), 0, CONV(&server), sizeof(server));// 2. clientdonewhile (true){std::cout << "Please Enter# ";std::string message;std::getline(std::cin, message);// client 不需要bind吗?socket <-> socket// client必须也要有自己的ip和端口!但是客户端,不需要自己显示的调用bind!!// 而是,客户端首次sendto消息的时候,由OS自动进行bind// 1. 如何理解client自动随机bind端口号? 一个端口号,只能被一个进程bind// 2. 如何理解server要显示的bind?服务器的端口号,必须稳定!!必须是众所周知且不能改变轻易改变的!int n = ::sendto(sockfd, message.c_str(), message.size(), 0, CONV(&server), sizeof(server));(void)n;}return 0;
}
在运行客户端代码之前,我们可以创建一个管道将其重定向到cerro,然后运行客户端,这样服务器群发收到的消息就会写入到管道中
相关文章:
【Linux】Socket编程—UDP
🔥 个人主页:大耳朵土土垚 🔥 所属专栏:Linux系统编程 这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉 文章目…...
2025年物联网相关专业毕业论文选题参考,文末联系,选题相关资料提供
一、智能穿戴解决方案研究方向 序号解决方案论文选题论文研究方向1智能腰带健康监测基于SpringBoot和Vue的智能腰带健康监测数据可视化平台开发研究如何利用SpringBoot和Vue技术栈开发一个数据可视化平台,用于展示智能腰带健康监测采集的数据,如心率、血…...
如何在WPS和Word/Excel中直接使用DeepSeek功能
以下是将DeepSeek功能集成到WPS中的详细步骤,无需本地部署模型,直接通过官网连接使用:1. 下载并安装OfficeAI插件 (1)访问OfficeAI插件下载地址:OfficeAI助手 - 免费办公智能AI助手, AI写作,下载…...
DeepSeek之Api的使用(将DeepSeek的api集成到程序中)
一、DeepSeek API 的收费模式 前言:使用DeepSeek的api是收费的 免费版: 可能提供有限的免费额度(如每月一定次数的 API 调用),适合个人开发者或小规模项目。 付费版: 超出免费额度后,可能需要按…...
使用DeepSeek实现AI自动编码
最近deepseek很火,低成本训练大模型把OpenAI、英伟达等股票搞得一塌糊涂。那它是什么呢,对于咱们程序员编码能有什么用呢?DeepSeek 是一款先进的人工智能语言模型,在自然语言处理和代码生成方面表现出色。它经过大量代码数据训练&…...
30~32.ppt
目录 30.导游小姚-介绍首都北京❗ 题目 解析 31.小张-旅游产品推广文章 题目 解析 32.小李-水的知识❗ 题目 解析 30.导游小姚-介绍首都北京❗ 题目 解析 新建幻灯片-从大纲-重置-检查设计→主题对话框→浏览主题:考生文件夹(注意&#x…...
Java的匿名内部类转为lamada表达式
在Java中,匿名内部类通常用于创建没有命名类的实例。例如,你可能需要创建一个实现了某个接口的匿名类,或者在需要重写某个方法时使用它。在Java 8及更高版本中,你可以使用Lambda表达式来替代传统的匿名内部类,使得代码…...
redis高级数据结构Stream
文章目录 背景stream概述消息 ID消息内容常见操作独立消费创建消费组消费 Stream弊端Stream 消息太多怎么办?消息如果忘记 ACK 会怎样?PEL 如何避免消息丢失?分区 Partition Stream 的高可用总结 背景 为了解决list作为消息队列是无法支持消息多播问题,Redis5.0…...
LeetCode781 森林中的兔子
问题描述 在一片神秘的森林里,住着许多兔子,但是我们并不知道兔子的具体数量。现在,我们对其中若干只兔子进行提问,问题是 “还有多少只兔子与你(指被提问的兔子)颜色相同?” 我们将每只兔子的…...
单硬盘槽笔记本更换硬盘
背景 本人的笔记本电脑只有一个硬盘槽,而且没有M.2的硬盘盒,只有一个移动硬盘 旧硬盘:512G 新硬盘:1T 移动硬盘:512G 参考链接:https://www.bilibili.com/video/BV1iP41187SW/?spm_id_from333.1007.t…...
EB生成配置的过程
EB Tresos Studio,简称EB,通过图形化的模式进行配置生成,并根据选项配置生成配置代码,即 MCAL 层各个模块的配置参数。 在 MCAL 代码中,分为静态代码和配置代码。静态代码,就是 AUTOSAR 规范内容,包含对硬件的封装以及标准化接口的封装;配置代码一般用于配置初始化结构…...
量化交易数据获取:xtquant库的高效应用
量化交易数据获取:xtquant库的高效应用 在量化交易领域,历史行情数据的重要性不言而喻。它不仅为策略回测提供基础,也是实时交易决策的重要参考。本文将介绍如何使用xtquant库来高效获取和处理历史行情数据。 技术背景与应用场景 对于量化…...
哨兵模式与 Redis Cluster:高可用 Redis 的深度剖析
深入探讨 Redis 高可用性解决方案:哨兵模式与 Redis Cluster 一、哨兵模式(Redis Sentinel)深入解析 (一)工作原理详解 哨兵模式通过一个或多个哨兵实例监控 Redis 主从复制集群,确保在主节点发生故障时…...
C++20新特性
作者:billy 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 前言 C20 是 C 标准中的一个重要版本,引入了许多新特性和改进,包括模块(Modules)、协程…...
电机实验曲线数据提取
处理Python 代码供参考: 1、曲线数据还原 import cv2 import numpy as np import matplotlib.pyplot as plt# 读取图像 image_path 1.png image cv2.imread(image_path) image_copy image.copy() # 创建图像副本,用于叠加显示# 转换为灰度图像 gray cv2.cvtCo…...
windows蓝牙驱动开发-调试及支持的HCI和事件
调试蓝牙配置文件驱动程序 开发蓝牙配置文件驱动程序时,可以使用驱动程序验证程序来协助其调试。 若要启用验证检查,必须为 Bthusb.sys 启用驱动程序验证程序。 如果不执行此操作,将禁用验证检查。 若要完全利用验证检查,请确保…...
Excel大数据量导入导出
github源码 地址(更详细) : https://github.com/alibaba/easyexcel 文档:读Excel(文档已经迁移) B 站视频 : https://www.bilibili.com/video/BV1Ff4y1U7Qc 一、JAVA解析EXCEL工具EasyExcel Java解析、生成Excel比较…...
Linux系统命令无法使用(glib库相关问题)
1.背景描述 Yum强制安装了一些软件,安装软件成功无报错,完成后不久突然发现系统出问题了,所有的命令无法使用了,如ls、mv、cat等基本命令报错。 relocation error: /lib64/libpthread.so.0: symbol_libc_dl_error_tsd …...
Qt修仙之路2-1 仿QQ登入 法宝初成
widget.cpp #include "widget.h" #include<QDebug> //实现槽函数 void Widget::login1() {QString userusername_input->text();QString passpassword_input->text();//如果不勾选无法登入if(!check->isChecked()){qDebug()<<"xxx"&…...
DeepSeek-V3 论文解读:大语言模型领域的创新先锋与性能强者
论文链接:DeepSeek-V3 Technical Report 目录 一、引言二、模型架构:创新驱动性能提升(一)基本架构(Basic Architecture)(二)多令牌预测(Multi-Token Prediction…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
如何把工业通信协议转换成http websocket
1.现状 工业通信协议多数工作在边缘设备上,比如:PLC、IOT盒子等。上层业务系统需要根据不同的工业协议做对应开发,当设备上用的是modbus从站时,采集设备数据需要开发modbus主站;当设备上用的是西门子PN协议时…...
VSCode 使用CMake 构建 Qt 5 窗口程序
首先,目录结构如下图: 运行效果: cmake -B build cmake --build build 运行: windeployqt.exe F:\testQt5\build\Debug\app.exe main.cpp #include "mainwindow.h"#include <QAppli...
Vue3学习(接口,泛型,自定义类型,v-for,props)
一,前言 继续学习 二,TS接口泛型自定义类型 1.接口 TypeScript 接口(Interface)是一种定义对象形状的强大工具,它可以描述对象必须包含的属性、方法和它们的类型。接口不会被编译成 JavaScript 代码,仅…...
python学习day39
图像数据与显存 知识点回顾 1.图像数据的格式:灰度和彩色数据 2.模型的定义 3.显存占用的4种地方 a.模型参数梯度参数 b.优化器参数 c.数据批量所占显存 d.神经元输出中间状态 4.batchisize和训练的关系 import torch import torchvision import torch.nn as nn imp…...
