前言

之前的示例中,基于Linux的使用read&write函数完成数据I/O,基于Windows的则使用send&recv函数。这次的Linux示例也将使用send& recv函数,并讲解其与read&write函数相比的优点。还将介绍几种其他的I/O函数。

一、send & recv 函数

1.Linux 中的 send & recv

#include <sys/socket.h>
ssize_t send(int sockfd, const void * buf, size_t nbytes, int flags);
// 成功时返回发送的字节数,失败时返回-1。
// sockfd:表示与数据传输对象的连接的套接字文件描述符。
// buf:保存待传输数据的缓冲地址值。
// nbytes:待传输的字节数。
// flags:传输数据时指定的可选项信息。
#include <sys/socket.h>
ssize_t recv(int sockfd, void * buf, size_t nbytes, int flags);
// 成功时返回接收的字节数(收到EOF时返回0),失败时返回-1。
// sockfd:表示数据接收对象的连接的套接字文件描述符。
// buf:保存接收数据的缓冲地址值。
// nbytes:可接收的最大字节数。
// flags:接收数据时指定的可选项信息。

send函数和recv函数的最后一个参数是收发数据时的可选项。该可选项可利用位或(bitOR)
运算(运算符)同时传递多个信息。通过表查看选项的种类及含义。
可选项(Option) 含 义 send recv
MSG_OOB 用于传输带外数据(Out-of-band data) * *
MSG_PEEK 验证输人缓冲中是否存在接收的数据 *
MSG_DONTROUTE 数据传输过程中不参照路由(Routing)表,在本地(Local)网络中寻找目的地 *
MSG_DONTWAIT 调用I/O函数时不阻塞,用于使用非阻塞(Non-blocking)I/O * *
MSG_WAITALL 防止函数返回,直到接收全部请求的字节数 *
另外,不同操作系统对上述可选项的支持也不同。因此,为了使用不同可选项,各位需要对实际开发中采用的操作系统有一定了解。下面选取表中的一部分(主要是不受操作系统差异影响的)进行详细讲解。

2.MSG_OOB:发送紧急消息

MSG_OOB可选项用于发送“带外数据”紧急消息。假设医院里有很多病人在等待看病,此时若有急诊患者该怎么办?
“当然应该优先处理。”
如果急诊患者较多,需要得到等待看病的普通病人的谅解。正因如此,医院一般会设立单独的急诊室。需紧急处理时,应采用不同的处理方法和通道。MSG_OOB可选项就用于创建特殊发送方法和通道以发送紧急消息。下列示例将通过MSG_OOB可选项收发数据。使用MSG_OOB时

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define BUF_SIZE 30
void error_handling(char *message);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;
}void error_handling(char *message)
{fputs(message,stderr);fputc('\n', stderr);exit(1);
}

第29~32行:传输数据。第30和第32行紧急传输数据。正常顺序应该是123、4、567、890,但紧急传输了4和890,由此可知接收顺序也将改变。

从上述示例可以看出,紧急消息的传输比即将介绍的接收过程要简单,只需在调用send函数时指定MSG_OOB可选项。接收紧急消息的过程要相对复杂一些。

#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
void error_handling(char*message);
void urg_handler(int signo);int acpt_sock;
int recv_sock;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);fcntl(recv_sock, F_SETOWN, getpid());state=sigaction(SIGURG, &act, 0);while((str_len=recv(recv_sock, buf, sizeof(buf), 0))!= 0){if(str_len==-1)continue;buf[str_len]=0;puts(buf);}close(recv_sock);close(acpt_sock);return 0;
}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);
}void error_handling(char *message){fputs(message, stderr);fputc('\n', stderr);exit(1);
}

第29、47行:该示例中需要重点观察SIGURG信号相关部分。收到MSG_OOB紧急消息时,操作系统将产生SIGURG信号,并调用注册的信号处理函数。另外需要注意的是,第61行的信号处理函数内部调用了接收紧急消息的recv函数。第46行:调用fcntl函数,关于此函数将单独说明。

