Linux C/C++ 原始套接字:打造链路层ping实现
在C/C++中,我们可以使用socket函数来创建套接字。我们需要指定地址族为AF_PACKET,协议为htons(ETH_P_ALL)来捕获所有传入和传出的数据包。
可以使用sendto和recvfrom函数来发送和接收数据包。我们需要构建一个合法的链路层数据包,在数据包的头部添加目标MAC地址和源MAC地址,并指定以太网类型为htons(ETH_P_IP)。
然后,可以在发送前设置IP头部和ICMP头部。为了构建一个有效的ICMP Echo Request消息,需要正确设置ICMP类型、代码、校验和和标识符字段。
最后,通过接收数据包并解析来获取目标设备的响应。需要注意处理ICMP Echo Reply消息,并检查序列号和校验和的正确性。
原始套接字的概念
原始套接字(Raw Socket)是一种在网络编程中使用的特殊套接字类型,它允许应用程序直接访问和操作网络协议栈中的网络层和传输层协议。与其他套接字类型(如TCP套接字和UDP套接字)相比,原始套接字提供更底层的网络访问能力。
原始套接字允许应用程序发送和接收自定义的网络数据包,绕过操作系统的网络协议栈的上层处理。应用程序可以直接构建和解析网络协议的头部,并将数据发送到特定的目标主机或接口。这种直接的访问能力使得原始套接字成为一种强大的工具,能够实现各种高级网络功能和协议,如网络监测、网络仿真、路由协议实现等。
使用原始套接字时,应用程序需要具有足够的权限来使用它,通常需要以超级用户(如root用户)的身份运行。这是因为原始套接字具有更高的网络访问权限,可以直接操作底层网络接口和协议,不受操作系统的保护和限制。
原始套接字的使用需要对网络协议和协议栈的内部工作原理有一定的了解,以正确构建和解析网络数据包的格式。它通常用于开发高级网络应用程序、网络调试工具、网络安全评估工具等场景中,对网络进行更底层的控制和操作。而在普通的网络编程中,一般使用更高级的套接字类型(如TCP套接字和UDP套接字)来实现应用层协议的交互。
链路层ping实现原理
链路层ping工具使用原始套接字实现,涉及以下原理:
-
原始套接字:原始套接字是一种可以直接访问网络协议栈的套接字类型。它允许应用程序直接发送和接收网络数据包,绕过传输层和应用层协议的处理。
-
ICMP协议:链路层ping工具使用的是ICMP协议(Internet Control Message Protocol)。ICMP协议是在网络层之上使用的一种控制协议,用于在IP网络中传递错误消息和有关网络状态的信息。
-
ICMP Echo请求和响应:链路层ping工具通常发送ICMP Echo请求(也称为ping请求)到目标主机,并期望收到相应的ICMP Echo响应。当目标主机收到Echo请求时,它将生成一个对应的Echo响应并将其发送回源主机。
-
构造和解析ICMP数据包:链路层ping工具需要构造符合ICMP协议规范的数据包,并能够解析收到的ICMP响应数据包。ICMP数据包包含ICMP报文头部和负载数据,其中ICMP Echo请求和响应的报文类型为8和0。
-
IP数据包发送和接收:链路层ping工具使用原始套接字发送和接收IP数据包。在构造ICMP数据包时,会将ICMP报文作为负载放入IP数据报中,并设置目标IP地址、源IP地址、协议类型等IP头部字段。
-
校验和计算:在构造ICMP数据包时,需要计算校验和字段。校验和计算是为了确保数据在传输过程中的完整性,接收端可以通过校验和验证数据包的正确性。
-
Root权限:由于原始套接字需要对网络接口进行底层访问,因此使用原始套接字通常需要root权限。
链路层ping的需求
链路层ping工具的需求可以归纳为以下几个方面:
-
网络连通性测试:链路层ping工具可以用于测试两个网络节点之间的连通性。它能够发送ICMP Echo请求到目标节点,并获取对应的ICMP Echo响应,从而确定节点之间是否能够正常通信。
-
网络性能测试:链路层ping工具可以测量网络链路的性能指标,如往返延迟(RTT,Round-Trip Time)和丢包率。通过发送ICMP Echo请求,并测量请求到响应的时间差,可以估计网络链路的延迟。同时,通过记录响应丢失的数量,可以计算丢包率。
-
网络故障排除:当网络出现故障时,链路层ping工具可以用于排查故障原因。通过在网络各个节点之间进行ping测试,可以确定具体哪个节点之间出现问题,从而有针对性地进行故障排除。
-
确定网络拓扑:通过链路层ping工具,在网络中的不同节点之间进行ping测试,可以帮助确定网络的拓扑结构,包括节点之间的连接方式和路径。
-
定位网络性能瓶颈:链路层ping工具可以用于定位网络中的性能瓶颈。通过在不同节点之间进行ping测试,并分析各个测试点的延迟和丢包率,可以确定网络的性能瓶颈所在,从而采取相应的优化措施。
-
监控网络稳定性:链路层ping工具可以用于监测网络的稳定性。通过定期进行ping测试,并记录网络的性能指标,可以获得网络的运行情况,并及时发现潜在的问题。
-
自动化网络管理:链路层ping工具可以通过脚本或自动化程序进行批量测试和监控,从而实现对大规模网络的管理和优化。
Linux C/C++ 原始套接字:打造链路层ping工具
使用原始套接字(AF_PACKET、SOCK_raw)实现的链接层ping实用程序.
struct ping_config {unsigned long count;unsigned long size;unsigned long interval; /* in milliseconds */int listen;const char *ifname;int ifindex;unsigned char ifaddr[ETH_ALEN];struct list_head hosts;unsigned char replyto[ETH_ALEN];
};struct ping_host {unsigned char host[ETH_ALEN];struct list_head node;
};enum llcmp_types {LLCMP_ECHO_REQUEST = 128,LLCMP_ECHO_REPLY = 129,
};/* 类似于ICMPv6回显请求/回复的结构 */
struct ping_header {struct ethhdr ethhdr;uint8_t reserved1; uint8_t reserved2; __be16 payload_len; uint8_t type;uint8_t reserved3; uint16_t reserved4; __be16 identifier;__be16 seqno;uint8_t replyto[6];
} __attribute__((packed));struct ping_config ping_config;...static void sigint_handler(int signo) {term = 1;
}static int init_socket(void)
{struct ifreq ifreq;const char *ifname = ping_config.ifname;int ret;struct epoll_event event;/* create socket */sd = socket(AF_PACKET, SOCK_RAW, htons(LLCMP_ETHER_TYPE));if (sd < 0) {fprintf(stderr,"Error: Can't open a raw socket for ether type 0x%04x\n",LLCMP_ETHER_TYPE);return -EPERM;}/* bind socket to specific interface */ret = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, ifname,strlen(ifname));if (ret < 0) {fprintf(stderr, "Error: Can't bind to device %s\n", ifname);goto err;}/* get MAC address and index of interface */memset(&ifreq, 0, sizeof(ifreq));strcpy(ifreq.ifr_name, ifname);ret = ioctl(sd, SIOCGIFHWADDR, &ifreq);if (ret < 0) {fprintf(stderr, "Error: Can't get mac address of interface\n");goto err;}eth_copy(ping_config.ifaddr,(unsigned char *)ifreq.ifr_hwaddr.sa_data);ret = ioctl(sd, SIOCGIFINDEX, &ifreq);if (ret < 0) {fprintf(stderr, "Error: Can't get interface index\n");goto err;}ping_config.ifindex = ifreq.ifr_ifindex;/* add socket to epoll */memset(&event, 0, sizeof(event));event.events = EPOLLIN;event.data.fd = sd;if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sd, &event)) {fprintf(stderr, "Error: Can't add socket to epoll.\n");ret = -EPERM;goto err;}return 0;err:close(sd);return ret;
}static int config_add_host(const char *host) {struct ping_host *ping_host;ping_host = malloc(sizeof(*ping_host));if (!ping_host) {fprintf(stderr,"Error: Can't allocate host: out-of-memory\n");return -ENOMEM;}if (eth_str2bin(host, ping_host->host) < 0) {free(ping_host);fprintf(stderr, "Error: invalid MAC address: %s\n", host);return -EINVAL;}if (eth_is_zero(ping_host->host)) {free(ping_host);fprintf(stderr,"Error: zero MAC address not allowed\n");return -EINVAL;}list_add_tail(&ping_host->node, &ping_config.hosts);return 0;
}static void config_free_hosts(void) {struct ping_host *host, *tmp;list_for_each_entry_safe(host, tmp, &ping_config.hosts, node) {list_del(&host->node);free(host);}
}static int init_args(int argc, char *argv[])
{
...struct option long_opts[] ={{"help", no_argument, 0, 'h'},{"listen", no_argument, 0, 'l'},{"interface", required_argument, 0, 'I'},{"count", required_argument, 0, 'c'},{"size", required_argument, 0, 's'},{"interval", required_argument, 0, 'i'},{"replyto", required_argument, 0, 'r'},{0, 0, 0, 0}};while(1) {opt = getopt_long(argc, argv, "hlc:s:i:I:r:", long_opts, &opt_idx);if (opt == -1)break;errno = 0;switch(opt) {case 'h':usage(argv[0]);exit(2);break;case 'l':ping_config.listen = 1;break;case 'c':ping_config.count = strtoul(optarg, &endptr, 10);if (errno != 0 || endptr == optarg ||ping_config.count == 0)goto parse_err;break;case 's':ping_config.size = strtoul(optarg, &endptr, 10);if (errno != 0 || endptr == optarg)goto parse_err;break;case 'i':if (sscanf(optarg, "%f", &interval) == EOF ||interval <= 0 || interval > UINT16_MAX)goto parse_err;ping_config.interval = (unsigned long)(1000 * interval);if (ping_config.interval == 0)goto parse_err;break;case 'I':ping_config.ifname = optarg;break;case 'r':if (eth_str2bin(optarg, ping_config.replyto) < 0) {fprintf(stderr, "Error: invalid MAC address: %s\n\n",argv[optind]);exit(2);}if (eth_is_zero(ping_config.replyto)) {fprintf(stderr,"Error: zero MAC address not allowed\n\n");exit(2);}break;default:fprintf(stderr, "\n");usage(argv[0]);exit(2);}}...
}static int init_request_buffer(void)
{
...request_buffer = malloc(len);if (!request_buffer)return -ENOMEM;memset(request_buffer, 0, len);eth_copy(request_buffer->ethhdr.h_source, ping_config.ifaddr);request_buffer->ethhdr.h_proto = htons(LLCMP_ETHER_TYPE);request_buffer->payload_len = htons(ping_config.size);request_buffer->type = LLCMP_ECHO_REQUEST;request_buffer->identifier = identifier;if (eth_is_zero(ping_config.replyto))eth_copy(request_buffer->replyto, ping_config.ifaddr);elseeth_copy(request_buffer->replyto, ping_config.replyto);return 0;
}static int init_ping(int argc, char *argv[])
{int ret;INIT_LIST_HEAD(&ping_config.hosts);ret = init_args(argc, argv);if (ret < 0)return -EINVAL;epoll_fd = epoll_create1(0);if (epoll_fd < 0) {fprintf(stderr, "Can't create epoll file descriptor.\n");return -EPERM;}ret = init_socket();if (ret < 0)goto err1;if (signal(SIGINT, sigint_handler) == SIG_ERR) {fprintf(stderr, "Can't establish SIGINT handler.\n");ret = -EPERM;goto err2;}if (!ping_config.listen) {ret = init_request_buffer();if (ret < 0) {fprintf(stderr,"Can't allocate request buffer: %s\n",(ret == -EBUSY) ?"no entropy" : "out-of-memory");goto err2;}}return 0;err2:close(sd);
err1:close(epoll_fd);return ret;
}static int send_packet(struct ping_header *plhdr)
{
...ret = sendto(sd, plhdr, sizeof(*plhdr) + ntohs(plhdr->payload_len), 0,(struct sockaddr*)&addr, sizeof(addr));if (ret < 0) {fprintf(stderr, "Error: Could not send packet: %s\n", strerror(errno));return ret;}return 0;
}static int send_echo_request(void)
{
...list_for_each_entry(host, &ping_config.hosts, node) {eth_copy(request_buffer->ethhdr.h_dest, host->host);ret = send_packet(request_buffer);if (ret < 0)return ret;}rtcidx = (rtcidx + 1) % REQUEST_TIME_CACHE_SIZE;clock_gettime(CLOCK_MONOTONIC, &request_time_cache[rtcidx]);return 0;
}static void print_echo_request(struct ping_header *plhdr)
{
...eth_bin2str(plhdr->ethhdr.h_source, src);eth_bin2str(plhdr->ethhdr.h_dest, dst);eth_bin2str(plhdr->replyto, replyto);if (plhdr->type == LLCMP_ECHO_REQUEST)snprintf(type, sizeof(type), "echo request");else if (plhdr->type == LLCMP_ECHO_REPLY)snprintf(type, sizeof(type), "echo reply");elsesnprintf(type, sizeof(type), "unknown");printf("%s > %s, LLCMP, %s, reply-to %s, id 0x%04x, seq %u, length %u(%u)\n",src, dst, type, replyto, ntohs(plhdr->identifier),ntohs(plhdr->seqno), paylen, pktlen);
}static int recv_echo_request(struct ping_header *plhdr)
{print_echo_request(plhdr);plhdr->type = LLCMP_ECHO_REPLY;eth_copy(plhdr->ethhdr.h_dest, plhdr->replyto);eth_copy(plhdr->ethhdr.h_source, ping_config.ifaddr);eth_copy(plhdr->replyto, ping_config.ifaddr);return send_packet(plhdr);
}static int check_seqno_range(const uint16_t recv_seqno)
{
...if (send_seqno > REQUEST_TIME_CACHE_SIZE)last_seqno = send_seqno - REQUEST_TIME_CACHE_SIZE + 1;/* 不支持seqno环绕 */if (send_seqno > recv_seqno ||recv_seqno < last_seqno)return -ERANGE;return 0;
}static int echo_reply_timediffus_get(unsigned long *timediff,const uint16_t seqno)
{
...idx = (seqno - 1) % REQUEST_TIME_CACHE_SIZE;clock_gettime(CLOCK_MONOTONIC, &now);diff = timespec_diffus(request_time_cache[idx], now);diff = diff < 0 ? 0 : diff;if (diff > ULONG_MAX)return -ERANGE;*timediff = (unsigned long)diff;return 0;
}static int recv_echo_reply(struct ping_header *plhdr)
{
...eth_bin2str(plhdr->ethhdr.h_source, src);eth_bin2str(plhdr->replyto, replyto);if (echo_reply_timediffus_get(&timediff_us, ntohs(plhdr->seqno)) < 0)printf("%u(%u) bytes from %s (via %s): llcmp_seq=%u\n",paylen, pktlen, replyto, src, ntohs(plhdr->seqno));elseprintf("%u(%u) bytes from %s (via %s): llcmp_seq=%u time=%lu.%03lu ms\n",paylen, pktlen, replyto, src, ntohs(plhdr->seqno),timediff_us / 1000, timediff_us % 1000);return 0;
}static int recv_check_header(struct ping_header *plhdr, int len)
{
...if (ntohs(plhdr->payload_len) > len - sizeof(*plhdr)) {fprintf(stderr,"Warning: received malformed packet: payload_len too large (%i > %zu bytes)\n",ntohs(plhdr->payload_len), (size_t)len - sizeof(*plhdr));return -EINVAL;}if (ntohs(plhdr->ethhdr.h_proto) != LLCMP_ETHER_TYPE) {fprintf(stderr,"Warning: received malformed packet: invalid ether type (0x%04x, expected 0x%04x)\n",ntohs(plhdr->ethhdr.h_proto), LLCMP_ETHER_TYPE);return -EINVAL;}/* 源代码检查:忽略自己的帧 */if (eth_is_own(plhdr->ethhdr.h_source))return -EADDRNOTAVAIL;/* 目的地检查:只接受多播和自己的地址 */if (!eth_is_own(plhdr->ethhdr.h_dest) &&!eth_is_multicast(plhdr->ethhdr.h_dest))return -EADDRNOTAVAIL;return 0;
}static int recv_packet(int sd)
{
.../* 忽略不适合我们的东西 */if (ret == -EADDRNOTAVAIL)return 0;/* 畸形数据包 */else if (ret < 0)return ret;switch (plhdr->type) {case LLCMP_ECHO_REQUEST:/* 仅适用于监听 */if (!ping_config.listen)return 0;ret = recv_echo_request(plhdr);break;case LLCMP_ECHO_REPLY:/* 仅适用于请求者 */if (ping_config.listen)return 0;/* 忽略不是来自我们会话的回复 */if (plhdr->identifier != request_buffer->identifier)return 0;ret = recv_echo_reply(plhdr);break;default:fprintf(stderr,"Warning: unknown ping type: %u\n", plhdr->type);return -EINVAL;}return ret;
}static int get_next_timeout()
{
...add.tv_sec = ping_config.interval / 1000;add.tv_nsec = (ping_config.interval % 1000) * (1000*1000);next = timespec_sum(request_time_cache[rtcidx], add);diff = timespec_diffus(now, next) / 1000;if (diff < 0)return 0;return (diff > INT_MAX) ? INT_MAX : (int)diff;
}int main(int argc, char *argv[])
{
...while(!term) {if (!ping_config.listen) {if (!ev_count) {if (count &&count <= ntohs(request_buffer->seqno))break;send_echo_request();}timeout = get_next_timeout();}ev_count = epoll_wait(epoll_fd, events, MAX_EVENTS,timeout);for(int i = 0; i < ev_count; i++)recv_packet(events[i].data.fd);}...return 0;
}...
If you need the complete source code, please add the WeChat number (c17865354792)
dping利用其自己的非正式以太类型(0x4304),该类型允许在低级别上测试链路,而不依赖于像IPv4/IPv6这样的网络协议。在发送回显请求或回显回复之前,不需要ARP或ICMPv6邻居发现。
要使用dping,请在侦听模式下启动一个dping实例(–listen)。然后,您可以通过在指定了目标主机的MAC地址的不同主机上启动另一个dping实例来ping它。
-
示例:
dping可以用于单独测试链路的单播和多播能力。
-
运行效果:
单播测试:
Listener on ens33/MAC address :

