Linux高性能服务器编程——ch5笔记

第5章 Linux网络编程基础API

5.1 socket地址API

主机字节序(小端字节序):整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。
网络字节序(大端字节序):相反。

void byteorder()
{union{short value;char union_bytes[sizeof(short)];}test;test.value = 0x0102;if ((test.union_bytes[0] == 1) && (test.union_bytes[1] == 2)) {printf("big endian\n");} else if ((test.union_bytes[0] == 2) && (test.union_bytes[1] == 1)) {printf("little endian\n");} else {printf("unknown...\n");}
}

主机字节序和网络字节序之间的转换:发送端总是把要发送的数据转化成大端字节序数据后发送。(函数:htonl、htons、ntohl、ntohs)host to network long(short)

#include <bits/socket.h>
//是内存对齐的
struct sockaddr_storage
{sa_family_t sa_family;  //地址族类型的变量unsigned long int __ss_align;  //内存对齐char __ss_padding[128-sizeof(__ss_align)];  //socket地址值
}
#include <sys/un.h>
struct sockaddr_un
{sa_family_t sin_family;  //地址族:AF_UNIXchar sun_path[108];  //文件路径名
};
struct sockaddr_in
{sa_family_t sin_family;  //地址族:AF_INETu_int16_t sin_port;  //端口号,网络字节序struct in_addr sin_addr;  //IPv4地址结构体
};
struct in_addr
{u_int32_t s_addr;  //IPv4地址,网路字节序
};
struct sockaddr_in6
{sa_family_t sin6_family;  //地址族:AF_INET6u_int16_t sin6_port;  //端口号,网络字节序u_int32_t sin6_flowinfo;  //流信息,设置为0struct in6_addr sin6_addr;  //IPv6地址结构体u_int32_t sin6_scope_id;  //scope ID
};
struct in6_addr
{unsigned char sa_addr[16];  //IPv6地址,网路字节序
};

函数:inet_addr(点分十进制v4地址转化为网络字节序整数表示的v4地址)、inet_aton(与上一个一样,但结果转储在参数inp指向的地址中)、inet_ntoa(网络字节序表示的v4转化为点分十进制,不可重入)、inet_pton(适用v4和v6,网络字节序转储在参数dst指向的内存中)、inet_ntop(适用v4和v6,相反)

5.2 创建socket

socket就是可读、可写、可控制、可关闭的文件描述符(int)。

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
/*
** domain:底层协议族
** type:服务类型,SOCK_STREAM(TCP流服务)、SOCK_UGRAM(UDP数据报服务)
** protocol:0,表示默认协议
*/

5.3 命名socket

命名:将一个socket与socket地址绑定。bind函数。
服务器中需要,客户端不需要,而是采用匿名,即使用OS自动分配的socket地址。

5.4 监听socket

listen函数:创建一个监听队列以存放待处理的客户连接。客户端被动连接。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>static bool stop = false;/* SIGTERM信号的处理函数,触发时结束主程序中的循环 */
static void handle_term(int sig)
{stop = true;
}int main(int argc, char **argv)
{signal(SIGTERM, handle_term);if (argc < 4) {printf("usage: %s ip_address port_number backlog\n",basename(argv[0]));return 1;}const char *ip = argv[1];  //IP地址int port = atoi(argv[2]);  //端口号int backlog = atoi(argv[3]);  //backlog值,内核监听队列的最大长度int sock = socket(PF_INET, SOCK_STREAM, 0);assert(sock >= 0);/* 创建一个IPv4 socket地址 */struct sockaddr_in address;bzero(&address, sizeof(address));address.sin_family = AF_INET;inet_pton(AF_INET, ip, &address.sin_addr);address.sin_port = htons(port);int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));assert(ret != -1);ret = listen(sock, backlog);assert(ret != -1);/* 循环等待连接, 查到有SIGTERM信号将它中断 */while(!stop){sleep(1);}/* 关闭socket */close(sock);return 0;
}

监听队列中完整连接的上限通常比 backlog 值略大。

5.5 接受连接

