3.水中看月

前言

这篇文章讲解套接字分配IP地址和端口号。这部分内容也相对有些枯燥,但并不难,而
且是学习后续那些有趣内容必备的基础知识(计算机网络基础)。

一、分配给套接字的IP地址与端口号

IP是InternetProtocol(网络协议)的简写,是为收发网络数据而分配给计算机的值。端口号
并非赋予计算机的值,而是为区分程序中创建的套接字而分配给套接字的序号。下面逐一讲解。

1.网络地址

为使计算机连接到网络并收发数据,必需向其分配IP地址。IP地址分为两类。
■ IPv4(Internet Protocolversion 4)4字节地址族
■ IPv6 (Internet Protocol version 6)16字节地址族
IPv4与IPv6的差别主要是表示IP地址所用的字节数,目前通用的地址族为IPv4。IPv6是为了
应对2010年前后IP地址耗尽的问题而提出的标准,即便如此,现在还是主要使用IPv4,IPv6的普
及将需要更长时间。
IPv4标准的4字节IP地址分为网络地址和主机(指计算机)地址,且分为A、B、C、D、E等
类型。下图展示了IPv4地址族,一般不会使用已被预约了的E类地址。
在这里插入图片描述
网络地址(网络ID)是为区分网络而设置的一部分IP地址。假设向WWW.SEMI.COM公司传
输数据,该公司内部构建了局域网,把所有计算机连接起来。因此,首先应向SEMI.COM网络传
输数据,也就是说,并非一开始就浏览所有4字节IP地址,进而找到目标主机;而是仅浏览4字节
IP地址的网络地址,先把数据传到SEMI.COM的网络。SEMI.COM网络(构成网络的路由器)接
收到数据后,浏览传输数据的主机地址(主机ID)并将数据传给目标计算机。图展示了数据
传输过程。
在这里插入图片描述

某主机向203.211.172.103和203.211.217.202传输数据,其中203.211.172和203.211.217为该网
络的网络地址(稍后将给出网络地址的区分方法)。所以,“向相应网络传输数据”实际上是向构
成网络的路由器(Router)或交换机(Switch)传递数据,由接收数据的路由器根据数据中的主
机地址向目标主机传递数据。

2.网络地址分类与主机地址边界

只需通过IP地址的第一个字节即可判断网络地址占用的字节数,因为我们根据IP地址的边界
区分网络地址:
■ A类地址的首字节范围:0~127
■ B类地址的首字节范围:128~191
■ C类地址的首字节范围:192~223
还有如下这种表述方式:
■ A类地址的首位以0开始
■ B类地址的前2位以10开始
■ C类地址的前3位以110开始
正因如此,通过套接字收发数据时,数据传到网络后即可轻松找到正确的主机。

2.用于区分套接字的端口号

IP用于区分计算机,只要有IP地址就能向目标主机传输数据,但仅凭这些无法传输给最终的
应用程序。假设大家欣赏视频的同时在网上冲浪,这时至少需要1个接收视频数据的套接字和1
个接收网页信息的套接字。问题在于如何区分二者。也就是,传输到计算机的网络数据是发给播
放器,还是发送给浏览器?让我们更准确地描述问题。假设开发了如下应用程序:
“我开发了收发数据的P2P程序,该程序用块单位分割1个文件,从多台计算机接收
数据。”
假设大家对P2P有一定了解,即便不清楚也无所谓。如上所述,若想接收多台计算机发来的
数据,则需要相应个数的套接字。那如何区分这些套接字呢?
计算机中一般配有NIC(Network Interface Card,网络接口卡)数据传输设备。通过NIC向计
算机内部传输数据时会用到IP。操作系统负责把传递到内部的数据适当分配给套接字,这时就要利用端口号。也就是说,通过NIC接收的数据内有端口号,操作系统正是参考此端口号把数据传输给相应端口的套接字。
端口号就是在同一操作系统内为区分不同套接字而设置的,因此无法将1个端口号分配给不
同套接字。另外,端口号由16位构成,可分配的端口号范围是0-65535。但0-1023是知名端口
(Well-knownPORT),一般分配给特定应用程序,所以应当分配此范围之外的值。另外,虽然端
口号不能重复,但TCP套接字和UDP套接字不会共用端口号,所以允许重复。例如:如果某TCP
套接字使用9190号端口,则其他TCP套接字就无法使用该端口号,但UDP套接字可以使用。
总之,数据传输目标地址同时包含IP地址和端口号,只有这样,数据才会被传输到最终的目
的应用程序(应用程序套接字)。

