Linux网络——套接字与UdpServer

目录

一、socket 编程接口

1.1 sockaddr 结构

1.2 socket 常见API

二、封装 InetAddr

三、网络字节序

四、封装通用 UdpServer 服务端

4.1 整体框架

4.2 类的初始化 

4.2.1 socket

4.2.2 bind

4.2.3 创建流式套接字

4.2.4 填充结构体

4.3 服务器的运行 

4.3.1 recvfrom

4.3.2 sendto

4.3.3 接收数据

4.3.4 发送数据

4.4 UdpServer.hpp

五、封装通用 UdpClient 客户端


OSI 参考模型与 TCP/IP 分层模型的对比

一、socket 编程接口

1.1 sockaddr 结构

socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4、 IPv6、UNIX Domain Socket。然而, 各种网络协议的地址格式并不相同:

它们都定义在 netinet/in.h 中,其中,

struct sockaddr

  • 它是一个通用的套接字地址结构体,通常在需要传递通用地址结构体指针的地方使用。
  • 定义:
    struct sockaddr 
    {unsigned short sa_family;    // 地址族char sa_data[14];            // 地址数据
    };
    
  • sa_family 指定地址族,比如 AF_INETAF_UNIX 等。

struct sockaddr_in

  • 它专门用于 IPv4 地址的套接字编程。
  • 定义:
    struct sockaddr_in 
    {short int sin_family;        // 地址族 (AF_INET)unsigned short int sin_port; // 端口号struct in_addr sin_addr;     // IP 地址unsigned char sin_zero[8];   // 填充,使结构体大小与 `struct sockaddr` 一致
    };struct in_addr 
    {unsigned long s_addr;        // 32 位的 IP 地址
    };
    
  • sin_family 通常为 AF_INET,表示使用 IPv4;AF_INET6,表示使用 IPv6 
  • sin_port 存储端口号,使用 htons 函数转换为网络字节序
  • sin_addr 存储 IPv4 地址,使用 inet_addrinet_pton 函数进行设置。
  • in_addr中的 s_addr初始化时使用 INADDR_ANY

struct sockaddr_un

  • 它专门用于 UNIX 域套接字编程。
  • 定义:
    struct sockaddr_un 
    {sa_family_t sun_family;      // 地址族 (AF_UNIX)char sun_path[108];          // 路径名
    };
    
  • sun_family 通常为 AF_UNIX,表示使用 UNIX 域套接字。
  • sun_path 存储文件系统路径名,表示套接字文件的位置。

IPv4、 IPv6地址类型分别定义为常数AF_INET、 AF_INET6。这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容

socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为参数。

1.2 socket 常见API

套接字是通信的端点,允许在网络上的两个主机之间进行数据传输。

每个套接字都与一个特定的地址和端口绑定,以标识唯一的通信端点。

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

二、封装 InetAddr

InetAddr 将一套接字结构体的服务号与端口号封装成类,以后调用某一套接字的IP地址与端口号时就可以直接使用语言层面的一些数据类型,如 string 、uint16_t ,这样比较统一。

因为我们传入的 ip 地址是 "xxx.xxx.xxx.xxx" ,这是一个 string 类,在服务端的 main 函数中,可以使用 inet_addr 将其传入 struct addr_in 的 s_addr 中。

class InetAddr
{
private:struct sockaddr_in _addr;string _ip;uint16_t _port;
};


接下来,就是类的成员函数,保证类可以返回 sockaddr_in \ ip \ port 即可。
但是,需要注意的是,struct sockaddr_in 中的 IP 地址与端口号与我们定义的类型不同,系统中也提供了相应的函数便于我们的转化, ntohs 与 inet_ntoa 前者用于网络字节序转化为主机字节序,后者用于将网络字节顺序给出的主机地址转化为IPv4点分十进制的字符串。

void GetAddr()
{_ip = inet_ntoa(_addr.sin_addr.s_addr);_port = ntohs(_addr.sin_port);
}
#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
private:void GetAddress(){_port = ntohs(_addr.sin_port);_ip = inet_ntoa(_addr.sin_addr);}public:InetAddr(const struct sockaddr_in &addr) : _addr(addr){GetAddress();}std::string Ip(){return _ip;}uint16_t Port(){return _port;}~InetAddr(){}private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;
};

