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

【Linux】Socket编程—UDP

🔥 个人主页:大耳朵土土垚
🔥 所属专栏:Linux系统编程

这里将会不定期更新有关Linux的内容,欢迎大家点赞,收藏,评论🥳🥳🎉🎉🎉

文章目录

  • 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

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…...

2025年物联网相关专业毕业论文选题参考,文末联系,选题相关资料提供

一、智能穿戴解决方案研究方向 序号解决方案论文选题论文研究方向1智能腰带健康监测基于SpringBoot和Vue的智能腰带健康监测数据可视化平台开发研究如何利用SpringBoot和Vue技术栈开发一个数据可视化平台&#xff0c;用于展示智能腰带健康监测采集的数据&#xff0c;如心率、血…...

如何在WPS和Word/Excel中直接使用DeepSeek功能

以下是将DeepSeek功能集成到WPS中的详细步骤&#xff0c;无需本地部署模型&#xff0c;直接通过官网连接使用&#xff1a;1. 下载并安装OfficeAI插件 &#xff08;1&#xff09;访问OfficeAI插件下载地址&#xff1a;OfficeAI助手 - 免费办公智能AI助手, AI写作&#xff0c;下载…...

DeepSeek之Api的使用(将DeepSeek的api集成到程序中)

一、DeepSeek API 的收费模式 前言&#xff1a;使用DeepSeek的api是收费的 免费版&#xff1a; 可能提供有限的免费额度&#xff08;如每月一定次数的 API 调用&#xff09;&#xff0c;适合个人开发者或小规模项目。 付费版&#xff1a; 超出免费额度后&#xff0c;可能需要按…...

使用DeepSeek实现AI自动编码

最近deepseek很火&#xff0c;低成本训练大模型把OpenAI、英伟达等股票搞得一塌糊涂。那它是什么呢&#xff0c;对于咱们程序员编码能有什么用呢&#xff1f;DeepSeek 是一款先进的人工智能语言模型&#xff0c;在自然语言处理和代码生成方面表现出色。它经过大量代码数据训练&…...

30~32.ppt

目录 30.导游小姚-介绍首都北京❗ 题目​ 解析 31.小张-旅游产品推广文章 题目 解析 32.小李-水的知识❗ 题目​ 解析 30.导游小姚-介绍首都北京❗ 题目 解析 新建幻灯片-从大纲-重置-检查设计→主题对话框→浏览主题&#xff1a;考生文件夹&#xff08;注意&#x…...

Java的匿名内部类转为lamada表达式

在Java中&#xff0c;匿名内部类通常用于创建没有命名类的实例。例如&#xff0c;你可能需要创建一个实现了某个接口的匿名类&#xff0c;或者在需要重写某个方法时使用它。在Java 8及更高版本中&#xff0c;你可以使用Lambda表达式来替代传统的匿名内部类&#xff0c;使得代码…...

redis高级数据结构Stream

文章目录 背景stream概述消息 ID消息内容常见操作独立消费创建消费组消费 Stream弊端Stream 消息太多怎么办?消息如果忘记 ACK 会怎样?PEL 如何避免消息丢失?分区 Partition Stream 的高可用总结 背景 为了解决list作为消息队列是无法支持消息多播问题&#xff0c;Redis5.0…...

LeetCode781 森林中的兔子

问题描述 在一片神秘的森林里&#xff0c;住着许多兔子&#xff0c;但是我们并不知道兔子的具体数量。现在&#xff0c;我们对其中若干只兔子进行提问&#xff0c;问题是 “还有多少只兔子与你&#xff08;指被提问的兔子&#xff09;颜色相同&#xff1f;” 我们将每只兔子的…...

单硬盘槽笔记本更换硬盘

背景 本人的笔记本电脑只有一个硬盘槽&#xff0c;而且没有M.2的硬盘盒&#xff0c;只有一个移动硬盘 旧硬盘&#xff1a;512G 新硬盘&#xff1a;1T 移动硬盘&#xff1a;512G 参考链接&#xff1a;https://www.bilibili.com/video/BV1iP41187SW/?spm_id_from333.1007.t…...

EB生成配置的过程

EB Tresos Studio,简称EB,通过图形化的模式进行配置生成,并根据选项配置生成配置代码,即 MCAL 层各个模块的配置参数。 在 MCAL 代码中,分为静态代码和配置代码。静态代码,就是 AUTOSAR 规范内容,包含对硬件的封装以及标准化接口的封装;配置代码一般用于配置初始化结构…...

量化交易数据获取:xtquant库的高效应用

量化交易数据获取&#xff1a;xtquant库的高效应用 在量化交易领域&#xff0c;历史行情数据的重要性不言而喻。它不仅为策略回测提供基础&#xff0c;也是实时交易决策的重要参考。本文将介绍如何使用xtquant库来高效获取和处理历史行情数据。 技术背景与应用场景 对于量化…...

哨兵模式与 Redis Cluster:高可用 Redis 的深度剖析

深入探讨 Redis 高可用性解决方案&#xff1a;哨兵模式与 Redis Cluster 一、哨兵模式&#xff08;Redis Sentinel&#xff09;深入解析 &#xff08;一&#xff09;工作原理详解 哨兵模式通过一个或多个哨兵实例监控 Redis 主从复制集群&#xff0c;确保在主节点发生故障时…...

C++20新特性

作者&#xff1a;billy 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 前言 C20 是 C 标准中的一个重要版本&#xff0c;引入了许多新特性和改进&#xff0c;包括模块&#xff08;Modules&#xff09;、协程…...

电机实验曲线数据提取

处理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() # 创建图像副本&#xff0c;用于叠加显示# 转换为灰度图像 gray cv2.cvtCo…...

windows蓝牙驱动开发-调试及支持的HCI和事件

调试蓝牙配置文件驱动程序 开发蓝牙配置文件驱动程序时&#xff0c;可以使用驱动程序验证程序来协助其调试。 若要启用验证检查&#xff0c;必须为 Bthusb.sys 启用驱动程序验证程序。 如果不执行此操作&#xff0c;将禁用验证检查。 若要完全利用验证检查&#xff0c;请确保…...

Excel大数据量导入导出

github源码 地址&#xff08;更详细&#xff09; : https://github.com/alibaba/easyexcel 文档&#xff1a;读Excel&#xff08;文档已经迁移&#xff09; B 站视频 : https://www.bilibili.com/video/BV1Ff4y1U7Qc 一、JAVA解析EXCEL工具EasyExcel Java解析、生成Excel比较…...

Linux系统命令无法使用(glib库相关问题)

1.背景描述 Yum强制安装了一些软件&#xff0c;安装软件成功无报错&#xff0c;完成后不久突然发现系统出问题了&#xff0c;所有的命令无法使用了&#xff0c;如ls、mv、cat等基本命令报错。 relocation error&#xff1a; /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 论文解读:大语言模型领域的创新先锋与性能强者

论文链接&#xff1a;DeepSeek-V3 Technical Report 目录 一、引言二、模型架构&#xff1a;创新驱动性能提升&#xff08;一&#xff09;基本架构&#xff08;Basic Architecture&#xff09;&#xff08;二&#xff09;多令牌预测&#xff08;Multi-Token Prediction&#xf…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

docker详细操作--未完待续

docker介绍 docker官网: Docker&#xff1a;加速容器应用程序开发 harbor官网&#xff1a;Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台&#xff0c;用于将应用程序及其依赖项&#xff08;如库、运行时环…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

Admin.Net中的消息通信SignalR解释

定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...