【UEFI基础】EDK网络框架(TCP4)

TCP4

TCP4协议说明

相比UDP4,TCP4是一种面向连接的通信协议,因此有更好的可靠性。

TCP4的首部格式如下:

在这里插入图片描述

各个参数说明如下:

字段长度(bit)含义
Source Port16源端口,标识哪个应用程序发送。
Destination Port16目的端口,标识哪个应用程序接收。
Sequence Number32序号字段。
TCP链接中传输的数据流中每个字节都编上一个序号。
序号字段的值指的是本报文段所发送的数据的第一个字节的序号。
Acknowledgment Number32确认号。
是期望收到对方的下一个报文段的数据的第1个字节的序号。
即上次已成功接收到的数据字节序号加1。
只有ACK标识为1,此字段有效。
Data Offset4数据偏移,即首部长度。
指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。
以32比特(4字节)为计算单位。
最多有60字节的首部,若无选项字段,正常为20字节。
Reserved6保留,必须填0。
URG1紧急指针有效标识。
它告诉系统此报文段中有紧急数据,应尽快传送(相当于高优先级的数据)。
ACK1确认序号有效标识。
只有当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。
PSH1标识接收方应该尽快将这个报文段交给应用层。
接收到PSH = 1的TCP报文段,应尽快的交付接收应用进程,而不再等待整个缓存都填满了后再向上交付。
RST1重建连接标识。
当RST=1时,表明TCP连接中出现严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立连接。
SYN1同步序号标识,用来发起一个连接。
SYN=1表示这是一个连接请求或连接接受请求。
FIN1发端完成发送任务标识。
用来释放一个连接。
FIN=1表明此报文段的发送端的数据已经发送完毕,并要求释放连接。
Window16窗口:TCP的流量控制。
窗口起始于确认序号字段指明的值,这个值是接收端期望接收的字节数。
窗口最大为65535字节。
Checksum16校验字段,包括TCP首部和TCP数据,是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。
在计算检验和时,要在TCP报文段的前面加上12字节的伪首部。
Urgent Pointer16紧急指针,只有当URG标志置1时紧急指针才有效。
TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。
紧急指针指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)。
Options可变选项字段。
TCP协议最初只规定了一种选项,即最长报文段长度(只包含数据字段,不包括TCP首部),又称为MSS。
MSS告诉对方TCP“我的缓存所能接收的报文段的数据字段的最大长度是MSS个字节”。
新的RFC规定有以下几种选型:选项表结束,空操作,最大报文段长度,窗口扩大因子,时间戳。
* 选项表结束。
* 空操作:没有特殊含义,一般用于将TCP选项的总长度填充为4字节的整数倍。
* 最大报文段长度:又称为MSS,只包含数据字段,不包括TCP首部。
* 窗口扩大因子:3字节,其中一个字节表示偏移值S。新的窗口值等于TCP首部中的窗口位数增大到(16+S),相当于把窗口值向左移动S位后获得实际的窗口大小。
* 时间戳:10字节,其中最主要的字段是时间戳值(4字节)和时间戳回送应答字段(4字节)。
Padding可变填充字段,用来补位,使整个首部长度是4字节的整数倍。
data可变TCP负载。

对应UEFI中的代码:

typedef UINT32  TCP_SEQNO;
typedef UINT16  TCP_PORTNO;

//
// TCP header definition
//
typedef struct {
  TCP_PORTNO    SrcPort;
  TCP_PORTNO    DstPort;
  TCP_SEQNO     Seq;
  TCP_SEQNO     Ack;
  UINT8         Res     : 4;
  UINT8         HeadLen : 4;
  UINT8         Flag;
  UINT16        Wnd;
  UINT16        Checksum;
  UINT16        Urg;
} TCP_HEAD;

TCP的连接过程大致如下:

在这里插入图片描述

TCP4代码综述

TCP4也是一个通用的网络协议,其实现在NetworkPkg\TcpDxe\TcpDxe.inf,这里首先需要看下它的入口:

EFI_STATUS
EFIAPI
TcpDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Install the TCP Driver Binding Protocol
  //
  Status = EfiLibInstallDriverBindingComponentName2 (
             ImageHandle,
             SystemTable,
             &gTcp4DriverBinding,
             ImageHandle,
             &gTcpComponentName,
             &gTcpComponentName2
             );

  //
  // Initialize ISS and random port.
  //
  Seed            = NetRandomInitSeed ();
  mTcpGlobalIss   = NET_RANDOM (Seed) % mTcpGlobalIss;
  mTcp4RandomPort = (UINT16)(TCP_PORT_KNOWN + (NET_RANDOM (Seed) % TCP_PORT_KNOWN));
}