三、网络字节序

内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分, 网络数据流同样有大端小端之分. 那么如何定义网络数据流的地址呢?

换句话说,如果从一个大端存储的计算机传输数据至一个小端存储的计算机,那么如果网络层不进一步优化的话,传过去的数据不就都乱套了吗。

发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。
TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。
不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送/接收数据;如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可。

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

#include <arpa/inet.h>
uint32_t htonl(uint32_t, hostlong);
uint16_t htons(uint16_t, hostshort);
uint32_t ntohl(uint32_t, netlong);
uint16_t ntohs(uint16_t, netshort);

这些函数名很好记,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
例如htonl表示将32位的长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。

四、封装通用 UdpServer 服务端

服务端要负责的是接收客户端的请求并给予客户端一定的响应,对 UdpServer 的封装要包括套接字的建立、服务的绑定、数据的收集、数据的传输等。

4.1 整体框架

首先,我们知道每个Mac都有其独特的IP,那么Mac中有那么多的应用软件,应该如何才能确定当前服务需要在哪个应用中呢?这就引入了端口号,用于标识一台Mac中唯一的应用。所以在UdpServer中,不仅要创建流式套接字,还要有唯一的端口号。除此之外,如果在外部需要停止服务端的响应,可以设置一个布尔类型的变量来标识UdpServer是否在运行。

其次,在编写通用的 UdpServer 类时,构造函数通常不会将套接字文件描述符 (sockfd) 作为参数进行传递。这是因为套接字文件描述符是在类的内部创建和管理的,而不是由外部提供。

#include <iostream>static const int gdefaultsockfd = -1;
class UdpServer
{public:UdpServer(uint16_t port):_sockfd(gdefaultsockfd), _port(port), _isrunning(false){}
private:int _sockfd;uint16_t _port;bool _isrunning;
};

4.2 类的初始化 

上面我们提到编写通用的 UdpServer类时,构造函数通常不需要传入 sockfd ,在后面会将的 TcpServer 也是如此,所以在初始化函数时,就要对套接字进行创建以及与 sockaddr 的绑定。

4.2.1 socket

socket 函数用于创建一个新的套接字。套接字是网络通信的端点。 

#include <sys/types.h>      
#include <sys/socket.h>int socket(int domain, int type, int protocol);

传入参数

  • domain: 指定协议族,如 AF_INET(IPv4)或 AF_INET6(IPv6)等。
  • type: 指定套接字类型,如 SOCK_STREAM(TCP)或 SOCK_DGRAM(UDP)。
  • protocol: 通常为 0,表示自动选择合适的协议。如果需要特定协议,可以传递协议编号。

返回值

    成功时返回一个文件描述符,失败时返回 -1,并设置 errno 来指示错误。

4.2.2 bind

bind 函数将一个套接字绑定到一个特定的本地地址和端口上。这通常用于服务器端。 

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

传入参数

  • socket: 由 socket 函数返回的套接字文件描述符。
  • address: 指向一个 struct sockaddr 类型的指针,包含要绑定的地址信息。
  • address_len: 地址结构体的长度。

返回值

成功时返回 0,失败时返回 -1,并设置 errno 来指示错误。