上面的v示例中插人了未曾讲解的函数调用语句,关于此函数只讲解必要部分,过多的解释将脱离本章主题(之后将再次说明)。
fcntl(recv_sock, F_SETOwN, getpid(O));
fcntl函数用于控制文件描述符,上述调用语句的含义:
“将文件描述符recv_sock指向的套接字拥有者(F_SETOWN)改为把getpid函数返回值用作ID的进程。”
大家或许感觉“套接字拥有者”的概念有些生疏。操作系统实际创建并管理套接字,所以从严格意义上说,“套接字拥有者”是操作系统。只是此处所谓的“拥有者”是指负责套接字所有事务的主体。上面的描述可简要概括成这样:
“文件描述符recv_sock指向的套接字引发的SIGURG信号处理进程变为将getpid函数返回值用作ID的进程。”

当然,上述描述中的“处理SIGURG信号”指的是“调用SIGURG信号处理函数”。但之前讲过,多个进程可以共同拥有1个套接字的文件描述符。例如,通过调用fork函数创建子进程并同时复制文件描述符。此时如果发生SIGURG信号,应该调用哪个进程的信号处理函数呢?可以肯定的是,不会调用所有进程的信号处理函数(想想就知道这会引发更多问题)。因此,处理SIGURG
信号时必须指定处理信号的进程,而getpid函数返回调用此函数的进程ID。上述调用语句指定当前进程为处理SIGURG信号的主体。该程序中只创建了1个进程,因此,理应由该进程处理SIGURG信号。接下来先给出运行结果,再讨论剩下的问题。
在这里插入图片描述
输出结果可能出乎大家预料,尤其是如下事实令人极为失望:
“通过MSG_OOB可选项传递数据时只返回1个字节?而且也不是很快啊!”
的确!令人遗憾的是,通过MSG_OOB可选项传递数据时不会加快数据传输速度,而且通过信号处理函数urg_handler读取数据时也只能读1个字节。剩余数据只能通过未设置MSG_OOB可选项的普通输人函数读取。这是因为TCP不存在真正意义上的“带外数据”。实际上,MSG_OOB中的OOB是指Out-of-band,而“带外数据”的含义是:
“通过完全不同的通信路径传输的数据。”
即真正意义上的Out-of-band需要通过单独的通信路径高速传输数据,但TCP不另外提供,只利用TCP的紧急模式(Urgent mode)进行传输。

3.紧急模式工作原理

先给出结论,再补充说明紧急模式。MSG_OOB可选项可以带来如下效果:
– “嗨!这里有数据需要紧急处理,别磨蹭啦!” –
MSG_OOB的真正的意义在于督促数据接收对象尽快处理数据。这是紧急模式的全部内容,而且TCP“保持传输顺序”的传输特性依然成立。
“那怎能称为紧急消息呢!”
这确实是紧急消息!因为发送消息者是在催促数据处理的情况下传输数据的。急诊患者的及时救治需要如下两个条件:
■ 迅速入院
■ 医院急救
无法快速把病人送到医院,并不意味着不需要医院进行急救。TCP的紧急消息无法保证及时人院,但可以要求急救。当然,急救措施应由程序员完成。之前的示例oob_recv.c的运行过程中也传递了紧急消息,这可以通过事件处理函数确认。这就是MSG_OOB模式数据传输的实际意义。
在这里插入图片描述
send(sock, “890”, strlen(“890”), MSG_OOB);
如果将缓冲最左端的位置视作偏移量为0,字符0保存于偏移量为2的位置。另外,字符0右侧偏移量为3的位置存有紧急指针(UrgentPointer)。紧急指针指向紧急消息的下一个位置(偏移量加1),同时向对方主机传递如下信息:
– “紧急指针指向的偏移量为3之前的部分就是紧急消息!” –
也就是说,实际只用1个字节表示紧急消息信息。
在这里插入图片描述
TCP数据包实际包含更多信息,但图中只标注了与我们的主题相关的内容。TCP头中含有如下两种信息:
■ URG=1:载有紧急消息的数据包
■ URG指针:紧急指针位于偏移量为3的位置
指定MSG_OOB选项的数据包本身就是紧急数据包,并通过紧急指针表示紧急消息所在位置。但通过图无法得知以下事实:
“紧急消息是字符串890,还是90?如若不是,是否为单个字符0?”
但这并不重要。如前所述,除紧急指针的前面1个字节外,数据接收方将通过调用常用输人函数读取剩余部分。换言之,紧急消息的意义在于督促消息处理,而非紧急传输形式受限的消息。

4.检查输入缓冲

同时设置MSG_PEEK选项和MSG_DONTWAIT选项,以验证输入缓冲中是否存在接收的数据。设置MSG_PEEK选项并调用recv函数时,即使读取了输人缓冲的数据也不会删除。因此,该选项通常与MSG_DONTWAIT合作,用于调用以非阻塞方式验证待读数据存在与否的函数。下面我们通过示例了解二者含义。