因为TCP4也是一个UEFI Driver Model,所以第一步是安装gTcp4DriverBinding,其实现:

EFI_DRIVER_BINDING_PROTOCOL  gTcp4DriverBinding = {
  Tcp4DriverBindingSupported,
  Tcp4DriverBindingStart,
  Tcp4DriverBindingStop,
  0xa,
  NULL,
  NULL
};

而第二步是初始化一个随机的TCP端口,根据通用网络协议的做法,TCP的端口占两个字节(即16位),只要不是0-1023里面的公认端口都可以,且跟UDP端口的一致也没有关系。

最后还有一个mTcpGlobalIss,这里的ISS全称是Initial Sending Sequence,它的值本身不是很重要,从名字也知道它的作用就是指定TCP发送的第一个包的序列号,这是因为TCP一次发送的包可能会有很多,所以需要排序。

UDP4在UEFI网络协议栈中的关系图:

支持
提供
支持
支持
提供
支持
提供
提供
提供
支持
提供
提供
支持
支持
提供
提供
提供
支持
提供
提供
gEfiPciIoProtocolGuid
UNDI
gEfiNetworkInterfaceIdentifierProtocolGuid_31
gEfiDevicePathProtocolGuid
SNP
gEfiSimpleNetworkProtocolGuid
MNP
gEfiVlanConfigProtocolGuid
gEfiManagedNetworkServiceBindingProtocolGuid
gEfiManagedNetworkProtocolGuid
ARP
gEfiArpServiceBindingProtocolGuid
gEfiArpProtocolGuid
IP4
gEfiIp4ServiceBindingProtocolGuid
gEfiIp4Config2ProtocolGuid
gEfiIp4ProtocolGuid
UDP4
gEfiTcp4ServiceBindingProtocolGuid
gEfiTcp4ProtocolGuid

Tcp4DriverBindingSupported

TCP4依赖于IP4:

EFI_STATUS
EFIAPI
Tcp4DriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
  )
{
  //
  // Test for the Ip4ServiceBinding Protocol
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiIp4ServiceBindingProtocolGuid,
                  NULL,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL
                  );
  return Status;
}

Tcp4DriverBindingStart

Start函数里面只有一个函数TcpCreateService(),它的作用就是初始化TCP_SERVICE_DATA

TCP_SERVICE_DATA

该结构体本身也比较简单:

typedef struct _TCP_SERVICE_DATA {
  UINT32                          Signature;
  EFI_HANDLE                      ControllerHandle;
  EFI_HANDLE                      DriverBindingHandle;
  UINT8                           IpVersion;
  IP_IO                           *IpIo;
  EFI_SERVICE_BINDING_PROTOCOL    ServiceBinding;
  LIST_ENTRY                      SocketList;
} TCP_SERVICE_DATA;

其重点其实就是一个SocketList,它对应列表成员是SOCKET

SOCKET

Socket就是TCP的子项。该结构体如下:

///
/// The socket structure representing a network service access point.
///
struct _TCP_SOCKET {
  //
  // Socket description information
  //
  UINT32                      Signature;     ///< Signature of the socket
  EFI_HANDLE                  SockHandle;    ///< The virtual handle of the socket
  EFI_HANDLE                  DriverBinding; ///< Socket's driver binding protocol
  EFI_DEVICE_PATH_PROTOCOL    *ParentDevicePath;
  EFI_DEVICE_PATH_PROTOCOL    *DevicePath;
  LIST_ENTRY                  Link;
  UINT8                       ConfigureState;
  SOCK_TYPE                   Type;
  UINT8                       State;
  UINT16                      Flag;
  EFI_LOCK                    Lock;         ///< The lock of socket
  SOCK_BUFFER                 SndBuffer;    ///< Send buffer of application's data
  SOCK_BUFFER                 RcvBuffer;    ///< Receive buffer of received data
  EFI_STATUS                  SockError;    ///< The error returned by low layer protocol
  BOOLEAN                     InDestroy;