二、地址信息表示

应用程序中使用的IP地址和端口号以结构体的形式给出了定义。将以IPv4为中心,围绕此结构体讨论目标地址的表示方法。

1.表示IPv4地址的结构体

■ 问题1:“采用哪一种地址族?”
■ 答案1:“基于IPv4的地址族。”

■ 问题2:“IP地址是多少?”
■ 答案2:“211.204.214.76。”

■ 问题3:“端口号是多少?”
■ 答案3:“2048。”

结构体定义为如下形态就能回答上述提问,此结构体将作为地址信息传递给bind函数。

struct sockaddr_in
{SA_famIly_t		sin_family;	// 地址族(Address Family)uint16_t		sin_port;	// 16 位TCP/UDP端口号struct in_addr	sin_addr;	// 32 位 IP 地址char	sin_zero[8];	// 不使用
}

该结构体中提到的另一个结构体in_addr定义如下,它用来存放32位IP地址。

struct in_addr
{In_addr_t	s_addr;	// 32位IPv4 地址
}

in_addr_t IP地址,声明为uint32_t

2.结构体sockaddr_in的成员分析

Ⅰ.sin_family

每种协议族适用的地址族均不同。比如,IPv4使用4字节地址族,IPv6使用16字节地址族。
可以参考表中保存sin_family地址信息。

地址族(Address Family) 含 义
AF_INET IPv4网络协议中使用的地址族
AF_INET6 IPv6网络协议中使用的地址族
AF_LOCAL 本地通信中采用的UNIX协议的地址族

Ⅱ.sin_port

该成员保存16位端口号,重点在于,它以网络字节序保存(关于这一点稍后将给出详细说明)

Ⅲ.sin_addr

该成员保存32位IP地址信息,且也以网络字节序保存。为理解好该成员,应同时观察结构体
in_addr。但结构体in_addr声明为uint32_t,因此只需当作32位整数型即可。

Ⅳ.sin_zero

无特殊含义。只是为使结构体sockaddr_in的大小与sockaddr结构体保持一致而插人的成员。
必需填充为0,否则无法得到想要的结果。后面会另外讲解sockaddr。
从之前介绍的代码也可看出,sockaddrin结构体变量地址值将以如下方式传递给bind函数。
稍后将给出关于bind函数的详细说明,希望各位重点关注参数传递和类型转换部分的代码。

struct sockaddr_in serv_addr;
. . .
if(bind(serv_sock, (struct sockaddr * ) &serv_addr, sizeof(serv_addr)) == -1)
error_handling("bind() error");
. . .

此处重要的是第二个参数的传递。实际上,bind函数的第二个参数期望得到sockaddr结构体变量地址值,包括地址族、端口号、IP地址等。从下列代码也可看出,直接向sockaddr结构体填充这些信息会带来麻烦。

struct sockaddr
{sa_family_t	sin_family;	// 地址族(AddressFamily)char sa_data[14];	// 地址信息
}

此结构体成员sa_data保存的地址信息中需包含IP地址和端口号,剩余部分应填充0,这也是bind函数要求的。而这对于包含地址信息来讲非常麻烦,继而就有了新的结构体sockaddr_in。若按照之前的讲解填写sockaddr_in结构体,则将生成符合bind函数要求的字节流。最后转换为sockaddr型的结构体变量,再传递给bind函数即可。