#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);int main(int argc, char *argv[])
{int sock;struct sockaddr_in send_adr;if(argc!=3) {printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock=socket(PF_INET,SOCK_STREAM,0);memset(&send_adr,0, sizeof(send_adr));send_adr.sin_family=AF_INET;send_adr.sin_addr.s_addr=inet_addr(argv[1]);send_adr.sin_port=htons(atoi(argv[2]));if(connect(sock, (struct sockaddr*)&send_adr, sizeof(send_adr))==-1)error_handling("connect() error!");write(sock,"123456789", strlen("123456789"));close(sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

上述示例在第24行发起连接请求,第27行发送字符串123。下个例子给出了使用MSG_PEEK和MSG_DONTWAIT选项的结果。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>#define BUF_SIZE 30
void error_handling(char *message);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){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;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n',stderr);exit(1);
}

第38行:调用recv函数的同时传递MSG_PEEK可选项,这是为了保证即使不存在待读取数据也不会进入阻塞状态。
第46行:再次调用recv函数。这次并未设置任何可选项,因此,本次读取的数据将从输入缓冲中删除。
在这里插入图片描述
通过运行结果可以验证,仅发送1次的数据被读取了2次,因为第一次调用recv函数时设置了MSG_PEEK可选项。以上就是MSG_PEEK可选项的功能啦!

二、readv & writev 函数

这次介绍的readv&writev函数有助于提高数据通信效率。先介绍这些函数的使用方法,再讨论其合理的应用场景哦。

1.使用readv & writev 函数

readv&writev函数的功能可概括如下:
“对数据进行整合传输及发送的函数。”
也就是说,通过writev函数可以将分散保存在多个缓冲中的数据一并发送,通过readv函数可以由多个缓冲分别接收。因此,适当使用这2个函数可以减少I/O函数的调用次数。下面先介绍writev函数。

#include <sys/uio.h>
ssize_t writev(int filedes, const struct iovec * iov, int iovcnt);
// 成功时返回发送的字节数,失败时返回-1。
// filedes:表示数据传输对象的套接字文件描述符。但该函数并不只限于套接字,因此,可以像read函数一样向其传递文件或标准输出描述符。
// iov:iovec结构体数组的地址值,结构体iovec中包含待发送数据的位置和大小信息。
// iovcnt:向第二个参数传递的数组长度。

可以看到,结构体iovec由保存待发送数据的缓冲(char型数组)地址值和实际发送的数据长度信息构成。
在这里插入图片描述
图13-4中writev的第一个参数1是文件描述符,因此向控制台输出数据,ptr是存有待发送数据信息的iovec数组指针。第三个参数为2,因此,从ptr指向的地址开始,共浏览2个iovec结构体变量,发送这些指针指向的缓冲数据。接下来仔细观察图中的iovec结构体数组。ptr[0](数组第一个元素)的iov_base指向以A开头的字符串,同时iov_len为3,故发送ABC。而ptr[1](数组的第二个元素)的iov_base指向数字1,同时iov_len为4,故发送1234。
相信大家已掌握writev函数的使用方法和特性,接下来栗子来了:

#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);puts("");printf("Write bytes: %d \n", str_len);return 0;
}

第11、12行:写入第一个传输数据的保存位置和大小。
第13、14行:写入第二个传输数据的保存位置和大小。
第16行:writev函数的第一个参数为1,故向控制台输出数据。
在这里插入图片描述
下面介绍readv函数,它与writev函数正好相反:

#include <sys/uio.h>
ssize_t readv(int filedes, const struct iovec * iov, int iovcnt);
// 成功时返回接收的字节数,失败时返回-1。
// filedes:传递接收数据的文件(或套接字)描述符。
// iov:包含数据保存位置和大小信息的iovec结构体数组的地址值。
// iovcnt:第二个参数中数组的长度。

我们已经学习了writev函数,因此直接通过例子给出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;vec[1].iov_base=buf2;vec[1].iov_len=BUF_SIZE;str_len=readv(0, vec, 2);printf("Read bytes: %d \n",str_len);printf("First message: %s \n",buf1);printf("Second message: %s \n",buf2);return 0;
}