accept函数。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>int main(int argc, char *argv[])
{if (argc < 2){printf("usage: %s ip_address port_number!\n", basename(argv[0]));return 1;}const char *ip = argv[1];int port = atoi(argv[2]);int sock = socket(PF_INET, SOCK_STREAM, 0);assert(sock >= 0);struct sockaddr_in address;bzero(&address, sizeof(address));address.sin_family = AF_INET;inet_pton(AF_INET, ip, &address.sin_addr);address.sin_port = htons(port);int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));assert(ret != -1);ret = listen(sock, 5);assert(ret != -1);/* 暂停20秒以等待客户端连接和相关操作(掉线或者退出)完成 */sleep(20);struct sockaddr_in client;socklen_t client_addrlength = sizeof(client);int connfd = accept(sock, (struct sockaddr *)&client, &client_addrlength);if (connfd < 0) {printf("errno is: %d\n", errno);} else {/* 接受连接成功则打印客户端的IP地址和端口号 */char remote[INET_ADDRSTRLEN];printf("connected with ip: %s and port: %d\n", inet_ntop(AF_INET,&client.sin_addr, remote, INET_ADDRSTRLEN), ntohs(client.sin_port));close(connfd);}close(sock);return 0;
}

accept只是从监听队列中取出连接,而不论连接处于何种状态 (如上面的ESTABLISHED 状态和 CLOSE_WAIT 状态),更不关心任何网络状况的变化。

5.6 发起连接

客户端通过connect函数主动与服务器建立连接。

5.7 关闭连接

关闭该连接对应的socket。
close函数:将参数fd的引用计数减1,为0才是真正关闭。多进程程序中,一次fork默认使父进程中打开的socket的引用计数加1。读写同时关闭。
shutdown函数:分别关闭socket上的读或写,或者都关闭。

5.8 数据读写

TCP数据读写:recv(可能要多次调用才能读取完整数据)、send(写入数据)

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>int main(int argc, char *argv[])
{if (argc <= 2) {printf("usage: %s ip_address port_number\n", basename(argv[0]));return 1;}const char *ip = argv[1];int port = atoi(argv[2]);struct sockaddr_in server_address;bzero(&server_address, sizeof(server_address));server_address.sin_family = AF_INET;inet_pton(AF_INET, ip, &server_address.sin_addr);server_address.sin_port = htons(port);int sockfd = socket(PF_INET, SOCK_STREAM, 0);assert(sockfd >= 0);if (connect(sockfd, (struct sockaddr *)&server_address,sizeof(server_address)) > 0) {printf("connection failed\n");} else {const char *oob_data = "abc";const char *normal_data = "123";send(sockfd, normal_data, strlen(normal_data), 0);send(sockfd, oob_data, strlen(oob_data), MSG_OOB);  //flags=MSG_OOB 带外数据send(sockfd, normal_data, strlen(normal_data), 0);}close(sockfd);return 0;
}
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>#define BUF_SIZE 1024int main(int argc, char *argv[])
{if (argc <= 2) {printf("usage: %s ip_address port_number\n", basename(argv[0]));return 1;}const char *ip = argv[1];int port = atoi(argv[2]);struct sockaddr_in address;bzero(&address, sizeof(address));address.sin_family = AF_INET;inet_pton(AF_INET, ip, &address.sin_addr);address.sin_port = htons(port);int sock = socket(PF_INET, SOCK_STREAM, 0);assert(sock);int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));assert(ret != -1);ret = listen(sock, 5);assert(ret != -1);struct sockaddr_in client;socklen_t client_addrlength = sizeof(client);int connfd = accept(sock, (struct sockaddr *)&client, &client_addrlength);if (connfd < 0) {printf("errno is : %d\n", errno);} else {char buffer[BUF_SIZE];memset(buffer, '\0', BUF_SIZE);ret = recv(connfd, buffer, BUF_SIZE - 1, 0);printf("got %d bytes of normal data '%s'\n", ret, buffer);memset(buffer, '\0', BUF_SIZE);ret = recv(connfd, buffer, BUF_SIZE - 1, MSG_OOB);  //flags=MSG_OOB 带外数据printf("got %d bytes of oob data '%s'\n", ret, buffer);memset(buffer, '\0', BUF_SIZE);ret = recv(connfd, buffer, BUF_SIZE - 1, 0);printf("got %d bytes of normal data '%s'\n", ret, buffer);memset(buffer, '\0', BUF_SIZE);ret = recv(connfd, buffer, BUF_SIZE - 1, 0);printf("got %d bytes of normal data '%s'\n", ret, buffer);close(connfd);}close(sock);return 0;
}