三、网络字节序与地址变换

不同CPU中,4字节整数型值1在内存空间的保存方式是不同的。4字节整数型值1可用2进制
表示如下。
00000000 00000000 00000000 00000001
有些CPU以这种顺序保存到内存,另外一些CPU则以倒序保存。
00000001 00000000 00000000 00000000
若不考虑这些就收发数据则会发生问题,因为保存顺序的不同意味着对接收数据的解析顺序
也不同。

1.字节序(Order)与网络字节序

CPU向内存保存数据的方式有2种,这意味着CPU解析数据的方式也分为2种。
■ 大端序(BigEndian):高位字节存放到低位地址。
■ 小端序(LittleEndian):高位字节存放到高位地址(Intel, AMD) 。
(学过汇编的应该就知道了吧,没有学过的话就是按照地址排列方式不同分了两种方式)
在这里插入图片描述
代表CPU数据保存方式的主机字节序(Host Byte Order)在不同CPU中也各不相同。目前主流的
Intel系列CPU以小端序方式保存数据。接下来分析2台字节序不同的计算机之间数据传递过程中
可能出现的问题:
在这里插入图片描述
0x12和0x34构成的大端序系统值与0x34和0x12构成的小端序系统值相同。换言之,只有改变数据保存顺序才能被识别为同一值。图中,大端序系统传输数据0x1234时未考虑字节序问题,而直接以0x12、0x34的顺序发送。结果接收端以小端序方式保存数据,因此小端序接收的数据变成0x3412,而非0x1234。正因如此,在通过网络传输数据时约定统一方式,这种约定称为网络字节序(Network Byte Order),非常简单——统一为大端序。
即,先把数据数组转化成大端序格式再进行网络传输。因此,所有计算机接收数据时应识别该数据是网络字节序格式,小端序系统传输数据时应转化为大端序排列方式。

2.字节序转换(Endian Conversions)

相信大家已经理解了为何要在填充sockadrin结构体前将数据转换成网络字节序。接下来介绍帮助转换字节序的函数。
■ unsigned short htons(unsigned short);
■ unsigned short ntohs(unsigned short);
■ unsigned long htonl(unsigned long);
■ unsined long ntohl(unsigned long);
通过函数名应该能掌握其功能,只需了解以下细节。
■ htons中的h代表主机(host)字节序。
■ htons中的n代表网络(network)字节序。
另外,s指的是short,l指的是long(Linux中long类型占用4个字节,这很关键)。因此,htons
是h、to、n、s的组合,也可以解释为“把short型数据从主机字节序转化为网络字节序”。
再举个例子,ntohs可以解释为“把short型数据从网络字节序转化为主机字节序”。
通常,以s作为后缀的函数中,s代表2个字节short,因此用于端口号转换;以1作为后缀的函
数中,1代表4个字节,因此用于IP地址转换。另外,有些读者可能有如下疑问:
“我的系统是大端序的,为sockaddr_in结构体变量赋值前就不需要转换字节序了
吧?”
这么说也不能算错。但我认为,有必要编写与大端序无关的统一代码。这样,即使在大端序系统中,最好也经过主机字节序转换为网络字节序的过程。当然,此时主机字节序与网络字节序相同,不会有任何变化。下面通过示例说明以上函数的调用过程。

// endian_conv.c
#include <stdio.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{unsigned short host_port=0x1234; // 6unsigned short net_port;unsigned long host_addr=0x12345678; // 8unsigned long net_addr;net_port=htons(host_port); // 11net_addr=htonl(host_addr); // 12printf("Host ordered port: %#x \n",host_port);printf("Network ordered port: %#x \n",net_port);printf("Host ordered address:%#lx \n",host_addr);printf("Network ordered address: %#lx \n",net_addr);return 0;
}