第12、13行:设置第一个数据的保存位置和大小。接收数据的大小已指定为5,因此,无论buf1的大小是多少,最多仅能保存5个字节。
第14、15行:vec[0]中注册的缓冲中保存5个字节,剩余数据将保存到vec[1]中注册的缓冲。结构体iovec的成员iov_len中应写入接收的最大字节数。
第17行:「eadv函数的第一个参数为0,因此从标准输入接收数据。
在这里插入图片描述

2.合理使用 readv&writev函数

哪种情况适合使用readv和writev函数?实际上,能使用该函数的所有情况都适用。例如,需要传输的数据分别位于不同缓冲(数组)时,需要多次调用write函数。此时可以通过1次writev函数调用替代操作,当然会提高效率。同样,需要将输人缓冲中的数据读人不同位置时,可以不必多次调用read函数,而是利用1次readv函数就能大大提高效率。
即使仅从C语言角度看,减少函数调用次数也能相应提高性能。但其更大的意义在于减少数据包个数。假设为了提高效率而在服务器端明确阻止了Nagle算法。其实writev函数在不采用Nagle算法时更有价值。
这个示例中待发送的数据分别存在3个不同的地方,此时如果使用write函数则需要3次函数调用。但若为提高速度而关闭了Nagle算法,则极有可能通过3个数据包传递数据。反之,若使用writev函数将所有数据一次性写人输出缓冲,则很有可能仅通过1个数据包传输数据。所以writev函数和readv函数非常有用。
再考虑一种情况:将不同位置的数据按照发送顺序移动(复制)到1个大数组,并通过1次write函数调用进行传输。这种方式是否与调用writev函数的效果相同?当然!但使用writev函数更为便利。因此,如果遇到writev函数和readv函数的适用情况,希望大家不要错过机会。


总结