Sender on ens37/MAC address :

广播测试:
回复功能可用于强制侦听器回复广播地址。这样,就可以在不依赖单播的情况下单独测试接口的广播功能。
Listener on ens33/MAC address :

Sender on ens37/MAC address :

数据包格式
- 链路层控制消息协议
数据包格式(主要)类似于ICMPv6,即IPv6的互联网控制消息协议。目前,LLCMP回声请求和LLCMP回声回复在dping中实现:
0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Ethernet Destination ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... Ethernet Destination | Ethernet Source ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... Ethernet Source |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| LLCMP Ethernet Type | Reserved1 | Reserved2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| LLCMP Payload Length | LLCMP Type | Reserved3 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| LLCMP Identifier | LLCMP Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| LLCMP Reply-To ... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| ... LLCMP Reply-To |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
以太网目标:
接收器的6字节MAC地址。除00:00:00:00:00:00外,其他时间都允许。
以太网源:
6字节的发送方MAC地址。只允许单播MAC地址(第一个八位字节的最低有效位设置为0,而不是00:00:00:00:00)。
LLCMP以太网类型:
0x4304(网络字节顺序/big-endian),非官方以太网类型。
预留1:
保留,稍后可能有LLCMP跃点限制
预留2:
为稍后的服务质量保留,可能是LLCMP流量类
LLCMP有效载荷长度:
LLCMP标头之外的字节数。通常为“帧大小-14字节以太网头-16字节LLCMP头”。(待办事项:可能将LLCMP标头大小定义为6字节,因此排除回声请求/回复特定字段“标识符”、“序列号”和“回复”?)
LLCMP类型:
当前实施/定义:
LLCMP回声请求,128
LLCMP回声回复,129
预留3:
保留,以后可能是特定LLCMP类型的代码/子类型或标志
LLCMP标识符:
随机,但对于特定LLCMP回显请求/回复会话固定为2个字节
LLCMP序列号:
一个2字节的序列号(网络字节顺序/big-endian),用于特定的LLCMP回显请求/回复交换。从1开始,每次回显请求增加1。
LLCMP回复:
6字节MAC地址。在LLCMP回声请求中,指定接收器应在其LLCMP回声回复中使用的以太网目的地。在LLCMP中,回声回复等于LLCMP回声回复的以太网源。也可用于检测第2层源NAT/代理。
总结
原始套接字是一种特殊的套接字类型,允许应用程序直接访问和操作网络协议栈中的网络层和传输层协议。使用原始套接字可以实现更底层的网络访问和控制,例如构建和解析自定义的网络数据包。
在实现链路层ping工具时,原始套接字可以用于发送和接收ICMP Echo请求和响应,从而测试网络的连通性、性能和稳定性。
通过原始套接字,我们可以 bypass 操作系统的一些网络协议栈处理,直接操作链路层数据,以及分析 network path 过程中的网络延迟和网络损耗,进而诊断、分析和解决网络问题。使用原始套接字还需要注意权限管理和安全性,并且需要对网络协议和协议栈有一定的了解。
Welcome to follow WeChat official account【程序猿编码】
参考:RFC 792、RFC 791、RFC 4861、RFC 4443
相关文章:
Linux C/C++ 原始套接字:打造链路层ping实现
在C/C中,我们可以使用socket函数来创建套接字。我们需要指定地址族为AF_PACKET,协议为htons(ETH_P_ALL)来捕获所有传入和传出的数据包。 可以使用sendto和recvfrom函数来发送和接收数据包。我们需要构建一个合法的链路层数据包,在数据包的头…...
TCP 粘包/拆包
文章目录 概述粘包拆包发生场景解决TCP粘包和拆包问题的常见方法Netty对粘包和拆包问题的处理小结 概述 TCP的粘包和拆包问题往往出现在基于TCP协议的通讯中,比如RPC框架、Netty等 TCP 粘包/拆包 就是你基于 TCP 发送数据的时候,出现了多个字符串“粘”…...
【Spring Boot 3】应用启动执行特定逻辑
【Spring Boot 3】应用启动执行特定逻辑 背景介绍开发环境开发步骤及源码工程目录结构总结背景 软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花…...
设计模式(行为型模式)观察者模式
目录 一、简介二、观察者模式2.1、事件接口及其实现2.2、观察者接口及其实现2.3、主题接口及其实现2.4、使用 三、优点与缺点 一、简介 观察者模式(Observer Pattern)是一种行为设计模式,它定义了一种一对多的依赖关系,当一个对象…...
Windows 版Oracle 数据库(安装)详细过程
首先到官网上去下载oracle64位的安装程序 第一步:将两个datebase文件夹解压到同一目录中。 当下载完成后,它里面是两个文件夹 win64_11gR2_database_1of2, win64_11gR2_database_2of2,我们需要把其中的一个database文件夹整合在一起(复制一个database文件夹到另一…...
编程实例分享,计费系统一定要安装灯光控制吗?佳易王计时计费管理系统软件V18.0教程说明
编程实例分享,计费系统一定要安装灯光控制吗?佳易王计时计费管理系统软件V18.0教程说明 一、前言 以下教程以 佳易王计时计费软件V18.0为例说明 1、该软件既可以接灯控,也可以不接灯控,如果接灯控,则点击开始计时的时…...
【webpack】优化提升
webpack优化提升 安装webpack相关内容向下兼容游览器-babel/polyfill进一步优化babel/polyfill模块联邦-共享模块如何提升构建性能通用环境下1,webpack更新到最新版本2,将loader应用于最少数量的必要模块3,引导(每个额外的loader/…...
视频无损放大修复工具Topaz Video AI 新手入门教程
想要自学Topaz Video AI ?Topaz Video AI 如何使用?这里给大家带来了视频无损放大修复工具Topaz Video AI 新手入门教程,快来看看吧! 下载:Topaz Video AI for mac 导入您的文件 有两种方法可以将文件导入 Topaz Vid…...
《向量数据库指南》——Milvus Cloud 「部署」:简化部署一直在路上
“docker-compose 能部署分布式吗?”"单机部署为什么还依赖这么多组件?"“大家 Milvus Cloud 集群部署有没有实践过比较好的方案?” 作为一个开源数据库,是否能够进行快速部署,是所有工作的前提。在简化部署的道路上,社区从来没有停止过脚步。2023 年,社区推…...
使用x86架构+Nvidia消费显卡12G显存,搭建智能终端,将大模型本地化部署,说不定是未来方向,开源交互机器人设计
1,大模型本地部署 视频说明地址: https://www.bilibili.com/video/BV1BF4m1u769/ 【创新思考】(1):使用x86架构Nvidia消费显卡12G显存,搭建智能终端,将大模型本地化部署,语音交互机…...
关于RabbitMQ面试题汇总
什么是消息队列?消息队列有什么用? 消息队列是一种在应用程序之间传递消息的通信机制。它是一种典型的生产者-消费者模型,其中生产者负责生成消息并将其发送到队列中,而消费者则从队列中获取消息并进行处理。消息队列的主要目的是…...
GoogleTest 单元测试
假设我们有两个函数 complexFunction 和 helperFunction,其中 complexFunction 调用了 helperFunction。我们将编写测试 complexFunction 的单元测试,并在调用 helperFunction 的地方打桩。 // 复杂函数示例 int helperFunction(int x) {return x * 2; …...
Mac利用brew安装mysql并设置初始密码
前言 之前一直是在windows上开发后段程序,所以只在windows上装mysql。(我记得linux只需要适应yum之类的命令即可) 另外, linux的移步 linux安装mysql (详细步骤,初次初始化,sql小例子,可视化操作客户端推荐) 好家伙,我佛了,写完当天网上发…...
R语言入门笔记2.2
ifelse语句 结构:ifelse(条件,表达式1,表达式2) #满足条件,则进入表达式1,否则为表达式2 例1: > a1 > ifelse(a<0,"a小于0",ifelse(a0,0,"a大于0")) [1] "a大于0"a1 - 这行代码给变量a…...
一般系统的请求认证授权思路【gateway网关+jwt+redis+请求头httpheader】
gateway:网关,我们都知道网关的作用就是对系统的所有请求,网关都会进行拦截,然后做一些操作(例如:设置每个请求的请求头httpHeader,身份认证等等)此时一般会使用到网关过滤器&#x…...
c# 正则表达式 帮助类
public class RegexHelper { /// <summary> /// 验证输入字符串是否与模式字符串匹配,匹配返回true /// </summary> /// <param name"input">输入字符串</param> /// <param nam…...
告别mPDF迎来TCPDF和中文打印遇到的问题
mPDF是一个用PHP编写的开源PDF生成库。它最初由Claus Holler创建,于2004年发布。原来用开源软件打印中文没有问题,最近发现新的软件包中mPDF被TCPDF代替了,当然如果只用西文的PDF是没有发现问题,但要打印中文就有点抓瞎了如图1&am…...
mysql 多数据源
依赖 <dependencies><!--mysql连接--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--多数据源--><dependency><g…...
uniapp 使用renderjs引入echarts
效果图: 1.1renderjs引入echarts 组件zmui-echarts.vue: <template><view class"zmui-echarts" :prop"option" :change:prop"echarts.delay"></view> </template><script>export defaul…...
hr最讨厌这6种应届生简历❌
用求职方法,让你变成offer收割机,是我的责任❗ 简历写得好,面试少不了。最近很多应届生找龙猫帮看简历,我发现很多应届生是真不会写简历啊。 有的简历排版花里胡哨,有的自我评价千篇一律,有的实习经历太过…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
srs linux
下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935,SRS管理页面端口是8080,可…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