  //
  // Fields used to manage the connection request
  //
  UINT32                      BackLog;        ///< the limit of connection to this socket
  UINT32                      ConnCnt;        ///< the current count of connections to it
  SOCKET                      *Parent;        ///< listening parent that accept the connection
  LIST_ENTRY                  ConnectionList; ///< the connections maintained by this socket
  //
  // The queue to buffer application's asynchronous token
  //
  LIST_ENTRY                  ListenTokenList;
  LIST_ENTRY                  RcvTokenList;
  LIST_ENTRY                  SndTokenList;
  LIST_ENTRY                  ProcessingSndTokenList;

  SOCK_COMPLETION_TOKEN       *ConnectionToken; ///< app's token to signal if connected
  SOCK_COMPLETION_TOKEN       *CloseToken;      ///< app's token to signal if closed
  //
  // Interface for low level protocol
  //
  SOCK_PROTO_HANDLER          ProtoHandler;                      ///< The request handler of protocol
  UINT8                       ProtoReserved[PROTO_RESERVED_LEN]; ///< Data fields reserved for protocol
  UINT8                       IpVersion;
  NET_PROTOCOL                NetProtocol;                      ///< TCP4 or TCP6 protocol socket used
  //
  // Callbacks after socket is created and before socket is to be destroyed.
  //
  SOCK_CREATE_CALLBACK        CreateCallback;  ///< Callback after created
  SOCK_DESTROY_CALLBACK       DestroyCallback; ///< Callback before destroyed
  VOID                        *Context;        ///< The context of the callback
};

该结构体的创建来自SockCreate(),其调用流程:

TcpServiceBindingCreateChild
SockCreateChild
PktRcvdNotify
TcpRxCallback
TcpInput
TcpCloneTcb
SockClone
SockCreate

左边的PktRcvdNotify是IP4中的回调函数,右边就是常用的创建子项的函数。

SOCKET中的主要成员说明如下:

  • SockHandleNetProtocol:这两个参数需要一起看,它们的初始化位于SockCreate()函数中:
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &Sock->SockHandle,
                  TcpProtocolGuid,
                  &Sock->NetProtocol,
                  NULL
                  );

事实上就是安装了一个Protocol,对应的GUID是TcpProtocolGuid,它其实就是两个选择,v4和v6,对应到NetProtocol也就有了两个版本:

  if (SockInitData->IpVersion == IP_VERSION_4) {
    TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
    ProtocolLength  = sizeof (EFI_TCP4_PROTOCOL);
  } else {
    TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
    ProtocolLength  = sizeof (EFI_TCP6_PROTOCOL);
  }

我们需要关注的是gEfiTcp4ProtocolGuidEFI_TCP4_PROTOCOL,后者对应结构体:

struct _EFI_TCP4_PROTOCOL {
  EFI_TCP4_GET_MODE_DATA    GetModeData;
  EFI_TCP4_CONFIGURE        Configure;
  EFI_TCP4_ROUTES           Routes;
  EFI_TCP4_CONNECT          Connect;
  EFI_TCP4_ACCEPT           Accept;
  EFI_TCP4_TRANSMIT         Transmit;
  EFI_TCP4_RECEIVE          Receive;
  EFI_TCP4_CLOSE            Close;
  EFI_TCP4_CANCEL           Cancel;
  EFI_TCP4_POLL             Poll;
};

就是真正用于收发数据的TCP接口。

  • DriverBinding:这个值来自SOCK_INIT_DATA中的DriverBinding
Sock->DriverBinding       = SockInitData->DriverBinding;

而后者有来自TCP_SERVICE_DATA中的DriverBindingHandle

mTcpDefaultSockData.DriverBinding = TcpServiceData->DriverBindingHandle;

所以说到底SOCKET中的DriverBinding就是TCP_SERVICE_DATA中的DriverBindingHandle,最终就是EFI_DRIVER_BINDING_PROTOCOL中的DriverBindingHandle

  • ParentDevicePath:它跟上一个参数是有关联的:
  Status = gBS->OpenProtocol (
                  TcpServiceData->ControllerHandle,
                  &gEfiDevicePathProtocolGuid,
                  (VOID **)&This->ParentDevicePath,
                  TcpServiceData->DriverBindingHandle,
                  This->SockHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );

实际上就是代表网卡的设备路径,其值以字符串表示大概是这样的:

PciRoot(0x0)/Pci(0x2,0x0)/MAC(525400123456,0x1)

PCI路径可以不用关注,重点在于到MAC为止。

  • DevicePath:它是在ParentDevicePath之上增加了IPv4_DEVICE_PATH的结果:
  Sock->DevicePath = AppendDevicePathNode (Sock->ParentDevicePath, DevicePath);
  Status = gBS->InstallProtocolInterface (
                  &Sock->SockHandle,
                  &gEfiDevicePathProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  Sock->DevicePath
                  );

其值以字符串表示大概是这样的:

PciRoot(0x0)/Pci(0x2,0x0)/MAC(525400123456,0x1)/IPv4(0.0.0.0)
  • Link:该值与TCP_SERVICE_DATA中的SocketList连接。
  • ConfigureState:表示Socket的配置状态,有以下的值:
///
/// Socket configure state
///
#define SO_UNCONFIGURED        0
#define SO_CONFIGURED_ACTIVE   1
#define SO_CONFIGURED_PASSIVE  2
#define SO_NO_MAPPING          3
  • Type:Socket有两种类型,分别是流格式套接字和数据报格式套接字,对应如下代码:
///
///  The socket type.
///
typedef enum {
  SockDgram, ///< This socket providing datagram service
  SockStream ///< This socket providing stream service
} SOCK_TYPE;

流格式套接字也叫“面向连接的套接字”,它有以下的特征:

  1. 数据在传输过程中不会消失;
  2. 数据是按照顺序传输的;
  3. 数据的发送和接收不是同步的(有的教程也称“不存在数据边界”)。

数据报格式套接字也叫“无连接的套接字”,它有以下的特征:

  1. 强调快速传输而非传输顺序;
  2. 传输的数据可能丢失也可能损毁;
  3. 限制每次传输的数据大小;
  4. 数据的发送和接收是同步的(有的教程也称“存在数据边界”)。
  • State:表示Socket本身的状态,有如下的值:
///
/// Socket state
///
#define SO_CLOSED         0
#define SO_LISTENING      1
#define SO_CONNECTING     2
#define SO_CONNECTED      3
#define SO_DISCONNECTING  4
  • Flag:表示TCP头部中的标识,有如下的值:
///
/// Flags in the TCP header
///
#define TCP_FLG_FIN  0x01
#define TCP_FLG_SYN  0x02
#define TCP_FLG_RST  0x04
#define TCP_FLG_PSH  0x08
#define TCP_FLG_ACK  0x10
#define TCP_FLG_URG  0x20

在TCP4协议说明可以找到它们的说明。

  • SndBufferRcvBuffer:收发数据使用的缓存。
  • SockError:Socket的状态。
  • BackLog:表示Socket连接数上限。
  • ConnCnt:表示当前的Socket连接数。
  • Parent:它的类型也是SOCKET,从这里可以看出来Socket之间也有父子关系。从前面的调用流程可以看到,Socket可以通过SockCreate()函数创建,而后者又由两个函数调用:
SockCreateChild
SockCreate
SockClone

它们对应的入参是不同的,SockCreateChild()的入参是mTcpDefaultSockData

SOCK_INIT_DATA  mTcpDefaultSockData = {
  SockStream,
  SO_CLOSED,
  NULL,	// Parent
  TCP_BACKLOG,
  TCP_SND_BUF_SIZE,
  TCP_RCV_BUF_SIZE,
  IP_VERSION_4,
  NULL,
  TcpCreateSocketCallback,
  TcpDestroySocketCallback,
  NULL,
  NULL,
  0,
  TcpDispatcher,
  NULL,
};

SockClone()的实现:

SOCKET *
SockClone (
  IN SOCKET  *Sock
  )
{
  SOCKET          *ClonedSock;
  SOCK_INIT_DATA  InitData;

  InitData.BackLog         = Sock->BackLog;
  InitData.Parent          = Sock;	// 注意这里的Parent
  InitData.State           = Sock->State;
  InitData.ProtoHandler    = Sock->ProtoHandler;
  InitData.Type            = Sock->Type;
  InitData.RcvBufferSize   = Sock->RcvBuffer.HighWater;
  InitData.SndBufferSize   = Sock->SndBuffer.HighWater;
  InitData.DriverBinding   = Sock->DriverBinding;
  InitData.IpVersion       = Sock->IpVersion;
  InitData.Protocol        = &(Sock->NetProtocol);
  InitData.CreateCallback  = Sock->CreateCallback;
  InitData.DestroyCallback = Sock->DestroyCallback;
  InitData.Context         = Sock->Context;
  InitData.ProtoData       = Sock->ProtoReserved;
  InitData.DataSize        = sizeof (Sock->ProtoReserved);

  ClonedSock = SockCreate (&InitData);

从这里带出了新的父子关系。实际的测试中发现,SockCreateChild()会在启动中执行,并且Parent的值都是0,而SockClone()会在使用TCP时创建Socket,此时的Parent是一个有效的值。

  • ConnectionList:当前Socket维护的连接。
  • ListenTokenListRcvTokenListSndTokenListProcessingSndTokenList:处理收发数据的Token列表。
  • ConnectionToken:Socket连接后调用的Token。
  • CloseToken:Socket关闭时调用的Token。
  • ProtoHandlerProtoReserved:Socket请求的回调函数以及对应的入参,回调函数就是TcpDispatcher(),根据入参会执行不同的操作:
EFI_STATUS
TcpDispatcher (
  IN SOCKET  *Sock,
  IN UINT8   Request,
  IN VOID    *Data    OPTIONAL
  )
{
  switch (Request) {
    case SOCK_POLL:
    case SOCK_CONSUMED:
    case SOCK_SND:
    case SOCK_CLOSE:
    case SOCK_ABORT:
    case SOCK_SNDPUSH:
    case SOCK_SNDURG:
    case SOCK_CONNECT:
    case SOCK_ATTACH:
    case SOCK_FLUSH:
    case SOCK_DETACH:
    case SOCK_CONFIGURE:
    case SOCK_MODE:
    case SOCK_ROUTE:
    default:
  }
}
  • IpVersion:这里就是IP_VERSION_4
  • CreateCallbackDestroyCallbackContext:对应mTcpDefaultSockData中的函数,以及它们的入参。

EFI_TCP4_PROTOCOL

该Protocol的结构体如下:

///
/// The EFI_TCP4_PROTOCOL defines the EFI TCPv4 Protocol child to be used by
/// any network drivers or applications to send or receive data stream.
/// It can either listen on a specified port as a service or actively connected
/// to remote peer as a client. Each instance has its own independent settings,
/// such as the routing table.
///
struct _EFI_TCP4_PROTOCOL {
  EFI_TCP4_GET_MODE_DATA    GetModeData;
  EFI_TCP4_CONFIGURE        Configure;
  EFI_TCP4_ROUTES           Routes;
  EFI_TCP4_CONNECT          Connect;
  EFI_TCP4_ACCEPT           Accept;
  EFI_TCP4_TRANSMIT         Transmit;
  EFI_TCP4_RECEIVE          Receive;
  EFI_TCP4_CLOSE            Close;
  EFI_TCP4_CANCEL           Cancel;
  EFI_TCP4_POLL             Poll;
};

对应的实现在NetworkPkg\TcpDxe\TcpDriver.c:

EFI_TCP4_PROTOCOL  gTcp4ProtocolTemplate = {
  Tcp4GetModeData,
  Tcp4Configure,
  Tcp4Routes,
  Tcp4Connect,
  Tcp4Accept,
  Tcp4Transmit,
  Tcp4Receive,
  Tcp4Close,
  Tcp4Cancel,
  Tcp4Poll
};

相比于其它的网络Protocol,这个稍有不同,它包含了Connect、Accept、Close等TCP常用操作。

后面会介绍这些函数的实现。

Tcp4.Connect

对应的实现是Tcp4Connect,其实现是Socket的连接:

EFI_STATUS
EFIAPI
Tcp4Connect (
  IN EFI_TCP4_PROTOCOL          *This,
  IN EFI_TCP4_CONNECTION_TOKEN  *ConnectionToken
  )
{
  return SockConnect (Sock, ConnectionToken);
}

其它的Tcp4Accept、Tcp4Transmit、Tcp4Receive、Tcp4Close等也都是Socket的操作。

TCP代码示例

TCP的代码示例可以在beni/BeniPkg/DynamicCommand/TestDynamicCommand/TestTcp.c · jiangwei/edk2-beni - 码云 - 开源中国 (gitee.com)中找到,它实际上来自EmbeddedPkg\Drivers\AndroidFastbootTransportTcpDxe\FastbootTransportTcpDxe.inf,这是一个开源的模块,不过在编译过程中会报错,所以这里进行了移植,对应BeniPkg\Dxe\TransportTcpDxe\TcpTransportDxe.inf,而TestTcp.c模块就是调用了这个模块。

它将开启一个TCP服务端,可以通过TCP客户端(这里使用了Packet Sender,来自Packet Sender - Free utility to for sending / receiving of network packets. TCP, UDP, SSL.)来与它交互。

其运行结果如下:

在这里插入图片描述

这里接收数据并打印出来,所以能够在右边Shell下看到左边程序传递过来的数据。

注意这里的Address(192.168.3.128)和Port(1234)是硬编码的,需要根据实际情况修改。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在 Ubuntu 22.04 上安装 Linux、Apache、MySQL、PHP (LAMP) 堆栈

前些天发现了一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站。 如何在 Ubuntu 22.04 上安装 Linux、Apache、MySQL、PHP (LAMP) 堆栈 介绍 “LAMP”堆栈是一组开源软件&#…

Linux之进程管理篇(1)

初识进程 1.1 进程 进程&#xff1a;是具有独立功能的一次运行过程&#xff0c;是系统进行资源分配和调度的基本单位。Linux创建新进程时会为其指定和一个唯一的号码&#xff0c;即进程号(PID),以此区别不同的进程。进程不是程序&#xff08;程序&#xff1a;执行特定任务的一…

Istio

1、Istio介绍 Istio 是由 Google、IBM 和 Lyft 开源的微服务管理、保护和监控框架。 官网&#xff1a;https://istio.io/latest/zh/ 官方文档&#xff1a;https://istio.io/docs/ 中文官方文档&#xff1a;https://istio.io/zh/docs Github地址&#xff1a;https://github.com…

Kubernetes/k8s之包管理器helm

helm 在没有helm之前&#xff0c;我们要部署一个服务&#xff0c;deployment、service ingress 的作用通过打包的方式。把deployment、service ingress打包在一块&#xff0c;一键式部署服务。类似于yum功能。是官方提供的类似安装仓库的功能&#xff0c;可以实现一键化部署应…

Python量化交易- mplfinance库 -画K线图

mplfinance库 1. mplfinance 模块说明2. mplfinance安装3. mplfinance 模块 plot 基本用法参数typestylemake_addplot设置图表颜色 make_marketcolors添加图表样式 make_mpf_style 4. mplfinance 的基本K线图实现自定义风格和颜色图表尺寸调整、相关信息的显示添加完整移动平均…

C语言爬虫程序编写的爬取APP通用模板

互联网的飞快发展&#xff0c;尤其是手机终端业务的发展&#xff0c;让越来越多的事情都能通过手机来完成&#xff0c;电脑大部分的功能也都能通过手机实现&#xff0c;今天我就用C语言写一个手机APP类爬虫教程&#xff0c;方便后期拓展APP爬虫业务。而且这个模板是通用的适合各…

SIP12 脚模块式单路交流信号隔离变送器0~1VAC/0~5VAC转4-20mA/0-5VDC

概述&#xff1a; IPO AC系列模块式交流电压隔离变送器&#xff0c;能将输入的交流信号按比例转换成4~20mA标准信号, 输入为0~1Vrms等交流电压信号&#xff1b;输出为4~20mA直流电流信号或0~5VDC直流电压信号。实现辅助电源和输入输出信号之间2500VDC隔离&#xff0c;输入信号…

DevOps系列文章之 GitLab CI/CD

CICD是什么? 由于目前公司使用的gitlab&#xff0c;大部分项目使用的CICD是gitlab的CICD&#xff0c;少部分用的是jenkins&#xff0c;使用了gitlab-ci一段时间后感觉还不错&#xff0c;因此总结一下 介绍gitlab的CICD之前&#xff0c;可以先了解CICD是什么 我们的开发模式…

力扣刷MySQL-第五弹(详细讲解)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;力扣刷题讲解-MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出…

超级菜鸟怎么学习数据分析?

如果你有python入门基础&#xff0c;在考虑数据分析岗&#xff0c;这篇文章将带你了解&#xff1a;数据分析人才的薪资水平&#xff0c;数据人应该掌握的技术栈。 首先来看看&#xff0c;我在搜索数据分析招聘时&#xff0c;各大厂开出的薪资&#xff1a; 那各大厂在数据领域…

【Spring 篇】MyBatis多表操作:编织数据的交响乐

欢迎来到MyBatis的多表操作世界&#xff01;在这个充满交响乐的舞台上&#xff0c;我们将探索如何巧妙地编织多个数据表的数据&#xff0c;创造出一场旋律动听的数据交响曲。无需繁琐的SQL拼接&#xff0c;MyBatis让多表操作变得优雅而简单。让我们一起进入这个音乐殿堂&#x…

二.Winform使用Webview2在Demo1中实现地址简单校验

Winform使用Webview2在Demo1中实现地址简单校验 往期目录回顾添加对于的简单url验证提示通过上节和本节涉及到的函数有 往期目录 往期相关文章目录 专栏目录 回顾 通过一.Winform使用Webview2(Edge浏览器核心) 创建demo(Demo1)实现回车导航到指定地址 我们已经知道了解决资源…

裁员背景下 | 【2024年人生的里程碑】作为独立开发者,第一次承接外包项目的心得经历,也许说出你的心声哦!

&#x1f6aa; 每日一句 种子不落在肥土而落在瓦砾中&#xff0c;有生命力的种子决不会悲观和叹气&#xff0c;因为有了阻力才有磨炼。 背景介绍 在裁员背景下&#xff0c;寻找自主承包项目成为了一种可行且具有吸引力的选择。对于职业技术人员而言&#xff0c;自主承包项目不…

【服务器】Xshell与Xftp软件的使用指南

目录 【Xshell软件】 1.1 Xshell软件的功能 1.2 Xshell软件的使用 【Xftp软件】 2.1 Xftp软件的功能 2.2 Xftp软件的使用 可替代产品【FinalShell】 3.1 FinalShell软件的使用 3.2 FinalShell连接服务器失败解决方法 可替代产品【FileZilla】

暴力破解常见的服务器

目录 使用 pydictor 生成自己的字典工具liunx下载使用常用的参数说明插件型字典 (可自己根据 API 文档开发) 使用 hydra 工具在线破解系统用户密码使用 hydra 破解 windows 7 远程桌面密码使用 hydra 工具破解 ssh 服务 root 用户密码 使用 Medusa 工具在线破解medusa参数说明M…

STM32之RTC实时时钟

一、实时时钟概述 1、实时时钟介绍 英文缩写&#xff1a;RTC。显示年、月、日、时、分、秒、星期,自动计算闰年&#xff0c;能够区分每个月的天数。 RTC特点&#xff1a;能从RTC获取到具体的日期时间&#xff0c;断掉后再开机时间仍然准确&#xff08;需要纽扣电池&#xff…

JSON简单了解

文章目录 1、JSON介绍2、ES6模版字符串3、JS对象转化为JSON字符串3.1、手动JS对象转化为JSON字符串3.2、自动JS对象转化为JSON字符串 4、JS对象和java互相转换 1、JSON介绍 JSON 概念&#xff1a;JavaScript Object Notation。JavaScript 对象表示法&#xff0c;简单理解JSON是…

如何控制项目管理中的日程冲突?

《全球公司生产力报告》发现&#xff0c;62% 的公司领导表示&#xff0c;资源调度是他们在项目管理方面面临的最大挑战。其中&#xff0c;日程冲突是利用共享资源池管理多个项目的典型挑战。例如&#xff0c;团队成员参与的活动可能会重叠&#xff0c;也可能是任务分配给了无法…

设计模式篇---中介者模式

文章目录 概念结构实例总结 概念 中介者模式&#xff1a;用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显示地相互引用&#xff0c;从而使其耦合松散&#xff0c;而且可以独立地改变它们之间的交互。 就好比世界各个国家之间可能会产生冲突&#xff0c;但是当产…

乡镇县城小市场的外卖服务需求,现在是属于谁的机遇?

目前&#xff0c;县域经济正面临着几大利好。“返乡就业、返乡创业和告老还乡”现象越发普遍&#xff0c;这不仅在小县城中有所体现&#xff0c;同样在乡镇中也呈现出同样的趋势。一些产业链和工厂纷纷下沉到乡镇&#xff0c;带来了更多的就业机会。这不仅能够吸引年轻人回乡就…
最新文章