4.2.3 创建流式套接字

    void UdpInit(){// 1.创建流式套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno); exit(SOCKET_ERROR);}}

我们带入了上一节中的日志宏,同时因为 socket 函数可以带出错误信息,所以当套接字创建失败是,可以使用 strerror 打印一下错误信息,并可以通过枚举使 exit 时的信息更明确:

#include <cstring>
#include "Log.hpp"enum
{SOCKET_ERROR = 1,BIND_ERROR,USAGE_ERROR
}; 

4.2.4 填充结构体

这里使用的是 struct sockaddr_in 结构体,首先把结构体成员都初始化为0,这里使用 bezero 函数,sockaddr_in 结构体中的各个成员对 sin_family\sin_addr.s_addr 初始化,初始化的参数详见1.1 sockaddr 结构,然后向其中的 sin_port 填充我们输入的端口号。

        // 2.0创建struct sockaddr_in 并填充struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(_port);// 2.1 bind 将一个套接字绑定到一个特定的本地地址和端口上。这通常用于服务器端。 int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, "socket bind success\n");
    void InitServer(){// 1.创建流式套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno); // strerror->#include<string.h>exit(SOCKET_ERROR);}// 2.0创建struct sockaddr_in 并填充struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(_port);// 2.1 bind 将一个套接字绑定到一个特定的本地地址和端口上。这通常用于服务器端。 int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, "socket bind success\n");}

4.3 服务器的运行 

首先,我们希望服务器一直运行,所以需要设置死循环。其次,服务器进行收发信息要使用到函数recvfrom 与 sendto

4.3.1 recvfrom

recvfrom 函数用于从一个UDP套接字接收数据。它可以用于接收来自任意地址的数据,因此特别适合于UDP服务器。

#include <sys/types.h>
#include <sys/socket.h>ssize_t recvfrom(int socket, void *buffer, size_t length, int flags, struct sockaddr *address, socklen_t *address_len);

传入参数

  • socket: 套接字文件描述符,由 socket 函数返回。
  • buffer: 用于存储接收到的数据的缓冲区指针。
  • length: 缓冲区的长度。
  • flags: 通常为 0,也可以是一些控制操作行为的标志,例如 MSG_DONTWAIT(非阻塞操作)。
  • address: 指向 struct sockaddr 的指针,用于存储发送数据的源地址
  • address_len: 指向 socklen_t 的指针,指示 address 的大小,并在函数返回时设置为实际地址的长度。

返回值

成功时返回接收到的数据字节数,失败时返回 -1,并设置 errno 来指示错误。

4.3.2 sendto

sendto 函数用于通过一个UDP套接字发送数据。它可以用于发送数据到指定的地址,因此特别适合于UDP客户端和服务器。

#include <sys/types.h>
#include <sys/socket.h>ssize_t sendto(int socket, const void *message, size_t length, int flags, const struct sockaddr *dest_addr, socklen_t dest_len);

传入参数

  • socket: 套接字文件描述符,由 socket 函数返回。
  • message: 指向要发送的数据缓冲区的指针。
  • length: 要发送的数据的长度。
  • flags: 通常为 0,也可以是一些控制操作行为的标志,例如 MSG_DONTWAIT(非阻塞操作)。
  • dest_addr: 指向 struct sockaddr 的指针,包含目标地址信息
  • dest_len: 目标地址结构体的长度。

返回值

成功时返回发送的数据字节数,失败时返回 -1,并设置 errno 来指示错误。

4.3.3 接收数据

首先,所有的操作都要定义在一个 while 的死循环中。其次,因为 recvfrom 中需要使用缓冲区,所以还要定义一个缓冲区。同时, recvfrom 可以标明发送数据的源地址,所以可以定义一个 sockaddr_in 的结构体,用于存储发送数据的源地址,当接收成功时,可以使用之前定义的 InetAddr 类来接收该源地址。

    void Start(){_isrunning = true;while (_isrunning){char buffer[1024];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){buffer[n] = 0;InetAddr addr(peer);LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);}}_isrunning = false;}

4.3.4 发送数据

既然已经接收到数据了,我们需要让客户端知道服务端已经接收到了数据,所以当接收数据成功时,在使用 sendto 发送数据至客户端。 

    void Start(){_isrunning = true;while (_isrunning){char buffer[1024];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){buffer[n] = 0;InetAddr addr(peer);LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);}}}

以下是为什么服务端需要接收数据还需要传输到客户端的原因,究其原因还是与Udp最初的开发有关:

在 UDP 服务器接收到客户端的信息后使用 sendto 函数发回响应,是为了实现双向通信,使得客户端可以知道服务器已经正确接收到并处理了请求。以下是这种设计背后的主要原因:

1. 确认信息接收