UDP数据读写:recvfrom(UDP通信没有连接的概念,每次读取数据都要获取发送端的socket地址)、sendto(写入数据)。
通用数据读写函数:recvmsg(数据将被读取并存放在msg_iovlen块分散的内存,称为分散读)、sendmsg(msg_iovlen块分散内存中的数据将被一并发送,称为集中写)。

5.9 带外标记

内核检测到TCP紧急标志,然后通知应用程序带外数据到达的两种常见方式是:I/O复用产生的异常事件和SIGURG信号。应用程序通过sockatmark函数得到带外数据在数据流中的具体位置。

5.10 地址信息函数

getsockname:获取sockfd对应的本端socket地址,存储与参数指定的内存中。
getpeername:获取远端socket地址。

5.11 socket选项

读取和设置socket文件描述符属性:getsockopt、setsockopt。
对服务器而言,部分socket选项(在TCP同步报文段中设置)只能在调用listen(listen监听队列中的连接至少是SYN_RCVD状态,三次握手前两步至少已经完成,同步报文段已发出)之前设置才有效。
对客户端而言,部分socket选项应该在connect之前设置。

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>int main(int argc, char *argv[])
{if (argc <= 2) {printf("usage: %s ip_address port_number\n", basename(argv[0]));return 1;}const char *ip = argv[1];int port = atoi(argv[2]);int sock = socket(PF_INET, SOCK_STREAM, 0);assert(sock);int reuse = 1;//即使 sock 处于 TIME_WAIT 状态,与之绑定的 socket 地址也可以立即被重用setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));struct sockaddr_in address;bzero(&address, sizeof(address));address.sin_family = AF_INET;inet_pton(AF_INET, ip, &address.sin_addr);address.sin_port = htons(port);int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));assert(ret != -1);ret = listen(sock, 5);assert(ret != -1);struct sockaddr_in client;socklen_t client_addrlength = sizeof(client);int connfd = accept(sock, (struct sockaddr *)&client, &client_addrlength);if (connfd < 0) {printf("errno is %d\n", errno);} else {char remote[INET_ADDRSTRLEN];printf("connected with ip: %s and port: %d\n",inet_ntop(AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN), ntohs(client.sin_port));close(connfd);}close(sock);return 0;
}
#include <sys/socket.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>#define BUFFER_SIZE 512int main(int argc, char *argv[])
{if (argc <= 2) {printf("usage: %s ip_address port_number send_buffer_size\n",basename(argv[0]));return 1;}const char *ip = argv[1];int port = atoi(argv[2]);struct sockaddr_in server_address;bzero(&server_address, sizeof(server_address));server_address.sin_family = AF_INET;inet_pton(AF_INET, ip, &server_address.sin_addr);server_address.sin_port = htons(port);int sock = socket(PF_INET, SOCK_STREAM, 0);assert(sock >= 0);int sendbuf = atoi(argv[3]);int len = sizeof(sendbuf);/* 先设置TCP发送缓冲区的大小,然后立即读取之 */setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, sizeof(sendbuf));getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, (socklen_t *)&len);printf("the tcp send buffer size after setting is %d\n", sendbuf);if(connect(sock, (struct sockaddr *)&server_address, sizeof(server_address)) != -1){char buffer[BUFFER_SIZE];memset(buffer, 'a', BUFFER_SIZE);send(sock, buffer, BUFFER_SIZE, 0);}close(sock);return 0;
}
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>#define BUFFER_SIZE 1024int main(int argc, char *argv[])
{if (argc <= 2) {printf("usage: %s ip_address port_number recv_buffer_size\n",basename(argv[0]));return 1;}const char *ip = argv[1];int port = atoi(argv[2]);struct sockaddr_in address;bzero(&address, sizeof(address));address.sin_family = AF_INET;inet_pton(AF_INET, ip, &address.sin_addr);address.sin_port = htons(port);int sock = socket(PF_INET, SOCK_STREAM, 0);assert(sock >- 0);int recvbuf = atoi(argv[3]);int len = sizeof(recvbuf);/* 先设置TCP接受缓冲区的大小,然后立即读取之 */setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, sizeof(recvbuf));getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &recvbuf, (socklen_t *)&len);printf("the tcp receive buffer size after setting is %d\n", recvbuf);int ret = bind(sock, (struct sockaddr *)&address, sizeof(address));assert(ret != -1);ret = listen(sock, 5);assert(ret != -1);struct sockaddr_in client;socklen_t client_addrlength = sizeof(client);int connfd = accept(sock, (struct sockaddr *)&client, &client_addrlength);if (connfd < 0) {printf("errno is %d\n", errno);} else {char buffer[BUFFER_SIZE];memset(buffer, '\0', BUFFER_SIZE);while(recv(connfd, buffer, BUFFER_SIZE - 1, 0) > 0) {}close(connfd);}close(sock);return 0;
}