第6、8行:各保存2个字节、4个字节的数据。当然,若运行程序的CPU不同,则保存的字
节序也不同。
第11、12行:变量host_port和host_addr中的数据转化为网络字节序。若运行环境为小端
序CPU,则按改变之后的字节序保存。
在这里插入图片描述
这就是在小端序CPU中运行的结果。如果在大端序CPU中运行,则变量值不会改变。大部分
朋友都会得到类似的运行结果,因为Intel和AMD系列的CPU都采用小端序标准。
也许大家认为:“既然数据传输采用网络字节序,那在传输前应直接把数据转换成网络字节序,接收的数据也需要转换成主机字节序再保存。”如果数据收发过程中没有自动转换机制,那当然需要程序员手动转换。这光想想就让人觉得可怕,难道真要强求程序员做这些事情吗?实际上没必要,这个过程是自动的。除了向sockaddr_in结构体变量填充数据外,其他情况无需考虑字节序问题。

四、网络地址的初始化与分配

前面已讨论过网络字节序,接下来介绍以bind函数为代表的结构体的应用。

1.将字符串信息转换为网络字节序的整数型

sockaddr_in中保存地址信息的成员为32位整数型。因此,为了分配IP地址,需要将其表示为32位整数型数据。这对于只熟悉字符串信息的我们来说实非易事。各位可以尝试将IP地址201.211.214.36转换为4字节整数型数据。

对于IP地址的表示,我们熟悉的是点分十进制表示法(Dotted Decimal Notation)而非整数型数据表示法。幸运的是,有个函数会帮我们将字符串形式的IP地址转换成32位整数型数据。此函数在转换类型的同时进行网络字节序转换。

#include <arpa/inet.h>
in_addr_t inet_addr(const char * string);
// 成功时返回32位大端序整数型值,失败时返回 INADDR_NONE。

如果向该函数传递类似“211.214.107.99”的点分十进制格式的字符串,它会将其转换为32位整数型数据并返回。当然,该整数型值满足网络字节序。另外,该函数的返回值类型in_addr_t在内部声明为32位整数型。下面这个示例表示该函数的调用过程。

// inet_addr.c
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{char *addr1="1.2.3.4";char *addr2="1.2.3.256"; // 7unsigned long conv_addr=inet_addr(addr1);  // 9if(conv_addr==INADDR_NONE)printf("Error occured! \n");elseprintf("Network ordered integer addr: %#lx \n", conv_addr);conv_addr=inet_addr(addr2);// 15if(conv_addr==INADDR_NONE)printf("Error occureded \n");elseprintf("Network ordered integer addr: %#lx \n\n", conv_addr);return 0;
}

第7行:1个字节能表示的最大整数为255,也就是说,它是错误的IP地址。利用该错误地
址验证inet_addr函数的错误检测能力。
第9、15行:通过运行结果验证第9行的函数正常调用,而第15行的函数调用出现异常。
在这里插入图片描述
从运行结果可以看出,inet_addr函数不仅可以把IP地址转成32位整数型,而且可以检测无效的IP地址。另外,从输出结果可以验证确实转换为网络字节序。
inetaton函数与inetaddr函数在功能上完全相同,也将字符串形式IP地址转换为32位网络字节序整数并返回。只不过该函数利用了in_addr结构体,且其使用频率更高。

#IncLude <aRpA/Inet.h>
int inet_aton(const char * string, struct in_addr * addr);
// 成功时返回1(true),失败时返回0(false)。
// string 含有需转换的IP地址信息的字符串地址值。
// addr 将保存转换结果的in_addr结构体变量的地址值。

实际编程中若要调用inet_addr函数,需将转换后的IP地址信息代入sockaddr_in结构体中声明的in_addr结构体变量。而inet_aton函数则不需此过程。原因在于,若传递inaddr结构体变量地址值,函数会自动把结果填人该结构体变量。通过这个示例让大家了解inetaton函数调用过程。