在无连接的 UDP 协议中,数据包的发送和接收是独立的,且没有内建的机制来确认数据包是否成功到达对方。通过服务器发回一个响应,客户端可以确认其发送的信息已经被接收到并处理。

2. 双向通信

多数网络应用需要双向通信,不仅客户端需要向服务器发送数据,服务器也需要向客户端发送数据。比如,客户端发送请求数据,服务器处理后返回相应的结果。这种交互模式在很多应用场景中都是必须的。

3. 应用层协议实现

通过在应用层协议中定义请求-响应模式,可以更好地实现和管理通信过程。服务器接收到请求后返回响应,是许多协议(例如 DNS、DHCP 等)基本工作方式的一部分。

4. 保持通信会话

在某些应用中,客户端和服务器需要保持持续的通信会话。服务器向客户端发回响应,可以作为会话的一部分,确保双方在同一上下文中进行通信。

4.4 UdpServer.hpp

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <cstring>
#include <netinet/in.h>
#include "Log.hpp"
#include "InetAddr.hpp"enum
{SOCKET_ERROR = 1,BIND_ERROR,USAGE_ERROR
};static const int gdefaultsockfd = -1;
class UdpServer
{
public:UdpServer(uint16_t port) : _sockfd(gdefaultsockfd), _port(port), _isrunning(false){}void InitServer(){// 1.创建流式套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0){LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno); // strerror->#include<string.h>exit(SOCKET_ERROR);}// 2.0创建struct sockaddr_in 并填充struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(_port);// 2.1 bind 将一个套接字绑定到一个特定的本地地址和端口上。这通常用于服务器端。 int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));if (n < 0){LOG(FATAL, "bind error, %s, %d\n", strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, "socket bind success\n");}void Start(){_isrunning = true;while (_isrunning){char buffer[1024];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){buffer[n] = 0;InetAddr addr(peer);LOG(DEBUG, "get message from [%s:%d]: %s\n", addr.Ip().c_str(), addr.Port(), buffer);sendto(_sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&peer, len);}}}~UdpServer(){}private:int _sockfd;uint16_t _port;bool _isrunning;
};

如果以后有其他的业务,可以在类内定义一个回调函数成员指针或者使用 function 封装一个回调函数,在构造函数中传入该回调函数,并在 Start 中执行相应的回调函数即可,大致思路如下,具体改动的是 Start 中 sendto 的传入参数。

using func_t = std::function<std::string(const std::string&, bool &ok)>;
class UdpServer
{
public:UdpServer(uint16_t port, func_t func) : _sockfd(defaultfd), _port(port), _isrunning(false), _func(func){}void Start(){while (){if (){std::string response = _func(request, ok); sendto(_sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&peer, len);}}}
private:int _sockfd;uint16_t _port; bool _isrunning;// 给服务器设定回调,用来让上层进行注册业务的处理方法func_t _func;
};

五、封装通用 UdpClient 客户端

在 C/C++ 中,argcargv 是命令行参数的标准输入参数,用于在程序启动时获取命令行参数。