setsockopt设置缓冲区大小时,系统会将其值加倍,且TCP接收缓冲区最小值为256字节,发送缓冲区最小值为2048字节。
SO_RCVLOWAT 和 SO_SNDLOWAT 选项分别表示TCP接收缓冲区和发送缓冲区的低水位标记。它们一般被I/O复用系统调用,来判断socket是否可读或可写。默认1字节。
SO_LINGER 选项用于控制 close 系统调用在关闭TCP连接时的行为。默认情况下,当我们使用 close 来关闭一个socket时,close 将立即返回,TCP模块负责把该 socket对应的TCP发送缓冲区中残留的数据发送给对方。

5.12 网络信息API

socket地址由IP地址和端口号组成,不便于扩展,可以用主机名代替IP地址,用服务名称代替端口号。
gethostbyname:根据主机名称获取主机完整信息。
gethostbyaddr:根据IP地址获取主机完整信息。
getservbyname:根据名称获取某个服务的完整信息。
getservbyport:根据端口号获取某个服务的完整信息。
四个函数均不可重入,即非线程安全。尾部加_r是可重入版本。

#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>int main(int argc, char *argv[])
{assert(argc == 2);char *host = argv[1];/* 获取目标主机地址信息 */struct hostent *hostinfo = gethostbyname(host);assert(hostinfo);/* 获取daytime服务信息 */struct servent *servinfo = getservbyname("daytime", "tcp");assert(servinfo);printf("daytime port is :%d\n", ntohs(servinfo->s_port));struct sockaddr_in address;address.sin_family = AF_INET;address.sin_port = servinfo->s_port;/* 注意下面的代码,因为h_addr_list本身使用网络字节序的地址列表,所以使用其中的IP* 地址时,无需对目标IP地址转换字节序 */address.sin_addr = *(struct in_addr *)*hostinfo->h_addr_list;int sockfd = socket(PF_INET, SOCK_STREAM, 0);int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));assert(result != -1);char buffer[128];result = read(sockfd, buffer, sizeof(buffer));assert(result > 0);buffer[result] = '\0';printf("the day time is : %s\n", buffer);close(sockfd);return 0;
}

getaddrinfo:通过主机名获得IP地址(内部使用的是 gethostbyname 函数),也能通过服务名获得端口号(内部使用的是 getservbyname 函数)。它是否可重人取决于其内部调用的函数是否是它们的可重入版本。将隐式地分配堆内存,因为参数result指针(struct addrinfo*类型,指向链表,存储结果)原本是没有指向一块合法内存的,调用结束后,必须使用 freeaddrinfo 函数来释放这块内存。
getnameinfo:通过socket地址同时获得以字符串表示的主机名(内部使用的是gethostbyaddr 函数)和服务名(内部使用的是 getservbyport 函数),它是否可重入取决于其内部函数是否是它们的可重入版本。

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

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

相关文章

全光谱护眼灯有哪些?2023全光谱护眼台灯推荐