// inet_aton.c
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
void error_handling(char *message);int main(int argc, char *argv[])
{char *addr="127.232.124.79";struct sockaddr_in addr_inet; // 9if(!inet_aton(addr, &addr_inet.sin_addr)) // 10error_handling("Conversion error");elseprintf("Network ordered integer addr:%#x \n",addr_inet.sin_addr.s_addr);return 0;
}void error_handling(char *message)
{fputs(message,stderr);fputc('\n', stderr);exit(1);
}

第9、11行:转换后的IP地址信息需保存到sockaddr_in的in_addr型变量才有意义。因此,inet_aton函数的第二个参数要求得到in_addr型的变量地址值。这就省去了手动保存IP地址信息的过程。
在这里插入图片描述
上述运行结果无关紧要,更重要的是大家要熟练掌握该函数的调用方法。最后再介绍一个与inet_aton函数正好相反的函数,此函数可以把网络字节序整数型IP地址转换成我们熟悉的字符串形式。

#include <arpa/inet.h>
char * inet_ntoa(struct in_addr adr);
// 成功时返回转换的字符串地址值,失败时返回-1。

该函数将通过参数传人的整数型IP地址转换为字符串格式并返回。但调用时需小心,返回值
类型为char指针。返回字符串地址意味着字符串已保存到内存空间,但该函数未向程序员要求分
配内存,而是在内部申请了内存并保存了字符串。也就是说,调用完该函数后,应立即将字符串
信息复制到其他内存空间。因为,若再次调用inetntoa函数,则有可能覆盖之前保存的字符串信
息。总之,再次调用inet_ntoa函数前返回的字符串地址值是有效的。若需要长期保存,则应将字
符串复制到其他内存空间。下面给出该函数调用示例:

// inet_ntoa.c
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{struct sockaddr_in addr1, addr2;char *str_ptr;char str_arr[20];addr1.sin_addr.s_addr=htonl(0x1020304);addr2.sin_addr.s_addr=htonl(0x1010101);str_ptr=inet_ntoa(addr1.sin_addr); // 14strcpy(str_arr,str_ptr); // 15printf("Dotted-Decimal notation1: %s \n", str_ptr);inet_ntoa(addr2.sin_addr); // 18printf("Dotted-Decimal notation2: %s \n",str_ptr); // 19printf("Dotted-Decimal notation3: %s \n", str_arr); // 20return 0;
}

第14行:向inet_ntoa函数传递结构体变量addr1中的IP地址信息并调用该函数,返回字符
串形式的IP地址。
第15行:浏览并复制第14行中返回的IP地址信息。
第18、19行:再次调用inet_ntoa函数。由此得出,第14行中返回的地址已覆盖了新的IP
地址字符串,可通过第19行的输出结果进行验证。
第20行:第15行中复制了字符串,因此可以正确输出第14行中返回的IP地址字符串。
在这里插入图片描述

2.网络地址初始化

结合前面所学的内容,现在介绍套接字创建过程中常见的网络地址信息初始化方法。
struct sockaddr_in addr;
char * serv_ip = “211.217.168.13”; //声明IP地址字符串
char * serv_port = “9190”; //声明端口号字符串
memset(&addr, 0, sizeof(addr)); //结构体变量addr的所有成员初始化为0
addr.sin_family = AF_INET; //指定地址族
addr.sin_addr.s_addr = inet_addr(serv_ip); //基于字符串的IP地址初始化
addr.sin_port = htons(atoi(serv_port)); //基于字符串的端口号初始化
上述代码中,memset函数将每个字节初始化为同一值:第一个参数为结构体变量addr的地址值,即初始化对象为addr;第二个参数为0,因此初始化为0;最后一个参数中传人addr的长度,因此addr的所有字节均初始化为0。这么做是为了将sockaddrin结构体的成员sin_zero初始化为0。另外,最后一行代码调用的atoi函数把字符串类型的值转换成整数型。总之,上述代码利用字符串格式的IP地址和端口号初始化了sockaddrin结构体变量。
另外,代码中对IP地址和端口号进行了硬编码,这并非良策,因为运行环境改变就得更改代
码。因此,我们运行示例main函数时传人IP地址和端口号。

