套接字socket编程的基础知识点

目录

前言(必读)

网络字节序

网络中的大小端问题

为什么网络字节序采用的是大端而不是小端?

网络字节序与主机字节序之间的转换

字符串IP和整数IP

整数IP存在的意义

字符串IP和整数IP相互转换的方式 

inet_addr函数(会自动将转化出的整数IP从主机字节序变为网络字节序)

inet_ntoa函数(会自动先把从网络中读取到的整数IP从网络字节序转化成主机字节序)

sockaddr、sockaddr_in、sockaddr_un结构体

对sockaddr_in的补充说明

socket编程的常见函数

socket函数

sendto函数

recvfrom函数

bind函数


前言(必读)

注意本文中说明的是套接字socket编程的基础知识点,关于这些知识点的更深入的使用方式和场景,还是得在笔者关于【基于UDP协议的网络服务器的模拟实现】和【基于TCP协议的网络服务器的模拟实现】的文章中才能体现出来

网络字节序

网络中的大小端问题

计算机在存储数据时是有大小端的概念的:

  • 大端模式: 数据的高字节内容保存在内存的低地址处,数据的低字节内容保存在内存的高地址处。
  • 小端模式: 数据的高字节内容保存在内存的高地址处,数据的低字节内容保存在内存的低地址处。

如果编写的程序只在本地机器上运行,那么是不需要考虑大小端问题的,因为同一台机器上的数据采用的存储方式都是一样的,要么采用的都是大端存储模式,要么采用的都是小端存储模式。但如果涉及网络通信,那就必须考虑大小端的问题,否则对端主机识别出来的数据可能与发送端想要发送的数据是不一致的。如下图,现在两台主机之间在进行网络通信,其中发送端是小端机,而接收端是大端机。发送端将发送缓冲区中的数据按内存地址从低到高的顺序发出后,接收端从网络中获取数据依次保存在接收缓冲区时,也是按内存地址从低到高的顺序保存的。

但由于发送端和接收端采用的分别是小端存储和大端存储,此时对于内存地址从低到高为44332211的序列,发送端按小端的方式识别出来是0x11223344,而接收端按大端的方式识别出来是0x44332211,此时接收端识别到的数据与发送端原本想要发送的数据就不一样了,这就是由于大小端的偏差导致数据识别出现了错误。

由于我们不能保证通信双方存储数据的方式是一样的,因此网络当中传输的数据必须考虑大小端问题。因此TCP/IP协议规定,网络数据流采用大端字节序,即低地址高字节。无论是大端机还是小端机,都必须按照TCP/IP协议规定的网络字节序来发送和接收数据。

  • 如果发送端是小端,需要先将数据转成大端,然后再发送到网络当中。
  • 如果发送端是大端,则可以直接进行发送。
  • 如果接收端是小端,需要先将接收到数据转成小端后再进行数据识别。
  • 如果接收端是大端,则可以直接进行数据识别。

在这个例子中,由于发送端是小端机,因此在发送数据前需要先将数据转成大端,然后再发送到网络当中,而由于接收端是大端机,因此接收端接收到数据后可以直接进行数据识别,此时接收端识别出来的数据就与发送端原本想要发送的数据相同了。

需要注意的是,所有的大小端的转化工作是由操作系统来完成的,因为该操作属于通信细节,不过也有部分的信息需要我们自行进行处理,比如端口号和IP地址。

为什么网络字节序采用的是大端而不是小端?

问题:网络字节序采用的是大端,而主机字节序一般采用的是小端,那为什么网络字节序不采用小端呢,毕竟如果网络字节序采用小端的话,发送端和接收端在发生和接收数据时就不用进行大小端的转换了。

答案:该问题有很多不同说法,下面列举了两种说法:

说法一: TCP在Unix时代就有了,以前Unix机器都是大端机,因此网络字节序也就采用的是大端,但之后人们发现用小端能简化硬件设计,所以现在主流的都是小端机,但协议已经不好改了。

说法二: 大端序更符合现代人的读写习惯。

网络字节序与主机字节序之间的转换

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,系统提供了四个函数,可以通过调用以下库函数实现网络字节序和主机字节序之间的转换:

(头文件都是#include <arpa/inet.h>)

  • uint32_t htonl(uint32_t hostlong);
  • uint16_t htons(uint16_t hostshort);
  • uint32_t ntohl(uint32_t netlong);
  • uint16_t ntohs(uint16_t netshort);

函数名当中的h表示host,n表示network,l表示32位长整数,s表示16位短整数。例如htonl表示将32位长整数从主机字节序转换为网络字节序。如果主机是小端字节序,则这些函数将参数做相应的大小端转换然后返回。如果主机是大端字节序,则这些函数不做任何转换,将参数原封不动地返回。

字符串IP和整数IP

IP地址的表现形式有两种:

  • 字符串IP:类似于192.168.233.123这种字符串形式的IP地址,叫做基于字符串的点分十进制IP地址。
  • 整数IP:IP地址在进行网络传输时所用的形式,用一个32位的整数来表示IP地址。

整数IP存在的意义

网络传输数据时是寸土寸金的,如果我们在网络传输时直接以基于字符串的点分十进制IP的形式进行IP地址的传送,那么此时一个IP地址至少就需要15个字节,但实际并不需要耗费这么多字节。

IP地址实际可以划分为四个区域,其中每一个区域的取值都是0~255,而这个范围的数字只需要用8个比特位就能表示,因此我们实际只需要32个比特位就能够表示一个IP地址。其中这个32位的整数的每一个字节对应的就是IP地址中的某个区域,我们将IP地址的这种表示方法称之为整数IP,此时表示一个IP地址只需要4个字节。

因为采用整数IP的方案表示一个IP地址只需要4个字节,并且在网络通信也能表示同样的含义,因此在网络通信时就没有用字符串IP而用的是整数IP,因为这样能够减少网络通信时数据的传送。

字符串IP和整数IP相互转换的方式 

inet_addr函数(会自动将转化出的整数IP从主机字节序变为网络字节序)

实际在进行字符串IP和整数IP的转换时,我们不需要自己编写转换逻辑,系统已经为我们提供了相应的转换函数,我们直接调用即可。

函数用于【先将字符串IP转化成整数IP,然后把整数IP从主机字节序转化成网络字节序】,该函数的函数原型如下:

in_addr_t inet_addr(const char *cp);

该函数使用起来非常简单,我们只需传入待转换的字符串IP,该函数返回的就是转换后的整数IP。除此之外,inet_aton函数也可以将字符串IP转换成整数IP,不过该函数使用起来没有inet_addr简单。再次强调,inet_addr会做两个操作,1、将点分十进制字符串变为整数后,2、还会将整数从主机字节序变为网络字节序。

inet_ntoa函数(会自动先把从网络中读取到的整数IP从网络字节序转化成主机字节序)

函数用于【先将整数IP从网络字节序转化成主机字节序,然后将主机字节序的整数IP转换成字符串IP】,该函数的函数原型如下:

char *inet_ntoa(struct in_addr in);

需要注意的是,传入inet_ntoa函数的参数类型是in_addr,因此我们在传参时不需要选中in_addr结构当中的32位的成员传入,直接传入in_addr结构体即可。

sockaddr、sockaddr_in、sockaddr_un结构体

套接字不仅支持跨网络的进程间通信,还支持本地的进程间通信(域间套接字)。在进行跨网络通信时我们需要传递的端口号和IP地址,而本地通信则不需要,因此套接字提供了sockaddr_in结构体和sockaddr_un结构体,其中sockaddr_in结构体是用于跨网络通信的,而sockaddr_un结构体是用于本地通信的。

为了让套接字的网络通信和本地通信能够使用同一套函数接口,于是就出现了sockeaddr结构体,该结构体与sockaddr_in和sockaddr_un的结构都不相同,但这三个结构体头部的16个比特位都是一样的,这个字段叫做协议家族。

此时当我们在调用sendto、recvfrom或者其他函数需要传参时,就不用传入sockeaddr_in或sockeaddr_un这样的结构体,而统一传入sockeaddr这样的结构体。我们在设置参数时就可以通过设置协议家族这个字段,来表明我们是要进行网络通信还是本地通信,在这些API(即sendto、recvfrom或者其他函数)内部就可以提取sockeaddr结构头部的16位进行识别,进而得出我们是要进行网络通信还是本地通信,然后执行对应的操作。此时我们就通过通用sockaddr结构,将套接字网络通信和本地通信的参数类型进行了统一。(注意实际我们在进行网络通信时,定义的还是sockaddr_in这样的结构体,只不过在调用sendto、recvfrom或者其他函数时,在传参时需要将该结构体的地址类型进行强转为sockaddr*)

问题:读了上一段我们可能会有一个疑问,即为什么没有用void*代替struct sockaddr*类型?我们可以将这些函数的struct sockaddr*参数类型改为void*,此时在函数内部也可以直接指定提取头部的16个比特位进行识别,最终也能够判断是需要进行网络通信还是本地通信,那为什么还要设计出sockaddr这样的结构呢?

答案:实际在设计这一套网络接口的时候C语言还不支持void*,于是就设计出了sockaddr这样的解决方案。并且在C语言支持了void*之后也没有将它改回来,因为这些接口是系统接口,系统接口是所有上层软件接口的基石,系统接口是不能轻易更改的,否则引发的后果是不可想的,这也就是为什么现在依旧保留sockaddr结构的原因。

对sockaddr_in的补充说明

sockaddr_in结构体的定义如下图右半部分,可以看到struct sockaddr_in中的成员有:

  • sin_family:表示协议家族。
  • sin_port:表示端口号,是一个16位的整数。
  • sin_addr:表示IP地址,是一个32位的整数。

剩下的字段一般不做处理,当然你也可以进行初始化。其中sin_addr的类型是struct in_addr,实际该结构体当中就只有一个成员(如上图左半部分),该成员就是一个32位的整数,IP地址实际就是存储在这个整数当中的。

socket编程的常见函数

socket函数

int socket(int domain, int type, int protocol);

参数说明:

  • domain:创建套接字的域或者叫做协议家族,也就是创建套接字的类型。该参数就相当于struct sockaddr结构的前16个位。如果是本地通信就设置为AF_UNIX,如果是网络通信就设置为AF_INET(IPv4)或AF_INET6(IPv6)。
  • type:创建套接字时所需的服务类型。其中最常见的服务类型是SOCK_STREAM和SOCK_DGRAM,如果是基于UDP的网络通信,我们采用的就是SOCK_DGRAM,叫做用户数据报服务,如果是基于TCP的网络通信,我们采用的就是SOCK_STREAM,叫做流式套接字,提供的是流式服务。
  • protocol:创建套接字的协议类别。你可以指明为TCP或UDP,但该字段一般直接设置为0就可以了,设置为0表示的就是默认,此时会根据传入的前两个参数自动推导出你最终需要使用的是哪种协议。

返回值说明:

  • 套接字创建成功返回一个文件描述符,创建失败返回-1,同时错误码会被设置。

功能说明:

  • 说简单点就是创建了一个文件,并返回了一个指向该文件的文件描述符,之后我们就可以在当前进程中向这个文件里写入数据并向网络中发送,或者从网络中读取数据到这个文件里并再将数据从文件中读到当前进程里。

问题:socket为什么可以具备这样的功能呢?它的底层干了什么?

答案:(结合下图思考)socket函数是被进程所调用的,而每一个进程在系统层面上都有一个进程地址空间PCB(task_struct)、文件描述符表(files_struct)以及对应打开的各种文件。而文件描述符表里面包含了一个数组fd_array成员,其中数组中的0、1、2下标依次对应的就是标准输入、标准输出以及标准错误。

(结合下图思考)当我们调用socket函数创建套接字时,实际相当于我们打开了一个“网络文件”,打开后在内核层面上就形成了一个对应的struct file结构体,同时该结构体被连入到了该进程对应的文件双链表,并将该结构体的首地址填入到了fd_array数组当中下标为3的位置,此时fd_array数组中下标为3的指针就指向了这个打开的“网络文件”,最后3号文件描述符作为socket函数的返回值返回给了用户。

其中每一个struct file结构体中包含的就是对应打开文件各种信息,比如文件的属性信息、操作方法以及文件缓冲区等。其中文件对应的属性在内核当中是由struct inode结构体来维护的;而文件对应的操作方法实际就是一堆的函数指针(比如read*和write*),在内核当中就是由struct file_operations结构体来维护的。

而对于文件缓冲区,OS会为不同的文件都分配一块内存,用于暂时在内存中保存属于文件的数据:

  • 比如在当前情景下,网络文件的文件缓冲区就是OS为网卡文件分配的一块内存,用于在内存中暂时保存属于网卡设备(或者说网卡文件)的数据,之后会根据属于网卡文件的文件缓冲区的刷新策略,将文件缓冲区中的数据刷新到内核,再由内核刷新到网卡设备上,网卡就可以根据自己的刷新策略向网络中发送信息了。
  • 再比如普通磁盘文件的文件缓冲区就是OS为磁盘文件分配的一块内存,用于在内存中暂时保存属于磁盘设备(或者说磁盘文件)的数据,之后会根据属于磁盘文件的文件缓冲区的刷新策略,将文件缓冲区中的数据刷新到内核,再由内核刷新到磁盘设备(或者说磁盘文件)上,这就完成了一次写入磁盘的操作。

sendto函数

如上图红框处。

参数说明:

  • int sockfd,sendto函数是向某台机器上的某个进程发信息,发信息需要一个通信通道,这个通道为【当前进程--->sockfd指向的文件的文件缓冲区(即分配给该文件的某块内存)--->内核缓冲区--->网卡--->网络--->对方的网卡--->对方的内核缓冲区--->对方的sockfd指向的文件的文件缓冲区--->对方的进程】,所以也就能够理解sockfd这个参数的作用了,即给sendto函数提供文件描述符,以找到其指向的文件的缓冲区,提供通信的媒介。
  • void *buf,sendto函数是向某台机器上的某个进程发信息,那么需要发出的信息是什么呢?buf指针指向的数据就是这个待发的信息。buf的类型是void*,方便sendto发送不同种类的信息。
  • size_t len,sendto函数是向某台机器上的某个进程发信息,len就用于指定发送多大长度的信息。注意这个len不一定是实际发送信息的长度,只是用户指定并期望发这么多,如果用户指定发送的长度远远大于了buf指针指向数据的长度,那实际只会发送buf指针指向数据的长度个数据。实际发送的数据的长度可以通过sendto的返回值获取。
  • int flags,设置为0即可,不必关心。
  • const struct sockaddr *dest_addr,sendto函数是向某台机器上的某个进程发信息,向哪台机器和哪个进程发送就是通过dest_addr指针(dest即destination,翻译为目的地)指向的sockaddr对象决定的,sockaddr对象里包含了标识目标主机的ip地址和标识目标进程的端口号port。
  • socklen_t addrlen就是上一个指针参数指向的sockaddr对象所占的空间大小,传入sizeof(sockaddr对象即可)。

返回值说明:

  • 在参数中已经隐含了该信息,即表示当前进程实际发送给对方进程的数据的长度。如果发生错误,返回值为-1。

recvfrom函数

如上图红框处。

参数说明:

  • int sockfd。recvfrom函数是用于接收某台机器上的某个进程发过来的信息,接收信息需要一个通信通道,这个通道为【对方进程--->对方进程的sockfd指向的文件的文件缓冲区(即分配给该文件的某块内存)--->对方的内核缓冲区--->对方的网卡--->网络--->当前主机的网卡--->当前主机的内核缓冲区--->当前进程的sockfd指向的文件的文件缓冲区--->当前的进程】,所以也就能够理解sockfd这个参数的作用了,即给recvfrom函数提供文件描述符,以找到其指向的文件的缓冲区,提供通信的媒介。
  • void *buf。recvfrom函数是用于接收某台机器上的某个进程发过来的信息,那么需要接收的信息需要存在哪里呢?buf指针指向的这块空间就用于存放这个接收到的信息。buf的类型是void*,方便recvfrom接收不同种类的信息。
  • size_t len。recvfrom函数是用于接收某台机器上的某个进程发过来的信息,len就用于指定接收多大长度的信息。注意这个len不一定是实际接收信息的长度,只是用户指定并期望接收这么多,如果用户指定接收的长度远远大于了buf指针指向空间所能容纳的最大长度,那实际只会接收buf指针指向空间的最大长度个数据。实际接收的数据的长度可以通过recvfrom的返回值获取。
  • int flags。设置为0即可,不必关心。
  • const struct sockaddr *src_addr。src即sourcere,翻译为来源。recvfrom函数是用于接收某台机器上的某个进程发过来的信息的,那是哪台机器上的哪个进程给我发的信息呢?我们可以通过src_addr指针指向的sockaddr对象得知。说一下,src_addr是个输出型参数,我们需要先设置一个sockaddr的对象,无所谓是否初始化它,然后把该sockaddr对象的地址传入recvfrom函数,函数调用结束后,src_addr指针指向的这个sockaddr对象中就包含了【是哪台机器的哪个进程给我发信息】的信息,即sockaddr对象里包含了标识【给我发信息的主机】的ip地址和标识【给我发信息的进程】的端口号port。
  • socklen_t *addrlen。其是个输入输出型参数,在调用recvfrom函数前,addrlen指针指向【表示上一个参数src_addr指向对象大小】的socklen_t对象,所以调用recvfrom函数时给addrlen传入一个值初始化成了sizeof(scr_addr指向的sockaddr对象)的socklen_t对象的地址即可;调用recvfrom函数结束后,addrlen指针指向【表示实际填充进上一个参数scr_addr指向的sockaddr对象的数据的大小】的socklen_t对象。既然addrlen是个输入输出型参数,那么使用它的方式为:调用recvfrom函数前,我们得先设置一个socklen_t的对象,然后将它初始化成sizeof(scr_addr指向的sockaddr对象),然后将该socklen_t对象的地址传给recvfrom函数的形参addrlen,recvfrom函数调用完毕后,addrlen指向的socklen_t对象的值就变成了实际填充进上一个参数scr_addr指向的sockaddr对象的数据的大小。

返回值说明:

  • 在参数中已经隐含了该信息,即表示实际接收到的对方进程发过来的数据的长度。如果发生错误,返回值为-1。

bind函数

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

调用完socket函数成功创建了套接字文件后,需要调用bind函数将【当前进程】和【某个ip与某个port】进行绑定,原因为:

  • (结合下图思考)现在套接字已经创建成功了,但作为一款服务器来讲,如果只是把套接字创建好了,那我们也只是在系统层面上打开了一个文件,操作系统将来并不知道是要将数据写入到磁盘还是刷到网卡,此时该文件还没有与网卡或者说网络关联起来。
  • 套接字socket文件用于通信,首先,如果想要网络通信,则必须通过网卡,所以你必须得指定从哪个网卡(ip)读取数据送到socket文件,这就是bind ip的原因,数据读取完毕后,送到哪个端口(进程)呢?所以你必须指定一个端口号。

参数说明:

  • int sockfd。发信息需要一个通信通道,这个通道为【当前进程--->sockfd指向的文件的文件缓冲区(即分配给该文件的某块内存)--->内核缓冲区--->网卡--->网络--->对方的网卡--->对方的内核缓冲区--->对方的sockfd指向的文件的文件缓冲区--->对方的进程】。接收信息需要一个通信通道,这个通道为【对方进程--->对方进程的sockfd指向的文件的文件缓冲区(即分配给该文件的某块内存)--->对方的内核缓冲区--->对方的网卡--->网络--->当前主机的网卡--->当前主机的内核缓冲区--->当前进程的sockfd指向的文件的文件缓冲区--->当前的进程】,可以看到在收或者发信息时,sockfd指向的文件是作为通信通道的一环的,所以调用bind函数需要sockfd就是在告诉bind函数,我需要将哪个文件设置成通信通道的一环。 
  • const struct sockaddr *addr。bind函数用于将【当前进程】和【某个ip与某个port】进行绑定,ip和port信息就包含在addr指向的sockaddr对象中。
  • socklen_t addrlen。为上一个参数addr指针指向的sockaddr对象的大小,传入sizeof(上一个参数addr指针指向的sockaddr对象)即可。

返回值说明:

  • 如果bind函数成功执行,它将返回 0。这表示套接字成功绑定到指定的地址和端口。

  • 如果bind函数执行失败,它将返回 -1。这表示绑定操作未成功,并且通常会伴随着设置全局变量errno来指示错误的原因。通过检查bind函数的返回值和检查errno变量的值,你可以确定bind失败的原因,以便进行适当的错误处理。一些常见的bind失败原因包括:1、端口已经被占用:如果指定的端口已经被其他程序占用,bind将失败,并且errno可能会被设置为 EADDRINUSE。2、无效的地址或端口:如果指定的地址或端口无效,bind也会失败,并且errno的值会指示具体的错误类型。3、权限不足:有些系统可能要求程序拥有特定的权限才能绑定到某些端口,如果权限不足,bind也会失败,并且errno的值可能会指示权限相关的错误。因此,当你调用bind函数时,应该检查其返回值,如果返回值是-1,则通过查看errno的值来确定失败的原因,并根据错误原因进行适当的错误处理。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【计算机毕业设计】基于SpringBoot+Vue大学生心理健康管理系统的开发与实现

博主主页&#xff1a;一季春秋博主简介&#xff1a;专注Java技术领域和毕业设计项目实战、Java、微信小程序、安卓等技术开发&#xff0c;远程调试部署、代码讲解、文档指导、ppt制作等技术指导。主要内容&#xff1a;毕业设计(Java项目、小程序等)、简历模板、学习资料、面试题…

1 MySQL 高级(进阶) SQL 语句(一)

目录 1 MySQL SQL 语句 1.1SELECT 1.2 DISTINCT 1.3 WHERE 1.4 AND OR 1.5 in 1.6 BETWEEN 2 通配符 ----通常通配符都是跟 LIKE 一起使用的 2.1 LIKE 2.2 ORDER BY 3函数 3.1数学函数 3.2 聚合函数 3.3 字符串函数 4 GROUP BY 4.1 HAVING 5 别名 6 子查询 …

JavaScript中的解构赋值

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 对象解构赋值⭐ 数组解构赋值⭐ 默认值和剩余元素⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚…

数据湖在爱奇艺数据中台的应用

01 我们眼中的数据湖 作为爱奇艺的数据中台团队&#xff0c;我们的核心任务是管理和服务公司内的大量数据资产。在实施数据治理的过程中&#xff0c;我们不断吸收新的理念&#xff0c;引入尖端的工具&#xff0c;以精细化我们的数据体系管理。“数据湖”作为近年来数据领域广泛…

机器学习之正则化与验证提高模型泛化

文章目录 正则化&#xff08;Regularization&#xff09;&#xff1a;验证&#xff08;Validation&#xff09;&#xff1a; 正则化和验证是机器学习中重要的概念&#xff0c;它们帮助提高模型的性能和泛化能力。让我详细介绍一下这两个概念&#xff1a; 正则化&#xff08;Re…

【完美世界】天仙书院偷食也就算了,竟然还偷院长的孙女,美滋滋

Hello,小伙伴们&#xff0c;我是小郑继续为大家深度解析完美世界系列。 齐道临从天仙书院劫走石昊&#xff0c;为何天仙书院不仅没去找他麻烦&#xff0c;反而给他一块随意进入渡劫神莲池的令牌&#xff1f;石昊来到上界也是闹出不小的动静&#xff0c;先是在恶魔岛的神碑留名&…

LeetCode 75-02:字符串的最大公因子

前置知识&#xff1a;使用欧几里得算法求出最大公约数 func gcdOfStrings(str1 string, str2 string) string {if str1str2 ! str2str1 {return ""}return str1[:gcd(len(str1), len(str2))] }func gcd(a, b int)int{if b 0{return a}return gcd(b, a%b) }

求组合数(递归版)(杨辉三角形)

description 请编写递归函数&#xff0c;求组合数。 函数原型 double Cmb(int x, int y); 说明&#xff1a;x 和 y 为非负整数&#xff0c;且 x≥y&#xff0c;函数值为组合数 C x y ​ 。 裁判程序 #include <stdio.h> double Cmb(int x, int y); int main() { int m…

使用Python+Flask/Moco框架/Fiddler搭建简单的接口Mock服务

一、Mock测试 1、介绍 mock&#xff1a;就是对于一些难以构造的对象&#xff0c;使用虚拟的技术来实现测试的过程mock测试&#xff1a;在测试过程中&#xff0c;对于某些不容易构造或者不容易获取的对象&#xff0c;可以用一个虚拟的对象来代替的测试方法接口mock测试&#x…

JPA的注解@Field指定为Keyword失败,导致查询不到数据

一、背景 使用 jpa 对es操作&#xff0c;查询条件不生效&#xff0c;需求是批量查询课程编号。说白了&#xff0c;就是一个In集合的查询。在es里&#xff0c;如果是精准匹配是termQuery&#xff0c;比如&#xff1a; queryBuilder.filter(QueryBuilders.termQuery(“schoolId…

全国职业技能大赛云计算--高职组赛题卷①(容器云)

全国职业技能大赛云计算--高职组赛题卷①&#xff08;容器云&#xff09; 第二场次题目&#xff1a;容器云平台部署与运维任务1 Docker CE及私有仓库安装任务&#xff08;5分&#xff09;任务2 基于容器的web应用系统部署任务&#xff08;15分&#xff09;任务3 基于容器的持续…

ARM Cortex-M内核中系统堆栈

文章目录 有无OS的栈结构区别&#xff1a;裸机的任务栈结构带FreeRTOS操作系统的任务栈 ARM的寄存器有哪些特殊寄存器有哪些 关于FreeRTOS中的SP寄存器栈操作【压栈与弹栈的操作】一般函数嵌套调用时sp指针的变化Cortex-M内核的MSP与PSP作用 有无OS的栈结构区别&#xff1a; 裸…

【List篇】LinkedList 详解

目录 成员变量属性构造方法add(), 插入节点方法remove(), 删除元素方法set(), 修改节点元素方法get(), 取元素方法ArrayList 与 LinkedList的区别Java中的LinkedList是一种实现了List接口的 双向链表数据结构。链表是由一系列 节点(Node)组成的,每个节点包含了指向 上一个…

第9章 【MySQL】InnoDB的表空间

表空间 是一个抽象的概念&#xff0c;对于系统表空间来说&#xff0c;对应着文件系统中一个或多个实际文件&#xff1b;对于每个独立表空间来说&#xff0c;对应着文件系统中一个名为 表名.ibd 的实际文件。大家可以把表空间想象成被切分为许许多多个 页 的池子&#xff0c;当我…

队列(JAVA)

队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出的性质。 入队列&#xff1a;进行插入操作的一端称为队尾 出队列&#xff1a;进行删除操作的一端称为队头 在JAVA中队列和栈不同Stack是一个类&a…

线性代数的本质——几何角度理解

B站网课来自 3Blue1Brown的翻译版&#xff0c;看完醍醐灌顶&#xff0c;强烈推荐&#xff1a; 线性代数的本质 本课程从几何的角度翻译了线代中各种核心的概念及性质&#xff0c;对做题和练习效果有实质性的提高&#xff0c;下面博主来总结一下自己的理解 1.向量的本质 在物…

ElasticSearch(二)

1.DSL查询文档 elasticsearch的查询依然是基于JSON风格的DSL来实现的。 1.1.DSL查询分类 Elasticsearch提供了基于JSON的DSL&#xff08;Domain Specific Language&#xff09;来定义查询。常见的查询类型包括&#xff1a; 查询所有&#xff1a;查询出所有数据&#xff0c;…

Hadoop的HDFS高可用方案

一、Hadoop高可用简介 Hadoop 高可用 (High Availability) 分为 HDFS 高可用和 YARN 高可用&#xff0c;两者的实现基本类似&#xff0c;但 HDFSNameNode 对数据存储及其一致性的要求比 YARN ResourceManger 高得多&#xff0c;所以它的实现也更加复杂 1、HDFS系统高可用简介…

uniapp 离线打包 plus.runtime.install 安装页面不弹起

uniapp 离线打包 plus.runtime.install 安装页面不弹起 updateVersion(webview : any, eventTitle : string, eventContent : string) {const loading plus.nativeUI.showWaiting(准备下载);var dtask plus.downloader.createDownload(eventContent,{method: GET,timeout: 5…

Linux:文本搜索命令grep

相关阅读 Linuxhttps://blog.csdn.net/weixin_45791458/category_12234591.html grep是类Unix系统中用于搜索并打印文件中符合某种模式(pattern)的行。grep命令的的基本语法如下所示&#xff1a; grep [OPTIONS] PATTERN [FILE...] grep [OPTIONS] [-e PATTERN | -f FILE] [F…