`这些函数大家理解一下,应该没有那么难哦!

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

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

相关文章

设计模式五:桥模式(Bridge Pattern)

桥模式是一种结构型设计模式&#xff0c;它将抽象部分与其实现部分分离&#xff0c;使它们可以独立变化。这种模式通过提供桥梁结构将抽象和实现解耦。桥模式的结构桥模式包含以下主要角色&#xff1a;Abstraction&#xff08;抽象类&#xff09;&#xff1a;定义抽象接口&…

深入理解设计模式之模板模式:优雅地定义算法骨架

在软件开发中&#xff0c;我们经常会遇到这样的情况&#xff1a;多个类执行相似的操作流程&#xff0c;但每个类在流程的某些步骤上有自己特定的实现。如果为每个类都完整地编写整个流程&#xff0c;会导致大量重复代码&#xff0c;且难以维护。这时候&#xff0c;模板模式&…

基于单片机宠物喂食器/智能宠物窝/智能饲养

传送门 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目速选一览表 &#x1f449;&#x1f449;&#x1f449;&#x1f449;其他作品题目功能速览 概述 深夜加班时&#xff0c;你是否担心家中宠物饿肚子&#xff1f;出差旅途中&#xff0c;是否焦虑宠…

静态补丁脚本 - 修改 libtolua.so

直接改arm64的so&#xff0c; 使用python脚本。#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 静态补丁脚本 - 修改 libtolua.so 主要功能&#xff1a; 1. 修改 luaL_loadbuffer 函数&#xff0c;将跳转目标从 luaL_loadbufferx 改为 luaL_loadfilex 2. …

2-大语言模型—理论基础:详解Transformer架构的实现(2)

目录 1-大语言模型—理论基础&#xff1a;详解Transformer架构的实现(1)-CSDN博客https://blog.csdn.net/wh1236666/article/details/149443139?spm1001.2014.3001.5502 2.3、残差连接和层归一化 2.3.1、什么是层归一化&#xff1f; 2.3.2、层归一化的核心特点&#xff08…

SmartX 用户建云实践|富士康:基于榫卯企业云平台构建分布式云,支撑全球多地工厂重要产线

作为全球最大的电子科技智造服务商&#xff0c;富士康集团在全球范围内构建生产制造网络。为实现多厂区统一管理与降本增效&#xff0c;在逐步替代 VMware 虚拟化架构的过程中&#xff0c;富士康对比了自研 OpenStack Ceph 平台和 SmartX 超融合方案&#xff0c;最终选择基于 …

ADC选型设计

1、最大摆伏FSR&#xff1a; 0 ~ 4.096V&#xff0c;一般Vref要等于FSR 2、最大频率&#xff1a;根据奈奎斯特采样定理大于2倍的信号频率才够还原信号&#xff0c;所以选择20/50倍更好&#xff0c; 3、最小精度&#xff0c;对于一给定模拟输入&#xff0c;实际数字输出与理论预…

基于深度学习的火灾智能检测系统设计与实现

在各类安全事故中&#xff0c;火灾因其突发性强、破坏力大&#xff0c;一直是威胁人们生命财产安全的重大隐患。传统的火灾检测方式多依赖烟雾传感器、温度传感器等&#xff0c;存在响应滞后、易受环境干扰等问题。随着深度学习技术的飞速发展&#xff0c;基于计算机视觉的火灾…

HIVE实战处理(二十四)留存用户数

留存概念: 次X日活跃留存&#xff0c;次X日新增留存&#xff0c;也就是看今天的新增或活跃用户在后续几天的留存情况一、留存表的生成逻辑 因为用户活跃日期和留存的日期无法对齐所以搞了2级分区&#xff08;dt,static_day&#xff09; 1&#xff09;首先获得计算日D、根据要出…

W3C XHTML 活动:标准化的未来与交互式体验

W3C XHTML 活动:标准化的未来与交互式体验 概述 W3C(World Wide Web Consortium)是全球领先的互联网技术标准制定组织。XHTML,作为W3C推荐的标准之一,是一种基于XML的标记语言,旨在提供一个更加结构化、兼容性和可扩展性更高的网页内容表示方式。本文将围绕W3C的XHTML活…

Java-数构链表

1.链表 1.1链表的概念和结构 链表是一种物理存储结构上非连续存储结构&#xff0c;数据元素的逻辑顺序是通过链表中引用链接次序实现的。 这里大多讨论无头单向非循环链表。这种结构&#xff0c;结构简单&#xff0c;一般与其他数据结构结合&#xff0c;作为其他数据结构的子…

Windows系统软件游戏丢失找不到mgmtapi.dll修复解决方法

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

初识C++——开启新旅途

从今天开始主包也是掉入C这个深坑&#xff0c;上完课也是跟没上一样&#xff0c;所以写好博客复习还是很重要的&#xff0c;话不多说&#xff0c;进入正题~~1、命名空间(1)namespace的价值与作用在C/C中&#xff0c;变量、函数和后面要学到的类都是大量存在的&#xff0c;这些变…

vue2 面试题及详细答案150道(141 - 150)

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

第十三章 Go包管理

文章目录使用logurs处理程序日志logrus 常用配置使用viper处理程序配置使用logurs处理程序日志 下载包&#xff0c;在终端执行命令 go get github.com/sirupsen/logrus官方示例 package mainimport (log "github.com/sirupsen/logrus" )func main() {log.WithFiel…

EP01:【Python 第一弹】基础入门知识

一、基础入门知识 1.1 代码规范 1.1.1 语句分隔符 ; 换行 1.1.2 格式化 对 Windows 和 Linux 操作系统&#xff0c;快捷键是Ctrl Alt L对 macOS 操作系统&#xff0c;快捷键是Cmd Option L 1.1.3 注释 单行注释 # 这是一行注释多行注释 """ 这 是 …

实用的文件和文件夹批量重命名工具

在日常工作中&#xff0c;文件和文件夹的命名管理常常让人头疼。尤其是面对大量文件时&#xff0c;手动重命名不仅耗时&#xff0c;还容易出错。今天&#xff0c;我要给大家推荐一款超级实用的工具——OncePower 文件批量重命名&#xff0c;它不仅能批量重命名文件和文件夹&…

【Git】报错:git config --global http.sslBackend “openssl“

问题解决 报错&#xff1a;git config --global http.sslBackend “openssl”解决方法&#xff1a; git config --global http.sslBackend "openssl"之后再 push 即可正常提交。 &#x1f50d; 原因分析 ​​系统环境不支持 OpenSSL 后端​​ Git 在某些平台&#xf…

Redisson RLocalCachedMap 核心参详解

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

AI辅助编程时代的高效规范开发指南:工具、原则与提效策略

引言&#xff1a;AI辅助编程的时代背景与核心挑战 人工智能在编程领域的应用虽可追溯至20世纪50年代&#xff0c;但近十年实现了革命性突破&#xff0c;推动其从早期的代码补全工具演进为能理解上下文、生成完整函数乃至项目架构的智能系统。关键发展里程碑包括&#xff1a;20…