《TCP/IP网络编程》阅读笔记--I/O复用
目录
1--基于I/O复用的服务器
2--select()函数
3--基于I/O复用的回声服务器端
4--send()和recv()函数的常用可选项
5--readv()和writev()函数
1--基于I/O复用的服务器
多进程服务器端具有以下缺点:当有多个客户端发起连接请求时,就会创建多个进程来分别处理客户端的请求,创建多个进程往往需要付出巨大的代价;
I/O复用的服务器端可以减少进程数,无论连接多少个客户端,提供服务的进程都只有 1 个;
2--select()函数
select() 函数可以将多个文件描述符集中到一起来统一监视,监视文件描述符可以视为监视 socket;集中多个文件描述符时需要按照接收、传输和异常三种情况进行区分;
select() 通过 fd_set 数组变量来执行监视操作,fd_set 中文件描述符(索引)对应的位(值)被设置为 1 时,表明该文件描述符是监视对象;
// 对 fd_set 数组的常用操作
FD_ZERO(fd_set* fd_set); // 将 fd_set 变量的所有位初始化为0
FD_SET(int fd, fd_set* fdset); // 在参数 fdset 指向的变量中注册文件描述符fd的信息
FD_CLR(int fd, fd_set* fdset); // 从参数 fdset 指向的变量中消除文件描述符fd的信息
FD_ISSET(int fd, fd_set* fdset); // 若参数 fdset 指向的变量中包含文件描述符fd的信息,则返回true
#include <sys/select.h>
#include <sys/time.h>int select(int maxfd, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout);
// 成功时返回大于 0 的值,值为发生事件的文件描述符数;失败时返回 -1;超时返回 0
// maxfd 表示监视对象文件描述符的数量
// readset 表示将所有关注“是否存在待读取数据”的文件描述符注册到fd_set型变量,并传递其地址值
// writeset 表示将所有关注“是否可传输无阻塞数据”的文件描述符注册到fd_set型变量,并传递其地址值
// exceptset 表示将所有关注“是否发生异常”的文件描述符注册到fd_set型变量,并传递其地址值
// timeout 表示调用 select() 函数后,为防止陷入无限阻塞的状态,传递超时信息
select() 函数只有在监视的文件描述符发生变化时才返回,如果未发生变化就会进入阻塞状态;通过指定超时事件可以防止无限阻塞的情况,即使文件描述符中未发生变化,当超过了指定事件,就会从函数中返回,返回值为 0;
当调用 select() 函数时,除了发生变化的文件描述符之外,所有原来值为 1 的位均会变为 0,因此可知值仍为 1 的文件描述符发生了变化;
// gcc select.c -o select
// ./select
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>#define BUF_SIZE 30int main(int argc, char* argv[]){fd_set reads, temps;int result, str_len;char buf[BUF_SIZE];struct timeval timeout;FD_ZERO(&reads); // 初始化fd_set变量FD_SET(0, &reads); // 将文件描述符 0 对应的位设置为1,表示监视标准输入(文件描述符0对应标准输入stdin)while(1){temps = reads; // 记录初始值,新循环时重新初始化为初始值timeout.tv_sec = 5; // 超时时间设置为 5stimeout.tv_usec = 0;result = select(1, &temps, 0, 0, &timeout); // 5s内监视是否有标准输入时间发生if(result == -1){puts("select() error!");break;}else if(result == 0){ // 返回值为0表示超时puts("Time-out!");}else{if(FD_ISSET(0, &temps)){ // 验证是否是标准输入发生了变化,打印标准输入的内容str_len = read(0, buf, BUF_SIZE);buf[str_len] = 0;printf("message from console: %s", buf);}}}return 0;
}
3--基于I/O复用的回声服务器端
// gcc echo_selectserv.c -o echo_selectserv
// ./echo_selectserv 9190
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>#define BUF_SIZE 100void error_handling(char *buf){fputs(buf, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char* argv[]){int serv_sock, clnt_sock;struct sockaddr_in serv_adr, clnt_adr;struct timeval timeout;fd_set reads, cpy_reads;socklen_t adr_sz;int fd_max, str_len, fd_num, i;char buf[BUF_SIZE];if(argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}serv_sock = socket(PF_INET, SOCK_STREAM, 0);memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr)) == -1){error_handling("bind() error"); } if(listen(serv_sock, 5) == -1){error_handling("listen() error");}FD_ZERO(&reads); // 初始化fd_set变量FD_SET(serv_sock, &reads); // 监视 serv_sockfd_max = serv_sock;while(1){cpy_reads = reads; // 记录初始值timeout.tv_sec = 5; // 设置超时时间timeout.tv_usec = 5000;if((fd_num = select(fd_max+1, &cpy_reads, 0, 0, &timeout)) == -1){break;}if(fd_num = 0) continue; // 判断是否是超时// 真的有事件发生,执行以下代码for(i = 0; i < fd_max + 1; i++){if(FD_ISSET(i, &cpy_reads)){ // 查找发生状态变化的文件描述符if(i == serv_sock){ // 服务器端socket有变化,执行受理连接请求adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);FD_SET(clnt_sock, &reads); // 将客户端socket注册到fd_set变量中if(fd_max < clnt_sock){fd_max = clnt_sock;}printf("connected client: %d \n", clnt_sock);}else{ // 不是服务器端socket发生变化,表明有要接收的数据str_len = read(i, buf, BUF_SIZE);if(str_len == 0){ // 接收的是 EOF,表明要断开连接FD_CLR(i, &reads);close(i);printf("closed client: %d \n", i);}else{ // 接收的是真实数据write(i, buf, str_len); // 将接收到的数据返回客户端,实现回声功能}}}}}close(serv_sock);return 0;
}
4--send()和recv()函数的常用可选项
#include <sys/socket.h>
ssize_t send(int sockfd, const void* buf, size_t nbytes, int flags);
ssize_t recv(int sockfd, void* buf, size_t nbytes, int flags);
// flags 表示可选项信息
通常情况下,我们会将 send() 和 recv() 的可选项参数设置为 0,但其实际拥有以下可选项:
① MSG_OOB 表示用于传输带外数据(send、recv)
② MSG_PEEK 表示验证输入缓冲中是否存在接收的数据(recv)
③ MSG_DONTROUTE 表示数据传输过程中不参照路由表,在本地网络中寻找目的地(send)
④ MSG_DONTWAIT 表示调用 I/O 函数时不阻塞,用于使用非阻塞 I/O(send、recv)
⑤ MSG_WAITALL 表示防止函数返回,直到接收全部请求的字节数(recv)
MSG_OOB 用于发送带外数据的紧急消息,操作系统收到紧急消息时,将产生 SIGURG 信号,并调用注册的信号处理函数;
// gcc oob_recv.c -o oob_recv
// ./oob_recv 9190#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>#define BUF_SIZE 30
int acpt_sock;
int recv_sock;void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}void urg_handler(int signo){int str_len;char buf[BUF_SIZE];str_len = recv(recv_sock, buf, sizeof(buf) - 1, MSG_OOB);buf[str_len] = 0;printf("Urgent message: %s \n", buf);
}int main(int argc, char* argv[]){struct sockaddr_in recv_adr, serv_adr;int str_len, state;socklen_t serv_adr_sz;struct sigaction act;char buf[BUF_SIZE];if(argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}act.sa_handler = urg_handler; //设置信号的处理函数sigemptyset(&act.sa_mask);act.sa_flags = 0;acpt_sock = socket(PF_INET, SOCK_STREAM, 0);memset(&recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family = AF_INET;recv_adr.sin_addr.s_addr = htonl(INADDR_ANY);recv_adr.sin_port = htons(atoi(argv[1]));if(bind(acpt_sock, (struct sockaddr*) &recv_adr, sizeof(recv_adr)) == -1){error_handling("bind() error"); }listen(acpt_sock, 5); serv_adr_sz = sizeof(serv_adr);recv_sock = accept(acpt_sock, (struct sockaddr*)&serv_adr, &serv_adr_sz);// getpid() 返回进程ID// recv_sock发生SIGUGR信号,需要有确定的处理进程(假设创建了多个进程)来调用信号处理函数// fcntl() 将 getpid() 返回的进程作为 SIGUGR 信号的处理进程fcntl(recv_sock, F_SETOWN, getpid());state = sigaction(SIGURG, &act, 0); // 发生SIGURG信号时,调用urg_handler()函数while((str_len = recv(recv_sock, buf, sizeof(buf)-1, 0)) != 0){if(str_len == -1){continue;}buf[str_len] = 0;puts(buf);}close(recv_sock);close(acpt_sock);return 0;
}
// gcc oob_send.c -o oob_send
// ./oob_send 127.0.0.1 9190#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define BUF_SIZE 30void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[]){int sock;struct sockaddr_in recv_adr;if(argc != 3){printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock = socket(PF_INET, SOCK_STREAM, 0);memset(&recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family = AF_INET;recv_adr.sin_addr.s_addr = inet_addr(argv[1]);recv_adr.sin_port = htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&recv_adr, sizeof(recv_adr)) == -1){error_handling("connect() error!");}write(sock, "123", strlen("123")); send(sock, "4", strlen("4"), MSG_OOB); // 紧急传输数据write(sock, "567", strlen("567"));send(sock, "890", strlen("890"), MSG_OOB); // 紧急传输数据close(sock);return 0;
}
同时设置 MSG_PEEK 选项和 MSG_DONTWAIT 选项,可以验证输入缓冲中是否存在接收的数据,同时由于设置了 MSG_PEEK 选项,则调用 recv 函数时,即使读取了输入缓冲的数据也不会删除输入缓冲的数据(也就是说下一次读取时,还可以读到上一次读取的数据);
// gcc peek_recv.c -o peek_recv
// ./peek_recv 9190#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define BUF_SIZE 30void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char* argv[]){int acpt_sock, recv_sock;struct sockaddr_in acpt_adr, recv_adr;int str_len, state;socklen_t recv_adr_sz;char buf[BUF_SIZE];if(argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}acpt_sock = socket(PF_INET, SOCK_STREAM, 0);memset(&acpt_adr, 0, sizeof(acpt_adr));acpt_adr.sin_family = AF_INET;acpt_adr.sin_addr.s_addr = htonl(INADDR_ANY);acpt_adr.sin_port = htons(atoi(argv[1]));if(bind(acpt_sock, (struct sockaddr*) &acpt_adr, sizeof(acpt_adr)) == -1){error_handling("bind() error"); }listen(acpt_sock, 5); recv_adr_sz = sizeof(recv_adr);recv_sock = accept(acpt_sock, (struct sockaddr*)&recv_adr, &recv_adr_sz);while(1){// 设置 MSG_PEEK|MSG_DONTWAIT 选项,即使不存在待读取的数据,也不会进入阻塞状态// 假设存在待读取的数据,则读取且不删除输入缓冲的数据,因此下次仍可以读取str_len = recv(recv_sock, buf, sizeof(buf) - 1, MSG_PEEK|MSG_DONTWAIT);if(str_len > 0){break;}}buf[str_len] = 0;printf("Buffering %d bytes: %s \n", str_len, buf);// 读取上一次留在输入缓冲的数据str_len = recv(recv_sock, buf, sizeof(buf) - 1, 0);buf[str_len] = 0;printf("Read again: %s \n", buf);close(acpt_sock);close(recv_sock);return 0;
}
// gcc peek_send.c -o peek_send
// ./peek_send 127.0.0.1 9190#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[]){int sock;struct sockaddr_in recv_adr;if(argc != 3){printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock = socket(PF_INET, SOCK_STREAM, 0);memset(&recv_adr, 0, sizeof(recv_adr));recv_adr.sin_family = AF_INET;recv_adr.sin_addr.s_addr = inet_addr(argv[1]);recv_adr.sin_port = htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&recv_adr, sizeof(recv_adr)) == -1){error_handling("connect() error!");}write(sock, "123", strlen("123"));close(sock);return 0;
}
5--readv()和writev()函数
readv() 和 writev() 函数对数据进行整合后,再进行读取和发送;即通过 writev() 函数可以将分散保存在多个缓冲中的数据一并发送,通过 readv() 函数可以由多个缓冲分别接收;
#include <sys/uio.h>ssize_t writev(int filedes, const struct iovec* iov, int iovcnt);
// 成功时返回发送的字节数,失败时返回 -1
// filedes 表示文件描述符
// iov 表示 iovec 结构体数组的地址值
// iovcnt 表示向第二个参数传递的数组长度ssize_t readv(int filedes, const struct iovec* iov, int iovcnt);
// 成功时返回接收的字节数,失败时返回 -1// iovec结构体
struct iovec{void* iov_base; // 缓冲地址size_t iov_len; // 缓冲大小
}
代码实例:
// gcc writev.c -o write
// ./write#include <stdio.h>
#include <sys/uio.h>int main(int argc, char* argv[]){struct iovec vec[2];char buf1[] = "ABCDEFG";char buf2[] = "1234567";int str_len;vec[0].iov_base = buf1;vec[0].iov_len = 3; vec[1].iov_base = buf2;vec[1].iov_len = 4;str_len = writev(1, vec, 2); // 向文件描述符1写数据,即向标准输出写数据puts("");printf("Write bytes: %d \n", str_len);return 0;
}
// gcc readv.c -o readv
// ./readv#include <stdio.h>
#include <sys/uio.h>#define BUF_SIZE 100int main(int argc, char *argv[]){struct iovec vec[2];char buf1[BUF_SIZE] = {0,};char buf2[BUF_SIZE] = {0,};int str_len;vec[0].iov_base = buf1;vec[0].iov_len = 5; // 设置最多保存5个字节vec[1].iov_base = buf2;vec[1].iov_len = BUF_SIZE;str_len = readv(0, vec, 2); // 向标准输入(文件描述符0)读数据printf("Read bytes: %d \n", str_len);printf("First message: %s \n", buf1);printf("Second message: %s \n", buf2);return 0;
}
相关文章:

《TCP/IP网络编程》阅读笔记--I/O复用
目录 1--基于I/O复用的服务器 2--select()函数 3--基于I/O复用的回声服务器端 4--send()和recv()函数的常用可选项 5--readv()和writev()函数 1--基于I/O复用的服务器 多进程服务器端具有以下缺点:当有多个客户端发起连接请求时,就会创建多个进程来…...
[C#] 允许当前应用程序通过防火墙
通常在一台装有防火墙的电脑上运行程序的场合,往往会弹出对话框提示:是否允许执行该应用程序。 我们在开发软件的时候,可以事先在软件里面设置当前软件为防火墙允许通过的软件。这样,用户在使用时就可以避开前面提到的弹框了。 在…...

帆软FineReport决策报表Tab实现方案
最近有个需求是要做首页展示,为了减少前端工作量,利用采购的帆软FineReport来实现,记录过程,方便备查。 需求 做个Tab页,实现多个页切换。 方案一、利用帆软自带切换 帆软自带的有Tab控件,可实现切换&a…...
只打印文名
CMakeLists.txt set(CMAKE_C_FLAGS "-O0 -ggdb -D__NOTDIR_FILE__$(notdir $<)") // set(CMAKE_C_FLAGS "-O0 -ggdb -D__NOTDIR_FILE__$(notdir $<) -D__FILENAME__$(subst $(dir $<),,$<)")C文件 #include <stdio.h>#ifdef __NOTDIR_…...

【经典小练习】JavaSE—拷贝文件夹
🎊专栏【Java小练习】 🍔喜欢的诗句:天行健,君子以自强不息。 🎆音乐分享【如愿】 🎄欢迎并且感谢大家指出小吉的问题🥰 文章目录 🎄效果🌺代码🛸讲解&#x…...

FPGA-结合协议时序实现UART收发器(六):仿真模块SIM_uart_drive_TB
FPGA-结合协议时序实现UART收发器(六):仿真模块SIM_uart_drive_TB 仿真模块SIM_uart_drive_TB,仿真实现。 vivado联合modelsim进行仿真。 文章目录 FPGA-结合协议时序实现UART收发器(六):仿真模…...

Spring Boot集成EasyExcel实现数据导出
在本文中,我们将探讨如何使用Spring Boot集成EasyExcel库来实现数据导出功能。我们将学习如何通过EasyExcel库生成Excel文件,并实现一些高级功能,如支持列下拉和自定义单元格样式,自适应列宽、行高,动态表头 ÿ…...
EasyExcel3.0读(日期、数字或者自定义格式转换)
EasyExcel 3.0读(日期、数字或者自定义格式转换) 依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.2.1</version> </dependency>对象 package com.xiaobu.entity.vo;import …...

浅谈C++|STL之vector篇
一.vector的基本概念 vector是C标准库中的一种动态数组容器,提供了动态大小的数组功能,能够在运行时根据需要自动扩展和收缩。vector以连续的内存块存储元素,可以快速访问和修改任意位置的元素。 以下是vector的基本概念和特点: 动…...
微信、支付宝修改步数【小米运动】
简介 小米运动是一款流行的健身应用,可以记录用户的步数和运动数据。然而,有些用户希望能够修改步数,以达到一些特定的目的。本文将介绍一个Python脚本,可以帮助用户实现修改小米运动步数的功能。 正文 脚本介绍: 本脚本是一个Python脚本,用于修改小米运动步数。通过模…...
stu02-初识HTML
1.HTML概述 (1)HTML是Hyper Text Mark-up Language的首字母缩写。 (2)HTML是一种超文本标记语言。 (3) 超文本:指除了文字外,页面内还可以包含图片、链接、甚至音乐、视频等非文字元…...

软件测试7大误区
随着软件测试对提高软件质量重要性的不断提高,软件测试也不断受到重视。但是,国内软件测试过程的不规范,重视开发和轻视测试的现象依旧存在。因此,对于软件测试的重要性、测试方法和测试过程等方面都存在很多不恰当的认识…...

【深度学习】 Python 和 NumPy 系列教程(十二):NumPy详解:4、数组广播;5、排序操作
目录 一、前言 二、实验环境 三、NumPy 0、多维数组对象(ndarray) 多维数组的属性 1、创建数组 2、数组操作 3、数组数学 4、数组广播 5、排序操作 1. np.sort() 函数 2. np.argsort() 函数 3. ndarray.sort() 方法 4. 按列或行排序 5. n…...

CSS宽度问题
一、魔法 为 DOM 设置宽度有哪些方式呢?最常用的是配置width属性,width属性在配置时,也有多种方式: widthmin-widthmax-width 通常当配置了 width 时,不会再配置min-width max-width,如果将这三者混合使…...

浅谈C++|STL之string篇
一.string的基本概念 本质 string是C风格的字符串,而string本质是一个字符串 string和char * 区别 char * 是一个指针string是一个类,类内部封装了char *,管理这个字符串,是一个char * 型容器。 特点 string类内部封装了很多成…...

Kubernetes Dashboard安装部署
Kubernetes Dashboard安装部署 1. 下载Dashboard 部署文件2. 修改yaml配置文件3. 应用安装,查看pod和svc4. 创建dashboard服务账户5. 创建admin-user用户的登录密钥6. 登录6.1 使用token登录(1) 短期token(2) token长期有效 6.2 使用 Kubeconfig 文件登录 7.安装met…...
在Qt的点云显示窗口中添加坐标轴C++
通过摸索整理了三个方法: 一、方法1://不推荐,但可以参考 1、通过pcl的compute3DCentroid()方法计算点云的中心点坐标; 函数原型如下: compute3DCentroid (const pcl::PointCloud<PointT> &cloud, Eigen…...
[密码学入门]凯撒密码(Caesar Cipher)
密码体质五元组:P,C,K,E,D P,plaintext,明文空间 C,ciphertext,密文空间 K,key,密钥空间 E,encrypt,加密算法 D,decrypt,解密算法 单表代换…...

uboot 顶层Makefile-make xxx_deconfig过程说明三
一. uboot 的 make xxx_deconfig配置 本文接上一篇文章的内容。地址如下:uboot 顶层Makefile-make xxx_deconfig过程说明二_凌肖战的博客-CSDN博客 本文继续来学习 uboot 源码在执行 make xxx_deconfig 这个配置过程中,顶层 Makefile有关的执行思路。 …...
c++中的多线程通信
信息传递 #include <iostream> #include <thread> #include <chrono> #include <mutex> #include <condition_variable> #include <queue> // 用于存储和同步数据的结构 struct Data {std::queue<std::string> messag…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

Docker 运行 Kafka 带 SASL 认证教程
Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明:server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...

macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...