3.客户端地址信息初始化

上述网络地址信息初始化过程主要针对服务器端而非客户端。给套接字分配IP地址和端口号
主要是为下面这件事做准备:
“请把进入IP211.217.168.13、9190端口的数据传给我!”
反观客户端中连接请求如下:
“请连接到IP211.217.168.13、9190端口!”
请求方法不同意味着调用的函数也不同。服务器端的准备工作通过bind函数完成,而客户端则通过connect函数完成。因此,函数调用前需准备的地址值类型也不同。服务器端声明sockaddr_in结构体变量,将其初始化为赋予服务器端IP和套接字的端口号,然后调用bind函数;而客户端则声明sockaddrin结构体,并初始化为要与之连接的服务器端套接字的IP和端口号,然后调用connect函数。

4.INADDR_ANY

每次创建服务器端套接字都要输人IP地址会有些繁琐,此时可如下初始化地址信息。

struct sockaddr_in addr;
char * serv_port =9190";
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(atoi(serv_port));

与之前方式最大的区别在于,利用常数INADDR_ANY分配服务器端的IP地址。若采用这种
方式,则可自动获取运行服务器端的计算机IP地址,不必亲自输人。而且,若同一计算机中已分
配多个IP地址(多宿主(Multi-homed)计算机,一般路由器属于这一类),则只要端口号一致,
就可以从不同IP地址接收数据。因此,服务器端中优先考虑这种方式。而客户端中除非带有一部
分服务器端功能,否则不会采用。

5.第1章的hello_server.C、hello_client.c运行过程

第1章中执行以下命令以运行相当于服务器端的hello_server.c。
./hserver 9190
通过代码可知,向main函数传递的9190为端口号。通过此端口创建服务器端套接字并运行程
序,但未传递IP地址,因为可以通过INADDR_ANY指定IP地址。相信各位现在再去读代码会感
觉简单很多。
执行下列命令以运行相当于客户端的hello_client.c。与服务器端运行方式相比,最大的区别
是传递了IP地址信息。
./hclient 192.168.1.12 9190
192.168.1.12是回送地址(loopback address),指的是计算机自身IP地址。在第1章的示例中,服
务器端和客户端在同一计算机中运行,因此,连接目标服务器端的地址为192.168.1.12。

6.向套接字分配网络地址

既然已讨论了sockaddrin结构体的初始化方法,接下来就把初始化的地址信息分配给套接字。bind函数负责这项操作。

#include <sys/socket.h>
int bind(int sockfd, struct sockaddr * myaddr, socklen_t addrlen);
// 成功时返回0,失败时返回-1。
// sockfd 要分配地址信息(IP地址和端口号)的套接字文件描述符。
// myaddr 存有地址信息的结构体变量地址值。
// addrlen 第二个结构体变量的长度。

如果此函数调用成功,则将第二个参数指定的地址信息分配给第一个参数中的相应套接字。下面给出服务器端常见套接字初始化过程。

int serv_sock;
struct sockaddr_in serv_addr;
char * serv_port = "9190";/*创建服务器端套接字(监听套接字)*/
serv_Sock = socket(PF_INET, SOCK_STREAM, 0);/*地址信息初始化*/
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(serv_port));/*分配地址信息 */
bind(serv_sock, (struct sockaddr * )&serv_addr, sizeof(serv_addr));

服务器端代码结构默认如上,当然还有未显示的异常处理代码。

总结