随着电子设备的不断普及&#xff0c;手机、平板电脑、显示器、电视机等几乎是家家户户的必备品&#xff0c;也正因为眼睛有那么多时间、那么多机会去盯着屏幕&#xff0c;所以如今近视低龄化现象也越来越严重了。随着科技的不断发展&#xff0c;台灯的发展也越来越多样化&#…

成都瀚网科技有限公司:开抖音店铺有哪些注意事项?

成功经营一个小店不仅仅是发布产品视频那么简单&#xff0c;还需要注意一些重要的事情。开抖音店铺需要注意以下几点&#xff1a; 1、开抖音店铺有哪些注意事项&#xff1f; 合规管理&#xff1a;在抖音开店&#xff0c;首先要确保自己的运营合规。遵守相关法律法规及平台规定&…

关于yield你只需要知道这2点

1.yield是放在函数里面的&#xff0c;且带有yield的函数就叫做生成器&#xff0c;这时函数就成了一个对象&#xff0c;而不能把它作为函数来对待 def foo(num):print("introduction:")while:if num < 10:num 1yield num g foo()看上面的代码&#xff0c;我们把…

Python 基础问题

文章目录 python 中有哪些类型或数据结构什么是有序和无序什么是可变和不可变字典中&#xff0c;什么类型可以当做key去使用闭包是什么装饰器是什么 python 中有哪些类型或数据结构 在 Python 中&#xff0c;有多种类型和数据结构可用于存储和组织数据。以下是一些常见的类型和…

golang——工程组件logrus日志记录框架(结构化记录,支持文件切割,hook)