  • argc (argument count): 表示命令行参数的个数,包括程序名本身。
  • argv (argument vector): 是一个字符指针数组,包含了命令行输入的参数。argv[0] 通常是程序的名称argv[1]argv[argc-1] 是实际的命令行参数。

当程序正确启动时,应输入以下参数

./UdpClient 127.0.0.1 8080
  • argc 的值为 3。
  • argv 的内容如下:
    • argv[0]"./UdpClient",程序名。
    • argv[1]"127.0.0.1",服务器 IP。
    • argv[2]"8080",服务器端口。
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " serverip serverport\n"<< std::endl;
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 1. 创建socketint sockfd = socket(AF_INET, SOCK_DGRAM, 0);if(sockfd < 0){std::cerr << "socket error" << std::endl;}// 构建目标主机的socket信息struct sockaddr_in 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());std::string message;// 2. 直接通信即可while(true){std::cout << "Please Enter# ";std::getline(std::cin, message);sendto(sockfd, message.c_str(), message.size(), 0, (struct sockaddr*)&server, sizeof(server));struct sockaddr_in peer;socklen_t len = sizeof(peer);char buffer[1024];ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0, (struct sockaddr*)&peer, &len);if(n > 0){buffer[n] = 0;std::cout << "server echo# " << buffer << std::endl;}}return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/46465.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rabbitmq生产与消费

一、rabbitmq发送消息 一、简单模式 概述 一个生产者一个消费者模型 代码 //没有交换机&#xff0c;两个参数为routingKey和消息内容 rabbitTemplate.convertAndSend("test1_Queue","haha");二、工作队列模式 概述 一个生产者&#xff0c;多个消费者&a…

如何将PDF转换成可以直接编辑的CAD图纸?

PDF图纸是为了让用户更好的阅览CAD文件&#xff0c;但是&#xff0c;当我们想要对其进行编辑的时候&#xff0c;PDF图纸就是一个麻烦了。那么PDF转换成CAD后可以编辑吗&#xff1f;如何将PDF转换成可以直接编辑的CAD图纸呢&#xff1f;本篇给你答案。 1、启动迅捷CAD编辑器&…

【python学习】爬虫中常使用的urllib和requests库的的背景、定义、特点、功能、代码示例以及两者的区别

引言 urllib是Python标准库中的一个模块&#xff0c;它提供了一系列用于操作URL的功能 requests是一个Python第三方库&#xff0c;由Kenneth Reitz创建&#xff0c;用于简化HTTP客户端的编程 一、urllib的定义 urllib可以操作url&#xff0c;主要分为以下几个子模块&#xff1…

从人工巡检到智能防控:智慧油气田安全生产的新视角

一、背景需求 随着科技的飞速发展&#xff0c;视频监控技术已成为各行各业保障安全生产、提升管理效率的重要手段。特别是在油气田这一特殊领域&#xff0c;由于其工作环境复杂、安全风险高&#xff0c;传统的监控方式已难以满足实际需求。因此&#xff0c;基于视频监控AI智能…

ROS2从入门到精通2-3:机器人3D物理仿真Gazebo与案例分析

目录 0 专栏介绍1 什么是Gazebo&#xff1f;2 Gazebo架构2.1 Gazebo前后端2.2 Gazebo文件格式2.3 Gazebo环境变量 3 Gazebo安装与基本界面4 搭建自己的地图4.1 编辑地图4.2 保存地图4.3 加载地图 5 常见问题 0 专栏介绍 本专栏旨在通过对ROS2的系统学习&#xff0c;掌握ROS2底…

CSS技巧专栏:一日一例 8 - 纯CSS利用mask属性实现按钮边框对称包围特效

CSS技巧专栏:一日一例 8 - 纯CSS利用mask属性实现按钮边框对称包围特效 上篇作业解题 在前一篇文章的最后,给各位看官留了一个作业,如上图所示。本篇文章,我们来公布一下它的源码。 主要实现的思路 四个渐变色的线段,沿着四个方向的依次运动,(运动在加载前执行)使用 …

均值滤波算法及实现

均值滤波器的使用场景&#xff1a; 均值滤波器使用于处理一些如上述蓝色线的高斯噪声场景 红色曲线是经过均值滤波处理后的数据。主要因为均值滤波设置数据缓冲区&#xff08;也即延时周期&#xff09;&#xff0c;使得测量值经过缓冲不会出现特别大的变化。 黄色曲线为高斯噪声…

【iOS】—— 消息传递和消息转发

【iOS】—— 消息传递和消息转发 1. 消息传递SEL选择子IMP快速查找汇编代码查找过程总结消息转送快速查找IMP 慢速查找总结消息传递慢速查找IMP 2. 消息转发动态决议动态解析添加方法 快速转发慢速转发 总结动态决议消息转发消息的三次拯救 1. 消息传递 在iOS中&#xff0c;消…

一、单例模式

文章目录 1 基本介绍2 实现方式2.1 饿汉式2.1.1 代码2.1.2 特性 2.2 懒汉式 ( 线程不安全 )2.2.1 代码2.2.2 特性 2.3 懒汉式 ( 线程安全 )2.3.1 代码2.3.2 特性 2.4 双重检查2.4.1 代码2.4.2 特性 2.5 静态内部类2.5.1 代码2.5.2 特性 2.6 枚举2.6.1 代码2.6.2 特性 3 实现的要…

谷粒商城-全文检索-ElasticSearch

1.简介 一个分布式的开源搜索和分析引擎,可以 秒 级的从海量数据中检索 主要功能:做数据的检索和分析(MySQL专攻于数据的持久化存储与管理CRUD达到百万以上的数据MSQL就会很慢,海量数据的检索和分析还是要用ElasticSearch) 用途:我们电商项目里的所有的检索功能都是由Elasti…

基于X86+FPGA+AI数字化医疗设备:全自动尿沉渣检测仪

助力数字医疗发展&#xff0c;信迈可提供全自动尿沉渣检测仪专用计算机 随着信息技术的不断进步&#xff0c;医疗也进入了一个全新的数字化时代。首先是医疗设备的数字化&#xff0c;大大丰富了医疗信息的内涵和容量&#xff0c;具有广阔的市场发展前景。 数字化医疗设备&…

使用Redis的SETNX命令实现分布式锁

什么是分布式锁 分布式锁是一种用于在分布式系统中控制多个节点对共享资源进行访问的机制。在分布式系统中&#xff0c;由于多个节点可能同时访问和修改同一个资源&#xff0c;因此需要一种方法来确保在任意时刻只有一个节点能够对资源进行操作&#xff0c;以避免数据不一致或…

嵌入式香橙派人工智能AI开发板详细操作与远程聊天实现

大家好&#xff0c;今天给大分享一个OrangePi AIpro&#xff08;20T&#xff09;采用昇腾作为主控芯片的开发板&#xff0c;开箱以及对应功能的详细实现。 第一&#xff1a;板子基本介绍 接通电源给对应的开发板上电&#xff0c;观察其中的现象&#xff0c;如下&#xff1a; 注…

基于HAL库的stm32的OLED显示屏显示(IIC)

OLED OLED&#xff0c;即有机发光二极管( Organic Light Emitting Diode )。OLED由于同时具备自发光&#xff0c;不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性&#xff0c;被认为是下一代的平面显示器…

龙国专利局瑞数6

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(lianxi a…

配置和保护SSH

使用SSH访问远程命令行 描述Secure Shell SSH&#xff08;Secure Shell&#xff09; 是一种网络协议&#xff0c;用于在不安全的网络上安全地进行系统管理和数据传输。它最初由 Tatu Ylnen 于1995年设计&#xff0c;并成为保护网络服务免受攻击的标准。SSH提供了多种功能&…

基于SpringBoot+Vue的广场舞团系统(带1w+文档)

基于SpringBootVue的广场舞团系统(带1w文档) 基于SpringBootVue的广场舞团系统(带1w文档) 广场舞团&#xff0c;为用户随时随地查看广场舞团信息提供了便捷的方法&#xff0c;更重要的是大大的简化了管理员管理广场舞团信息的方式方法&#xff0c;更提供了其他想要了解广场舞团…

基于Trace的类型特化动态语言JIT编译

文章目录 Explain一、简介二、一个跟踪运行的示例三、跟踪树3.1 Traces类型特化&#xff08;Type specialization&#xff09; 3.2 Trace Trees3.3 黑名单&#xff08;Blacklisting&#xff09; 四、嵌套跟踪树4.1 Nesting Algorithm4.2 Blacklisting with Nesting 五、跟踪树优…

【Pytorch】一文向您详细介绍 torch.randn_like()

&#x1f389;&#x1f525;【Pytorch】一文向您详细介绍 torch.randn_like() &#x1f525;&#x1f389; 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; …

滑动窗口题目

题目描述&#xff1a; 计算两个字符串str1和str2在给定的含有n个元素的字符串数组strs中出现的最短距离。 详细解释&#xff1a; 定义整数变量n&#xff0c;用于存储字符串数组strs的长度。定义一个vector<string>类型的变量strs&#xff0c;用于存储输入的字符串。定义…