`大家关于创建和连接套接字理解的怎么样?可以多看几遍,然后想一下这么做的逻辑,不要着急,慢慢思考几天就可以啦!

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

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

相关文章

Linux驱动开发-①pinctrl 和 gpio 子系统②并发和竞争③内核定时器

Linux驱动开发-①pinctrl 和 gpio 子系统②并发和竞争③内核定时器 一&#xff0c;pinctrl 和 gpio 子系统1.pinctrl子系统2.GPIO子系统 二&#xff0c;并发和竞争1.原子操作2.自旋锁3.信号量4.互斥体 三&#xff0c;按键实验四&#xff0c;内核定时器1.关于定时器的有关概念1.…

奇安信二面

《网安面试指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇网安资料库https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247486065&idx2&snb30ade8200e842743339d428f414475e&chksmc0e4732df793fa3bf39…

Python库安装报错解决思路以及机器学习环境配置详细方案

文章目录 概要第三方库gdalpymoltalibmahotasgraphviznltk-datalazypredictscikit-surprisenb_extensionspyqt5-toolsspacy、en_core_web_sm 机器学习GPU-torch安装torch_geometric安装ubuntu安装显卡驱动dlib安装torch-cluster、torch-scatter、torch-sparse和torch-geometric…

Power Apps 技术分享:连接SharePoint列表数据源

前言 在使用Power Apps的时候&#xff0c;使用列表作为数据源是非常方便和经济的&#xff0c;列表创建简单&#xff0c;SharePoint的存储也不像Dataverse需要按照容量付费。 正文 1.我们先在SharePoint中建一个列表&#xff0c;添加一些测试数据&#xff0c;如下图&#xff1a;…

【Linux】learning notes(4)cat、more、less、head、tail、vi、vim

文章目录 catmore 查看整个文件less 查看整个文件head 查看部分文件tail 查看部分文件vim / vi cat cat 命令在 Linux 和 Unix 系统中非常常用&#xff0c;它用于连接文件并打印到标准输出设备&#xff08;通常是屏幕&#xff09;。虽然 cat 的基本用法很简单&#xff0c;但它…

C++11函数包装器

目录 std::function 注意事项 包装静态成员函数 包装非静态成员函数 std::bind 用法 应用场景 std::function function是C11引入的类&#xff0c;可以用任何可调用对象作为参数&#xff0c;构造出一个新对象。 可调用对象有函数指针&#xff0c;仿函数&#xff0c;lamb…

maven的安装配置

目录 一、官网下载压缩包 二、配置环境变量 设置 MAVEN_HOME 添加 MAVEN_HOME\bin 到 PATH 三、配置本机仓库和远程仓库 四、配置idea 一、官网下载压缩包 Download Apache Maven – Maven 如上图。选择这个压缩包 选择好文件&#xff0c;下载完后&#xff0c;配置环境变…

分布式事务

1 事务 众所周知&#xff0c;事务具有ACID四大特性&#xff1a; 原子性&#xff08;Atomicity&#xff09;&#xff1a;事务作为一个整体被执行&#xff0c;包含在其中的对数据库的操作要么全部被执行&#xff0c;要么都不执行。 一致性&#xff08;Consistency&#xff09;&a…

Postman中Authorization和Headers的区别

案例 笔者在进行token验证的时候碰到的问题 一般如果是进行token验证&#xff0c;大部分是在Headers下面添加token名称及token的值 这样&#xff1a;后端提取请求头的token即可 还有一种是&#xff0c;左侧选择Bearer Token&#xff0c;右侧添加token的值,后端传递的 大概…

1.备战SISAP 2025挑战:调研2024挑战

简介 紧张刺激的SISAP 2025 challenge发布了&#xff0c;此博客用于记录备战的一些准备&#xff0c;思路和实验。 25年挑战介绍 详细信息参考SISAP Indexing challenge 2025 Task 1&#xff1a;内存受限索引 这项任务要求参与者开发具有reranking&#xff08;重排&#xf…

FPGA学习(二)——实现LED流水灯

FPGA学习(二)——实现LED流水灯 目录 FPGA学习(二)——实现LED流水灯一、DE2-115时钟源二、控制6个LED灯实现流水灯1、核心逻辑2、代码实现3、引脚配置4、实现效果 三、模块化代码1、分频模块2、复位暂停模块3、顶层模块 四、总结 一、DE2-115时钟源 DE2-115板子包含一个50MHz…

进程间通信--匿名管道

进程间通信介绍 进程间通信目的 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程资源共享&#xff1a;多个进程之间共享同样的资源。通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通知它&#xff08;它们&#xff09;发生了某种事件&…

【鸿蒙开发】Hi3861学习笔记-Visual Studio Code安装(New)

00. 目录 文章目录 00. 目录01. Visual Studio Code概述02. Visual Studio Code下载03. Visual Studio Code安装04. Visual Studio Code插件05. 附录 01. Visual Studio Code概述 vscode是一种简化且高效的代码编辑器&#xff0c;同时支持诸如调试&#xff0c;任务执行和版本管…

人工智能 Day06 pandas库进阶

1.处理缺失数据 总体流程是这样的&#xff0c; 归根在于如何处理NAN&#xff0c;接下来详细赘述 1.1. 处理缺失值的相关函数 判断缺失值 pd.isnull(df)&#xff1a;用于判断 DataFrame df 中的元素是否为缺失值&#xff08;NaN &#xff09;&#xff0c;返回一个与df 形状相同…

【Tools】Visual Studio Code安装保姆级教程(2025版)

00. 目录 文章目录 00. 目录01. Visual Studio Code概述02. Visual Studio Code下载03. Visual Studio Code安装04. Visual Studio Code配置05. 附录 01. Visual Studio Code概述 Visual Studio Code&#xff08;简称 VS Code&#xff09;是由微软开发的一款免费、开源且跨平台…

14.使用各种读写包操作 Excel 文件:辅助模块

一 各种读写包 这些是 pandas 在底层使用的各种读写包。无须安装 pandas&#xff0c;直接使用这些读写包就能够读写 Excel 工作簿。可以尽可能地使用 pandas 来解决这类问题&#xff0c;只在 pandas 没有提供你所需要的功能时才用到读写包。 表中没有 xlwings &#xff0c;因为…

AI赋能实时安全背带监测解决方案

背景&#xff1a;安全背带检测的行业刚需与技术痛点 在建筑施工、石油化工、仓储物流等高危行业中&#xff0c;安全背带是保障作业人员生命安全的最后一道防线。据统计&#xff0c;超过30%的高空坠落事故与未正确佩戴安全背带直接相关。传统依赖人工巡检的监督方式存在效率低、…

神聖的綫性代數速成例題2. 行列式的性質

性質 1&#xff1a;行列式與它的轉置行列式相等&#xff1a; 設為行列式&#xff0c;為其轉置行列式&#xff0c;則。 性質 2&#xff1a;交換行列式的兩行 (列)&#xff0c;行列式變號&#xff1a; 若行列式經過交換第行和第行得到行列式&#xff0c;則。 性質 3&#xff…

大模型推理 memory bandwidth bound (3) - MLA

系列文章目录 大模型推理 & memory bandwidth bound (1) - 性能瓶颈与优化概述 大模型推理 & memory bandwidth bound (2) - Multi-Query Attention 大模型推理 & memory bandwidth bound (3) - MLA 文章目录 系列文章目录前言一、原理1.低秩压缩 & 动机2.矩阵…

CTP开发爬坑指北(九)

CTP API开发中有很多需要注意的小细节&#xff0c;稍有不慎就会出问题&#xff0c;不然&#xff0c;轻则表现与预期不符&#xff0c;重则程序崩溃影响策略盈利。本系列将容易遇到的坑列出来&#xff0c;以供开发时参考&#xff0c;如有疑义之处&#xff0c;欢迎指正。 在国内期…