logrus 介绍一个golang 日志框架logrus 支持文本与JSON数据格式支持结构化记录支持hook 文档介绍 logrus文档 std 官方案例介绍了如何配置std打印 package mainimport ("os"log "github.com/sirupsen/logrus" )func init() {// Log as JSON instead…

Unoconv入门介绍和问题汇总

简介&#xff1a;Unoconv是一款基于LibreOffice/ Openoffice开发的命令行工具&#xff0c;可以将不同格式的文件&#xff08;如DOC、PPT、PDF等&#xff09;在不同的操作系统上通过Libreoffice/Openoffice转换为PDF、ODT、DOC、PNG、PPTX等格式&#xff0c;并支持在命令行中指定…

Elasticsearch学习笔记

1.核心概念 bucket: 一个数据分组&#xff08;类似于sql group by以后的数据&#xff09;metric&#xff1a;对bucket执行的某种聚合分析的操作&#xff0c;比如说求平均值&#xff0c;最大值&#xff0c;最小值。一些系列的统计方法(类似 select count(1) MAX MIN AVG) 请…

CUDA学习笔记5——CUDA程序错误检测

CUDA程序错误检测 所有CUDA的API函数都有一个类型为cudaError_t的返回值&#xff0c;代表了一种错误信息&#xff1b;只有返回cudaSuccess时&#xff0c;才是成功调用。 cudaGetLastError()用来检测核函数的执行是否出错cudaGetErrorString()输出错误信息 #include <stdi…

【lesson13】进程控制初识

文章目录 进程创建 进程创建 请你描述一下&#xff0c;fork创建子进程操作系统都做了什么&#xff1f; fork创建子进程&#xff0c;系统里多了一个进程&#xff0c;进程 内核数据结构 进程代码数据&#xff0c;内核数据结构由OS维护&#xff0c;进程代码数据一般由磁盘维护。…

位运算相关笔记

位运算 Part 1&#xff1a;基础 左移&#xff1a;左移一位&#xff0c;相当于某数乘以 2 2 2。左移 x x x位,相当于该数乘以 2 x 2^x 2x。 右移&#xff1a;右移一位&#xff0c;相当于某数除以 2 2 2。右移 x x x位&#xff0c;相当于该数除以 2 x 2^x 2x。 与运算&…

【Edabit 算法 ★☆☆☆☆☆】【分钟转秒数】Convert Minutes into Seconds

【Edabit 算法 ★☆☆☆☆☆】【分钟转秒数】Convert Minutes into Seconds math numbers Instructions Write a function that takes an integer minutes and converts it to seconds. Examples convert(5) // 300 convert(3) // 180 convert(2) // 120Notes Don’t forge…

markdown语法(更新中)

a ⃗ \vec{a} a 向量 a ‾ \overline{a} a 平均值 a ‾ \underline{a} a​下横线 a ^ \widehat{a} a (线性回归&#xff0c;直线方程) y尖 a ~ \widetilde{a} a 颚化符号 等价无穷小 a ˙ \dot{a} a˙ 一阶导数 a \ddot{a} a 二阶导数 $\vec{a}$ 向量 $\overline{a}$ …

【三维重建】DreamGaussian:高斯splatting的单视图3D内容生成(原理+代码)

文章目录 摘要一、前言二、相关工作2.1 3D表示2.2 Text-to-3D2.3 Image-to-3D 三、本文方法3.1生成式 高斯 splitting3.2 高效的 mesh 提取3.3 UV空间的纹理优化 四. 实验4.1实施细节4.2 定性比较4.3 定量比较4.4 消融实验 总结&#xff08;特点、局限性&#xff09; 五、安装与…

【框架源码篇 01】Spring源码-手写IOC

Spring源码手写篇-手写IoC 一、IoC分析 1.Spring的核心 在Spring中非常核心的内容是 IOC和 AOP. 2.IoC的几个疑问? 2.1 IoC是什么&#xff1f; IoC:Inversion of Control 控制反转&#xff0c;简单理解就是&#xff1a;依赖对象的获得被反转了。 2.2 IoC有什么好处? IoC带…

[ROS2系列] ORBBEC(奥比中光)AstraPro相机在ROS2进行rtabmap 3D建图

目录 背景&#xff1a; 一、驱动AstraPro摄像头 二、安装rtabmap error1&#xff1a;缺包 三、尝试 四、参数讲解 五、运行 error2: Did not receive data since 5 seconds! 六、效果​编辑 error4: 背景&#xff1a; 1、设备&#xff1a;pc&#xff1b;jeston agx …

语音芯片KT142C两种音频输出方式PWM和DAC的区别

目录 语音芯片KT142C两种音频输出方式PWM和DAC的区别 一般的语音芯片&#xff0c;输出方式&#xff0c;无外乎两种&#xff0c;即dac输出&#xff0c;或者PWM输出 其中dac的输出&#xff0c;一般应用场景都是外挂功放芯片&#xff0c;实现声音的放大&#xff0c;比如常用的音箱…

WMS透明仓库:实现仓储的全方位可视化与优化

一、WMS透明仓库的定义与特点 1. WMS透明仓库的定义&#xff1a;WMS透明仓库是一种基于信息技术的仓库管理系统&#xff0c;通过实时数据采集、分析和可视化&#xff0c;将仓库内外的物流流程、库存状态、人员活动等信息以透明的方式展示给相关利益方。 2. 实时数据采集&…

力扣每日一题50:Pow(x,n)

题目描述&#xff1a; 实现 pow(x, n) &#xff0c;即计算 x 的整数 n 次幂函数&#xff08;即&#xff0c;xn &#xff09;。 示例 1&#xff1a; 输入&#xff1a;x 2.00000, n 10 输出&#xff1a;1024.00000示例 2&#xff1a; 输入&#xff1a;x 2.10000, n 3 输出…

性能评测 | GreatDB VIP PLUGIN方案 VS MySQL InnoDB Cluster高可用方案

前言 最近&#xff0c;我们与许多数据库用户进行了沟通和调研&#xff0c;了解到&#xff0c;目前仍有相当一部分投产的MySQL高可用或故障转移方案&#xff0c;用到了读写分离功能或业务接入VIP&#xff08;Virtual IP Address&#xff09;的方式&#xff0c;来屏蔽后端数据库架…

2022年合肥市庐阳区信息学区赛(初中组)

2022年合肥市庐阳区信息学区赛(初中组)第1题 信封 题目描述 有两张贺卡,需要装入一个信封邮寄,当然贺卡都是正的放进信封中,也就是两张贺卡的边同信封的边一都是平行的,不允许斜着放入信封。请问信封的最小尺寸。 输入格式 共两行。每行两个整数,分别表示两张贺卡的两个…