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

UDP4

UDP4协议说明

UDP的全称是User Datagram Protocol,它不提供复杂的控制机制,仅利用IP提供面向无连接的通信服务。它将上层应用程序发来的数据在收到的那一刻,立即按照原样发送到网络。

UDP报文格式:

在这里插入图片描述

各个参数说明如下:

字段长度(字节)描述
Source Port2发送端口,标识哪个应用程序发送(发送进程)。
Destination Port2目标端口,标识哪个应用程序接收(接收进程)。
Length2UDP首部加上UDP数据的字节数,最小为8。
Checksum2覆盖UDP首部和UDP数据,是可选的。
data octets变长UDP负载,可选的。

前面的四个参数对应到UEFI代码中就是UDP头部:

//
// UDP header definition
//
typedef struct {UINT16    SrcPort;UINT16    DstPort;UINT16    Length;UINT16    Checksum;
} EFI_UDP_HEADER;

UDP4代码综述

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

EFI_STATUS
EFIAPI
Udp4DriverEntryPoint (IN EFI_HANDLE        ImageHandle,IN EFI_SYSTEM_TABLE  *SystemTable)
{//// Install the Udp4DriverBinding and Udp4ComponentName protocols.//Status = EfiLibInstallDriverBindingComponentName2 (ImageHandle,SystemTable,&gUdp4DriverBinding,ImageHandle,&gUdp4ComponentName,&gUdp4ComponentName2);if (!EFI_ERROR (Status)) {//// Initialize the UDP random port.//mUdp4RandomPort = (UINT16)(((UINT16)NetRandomInitSeed ()) % UDP4_PORT_KNOWN + UDP4_PORT_KNOWN); // 宏的值是1024}
}

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

EFI_DRIVER_BINDING_PROTOCOL  gUdp4DriverBinding = {Udp4DriverBindingSupported,Udp4DriverBindingStart,Udp4DriverBindingStop,0xa,NULL,NULL
};

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

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

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

Udp4DriverBindingSupported

UDP4依赖于IP4:

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

Udp4DriverBindingStart

Start函数的流程大致如下:

  1. 初始化UDP4_SERVICE_DATA
  2. 安装gEfiUdp4ServiceBindingProtocolGuid

同其它驱动一样,重点也是结构体,这里就是UDP4_SERVICE_DATA

UDP4_SERVICE_DATA

UDP4_SERVICE_DATA在Start函数中创建:

EFI_STATUS
EFIAPI
Udp4DriverBindingStart (IN EFI_DRIVER_BINDING_PROTOCOL  *This,IN EFI_HANDLE                   ControllerHandle,IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath  OPTIONAL)
{Status = Udp4CreateService (Udp4Service, This->DriverBindingHandle, ControllerHandle);
}

其结构体定义如下:

typedef struct _UDP4_SERVICE_DATA_ {UINT32                          Signature;EFI_SERVICE_BINDING_PROTOCOL    ServiceBinding;EFI_HANDLE                      ImageHandle;EFI_HANDLE                      ControllerHandle;LIST_ENTRY                      ChildrenList;UINTN                           ChildrenNumber;IP_IO                           *IpIo;EFI_EVENT                       TimeoutEvent;
} UDP4_SERVICE_DATA;

相比之前的服务数据,这个结构体相当得简单,其中比较重要的成员有:

  • ServiceBinding:对应mUdp4ServiceBinding
EFI_SERVICE_BINDING_PROTOCOL  mUdp4ServiceBinding = {Udp4ServiceBindingCreateChild,Udp4ServiceBindingDestroyChild
};

用于创建UDP4子项。

  • ChildrenListChildrenNumber:对应UDP4_INSTANCE_DATA结构体,由Udp4ServiceBindingCreateChild()创建,是表示子项的结构体,在UDP4_INSTANCE_DATA会进一步介绍。
  • IpIo:它是对IP4实例的一个包装,UDP4通过它来进行通信,在IP_IO已经介绍过。在UDP4以及TCP4中会进场看到类似IP_IO这样的结构体,它们仅仅是对IP层的保证,之所以要有这样的包装,是因为存在IPv4和IPv6两个版本,而这里只关注IPv4。
  • TimeoutEvent:一个定时事件,创建的位置是在Udp4CreateService()函数中:
EFI_STATUS
Udp4CreateService (IN OUT UDP4_SERVICE_DATA  *Udp4Service,IN     EFI_HANDLE         ImageHandle,IN     EFI_HANDLE         ControllerHandle)
{//// Create the event for Udp timeout checking.//Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL,TPL_CALLBACK,Udp4CheckTimeout,Udp4Service,&Udp4Service->TimeoutEvent);//// Start the timeout timer event.//Status = gBS->SetTimer (Udp4Service->TimeoutEvent,TimerPeriodic,UDP4_TIMEOUT_INTERVAL // 50 milliseconds);

对应的回调函数Udp4CheckTimeout(),它用来检测接收到的报文是否过期,其主体代码:

VOID
EFIAPI
Udp4CheckTimeout (IN EFI_EVENT  Event,IN VOID       *Context)
{NET_LIST_FOR_EACH (Entry, &Udp4Service->ChildrenList) {//// Iterate all the instances belonging to this service context.//Instance = NET_LIST_USER_STRUCT (Entry, UDP4_INSTANCE_DATA, Link);NET_CHECK_SIGNATURE (Instance, UDP4_INSTANCE_DATA_SIGNATURE);if (!Instance->Configured || (Instance->ConfigData.ReceiveTimeout == 0)) {//// Skip this instance if it's not configured or no receive timeout.//continue;}NET_LIST_FOR_EACH_SAFE (WrapEntry, NextEntry, &Instance->RcvdDgramQue) {//// Iterate all the rxdatas belonging to this udp instance.//Wrap = NET_LIST_USER_STRUCT (WrapEntry, UDP4_RXDATA_WRAP, Link);//// TimeoutTick unit is microsecond, MNP_TIMEOUT_CHECK_INTERVAL unit is 100ns.//if (Wrap->TimeoutTick < (UDP4_TIMEOUT_INTERVAL / 10)) {//// Remove this RxData if it timeouts.//Udp4RecycleRxDataWrap (NULL, (VOID *)Wrap);} else {Wrap->TimeoutTick -= (UDP4_TIMEOUT_INTERVAL / 10);}}}
}

这里的Wrap对应结构体UDP4_RXDATA_WRAP

typedef struct _UDP4_RXDATA_WRAP_ {LIST_ENTRY               Link;NET_BUF                  *Packet;UINT32                   TimeoutTick;EFI_UDP4_RECEIVE_DATA    RxData;
} UDP4_RXDATA_WRAP;

它通过Udp4WrapRxData()创建,然后放到一个队列中供UDP驱动处理,如果来不及处理就会过期,而过期时间也由这里的成员TimeoutTick指定,而该成员由另外的一个值指定:

Wrap->TimeoutTick = Instance->ConfigData.ReceiveTimeout;

Instance是后面会介绍的UDP4_INSTANCE_DATA中的配置参数ConfigDataConfigData.ReceiveTimeout的值在创建时是-1,表示不会过期:

  //// use the -1 magic number to disable the receiving process of the ip instance.//Ip4ConfigData->ReceiveTimeout = (UINT32)(-1);

不过在UDP4的配置中可以修改:

EFI_STATUS
EFIAPI
Udp4Configure (IN EFI_UDP4_PROTOCOL     *This,IN EFI_UDP4_CONFIG_DATA  *UdpConfigData OPTIONAL)
{if (UdpConfigData != NULL) {if (Instance->Configured) {//// Save the reconfigurable parameters.//Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;}}
}

UDP4_INSTANCE_DATA

UDP4_INSTANCE_DATA表示一个UDP4子项,其它位于NetworkPkg\Udp4Dxe\Udp4Impl.h:

typedef struct _UDP4_INSTANCE_DATA_ {UINT32                  Signature;LIST_ENTRY              Link;UDP4_SERVICE_DATA       *Udp4Service;EFI_UDP4_PROTOCOL       Udp4Proto;EFI_UDP4_CONFIG_DATA    ConfigData;EFI_HANDLE              ChildHandle;BOOLEAN                 Configured;BOOLEAN                 IsNoMapping;NET_MAP                 TxTokens;NET_MAP                 RxTokens;NET_MAP                 McastIps;LIST_ENTRY              RcvdDgramQue;LIST_ENTRY              DeliveredDgramQue;UINT16                  HeadSum;EFI_STATUS              IcmpError;IP_IO_IP_INFO           *IpInfo;BOOLEAN                 InDestroy;
} UDP4_INSTANCE_DATA;

下面介绍其中比较重要的成员:

  • Udp4Service:指向UDP4服务的结构体。

  • Udp4Proto:对应EFI_UDP4_PROTOCOL,后面会进一步介绍。

  • ConfigData:UDP配置数据:

typedef struct {//// Receiving Filters//BOOLEAN             AcceptBroadcast;BOOLEAN             AcceptPromiscuous;BOOLEAN             AcceptAnyPort;BOOLEAN             AllowDuplicatePort;//// I/O parameters//UINT8               TypeOfService;UINT8               TimeToLive;BOOLEAN             DoNotFragment;UINT32              ReceiveTimeout;UINT32              TransmitTimeout;//// Access Point//BOOLEAN             UseDefaultAddress;EFI_IPv4_ADDRESS    StationAddress;EFI_IPv4_ADDRESS    SubnetMask;UINT16              StationPort;EFI_IPv4_ADDRESS    RemoteAddress;UINT16              RemotePort;
} EFI_UDP4_CONFIG_DATA;
  • TxTokensRxTokens:描述收发数据的映射:
typedef struct {LIST_ENTRY    Used;LIST_ENTRY    Recycled;UINTN         Count;
} NET_MAP;

真正的Token是EFI_UDP4_COMPLETION_TOKEN

typedef struct {EFI_EVENT     Event;EFI_STATUS    Status;union {EFI_UDP4_RECEIVE_DATA     *RxData;EFI_UDP4_TRANSMIT_DATA    *TxData;} Packet;
} EFI_UDP4_COMPLETION_TOKEN;
  • RcvdDgramQueDeliveredDgramQue:处理收发数据的队列。
  • IpInfo:底层IP4实例需要使用到的结构体:
///
/// The IP_IO_IP_INFO is used in IpIoSend() to override the default IP instance
/// in IP_IO.
///
typedef struct _IP_IO_IP_INFO {EFI_IP_ADDRESS               Addr;IP_IO_IP_MASK                PreMask;LIST_ENTRY                   Entry;EFI_HANDLE                   ChildHandle;IP_IO_IP_PROTOCOL            Ip;IP_IO_IP_COMPLETION_TOKEN    DummyRcvToken;INTN                         RefCnt;UINT8                        IpVersion;
} IP_IO_IP_INFO;

从注释中可以看到发送数据时会使用到。

EFI_UDP4_PROTOCOL

UDP4通信的接口,该Protocol的结构体如下:

///
/// The EFI_UDP4_PROTOCOL defines an EFI UDPv4 Protocol session that can be used
/// by any network drivers, applications, or daemons to transmit or receive UDP packets.
/// This protocol instance can either be bound to a specified port as a service or
/// connected to some remote peer as an active client. Each instance has its own settings,
/// such as the routing table and group table, which are independent from each other.
///
struct _EFI_UDP4_PROTOCOL {EFI_UDP4_GET_MODE_DATA    GetModeData;EFI_UDP4_CONFIGURE        Configure;EFI_UDP4_GROUPS           Groups;EFI_UDP4_ROUTES           Routes;EFI_UDP4_TRANSMIT         Transmit;EFI_UDP4_RECEIVE          Receive;EFI_UDP4_CANCEL           Cancel;EFI_UDP4_POLL             Poll;
};

对应的实现:

EFI_UDP4_PROTOCOL  mUdp4Protocol = {Udp4GetModeData,Udp4Configure,Udp4Groups,Udp4Routes,Udp4Transmit,Udp4Receive,Udp4Cancel,Udp4Poll
};

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

Udp4.GetModeData

对应的实现是Udp4GetModeData(),其代码实现:

EFI_STATUS
EFIAPI
Udp4GetModeData (IN  EFI_UDP4_PROTOCOL                *This,OUT EFI_UDP4_CONFIG_DATA             *Udp4ConfigData OPTIONAL,OUT EFI_IP4_MODE_DATA                *Ip4ModeData    OPTIONAL,OUT EFI_MANAGED_NETWORK_CONFIG_DATA  *MnpConfigData  OPTIONAL,OUT EFI_SIMPLE_NETWORK_MODE          *SnpModeData    OPTIONAL)
{if (Udp4ConfigData != NULL) {//// Set the Udp4ConfigData.//CopyMem (Udp4ConfigData, &Instance->ConfigData, sizeof (*Udp4ConfigData));}Ip = Instance->IpInfo->Ip.Ip4;//// Get the underlying Ip4ModeData, MnpConfigData and SnpModeData.//Status = Ip->GetModeData (Ip, Ip4ModeData, MnpConfigData, SnpModeData);}

从这里可以看出,上层的网络协议可以获取到下层所有的模式数据。

对于UDP4来说,数据在UDP4_INSTANCE_DATAConfigData成员中。

Udp4.Configure

对应的实现是Udp4Configure(),其代码实现:

EFI_STATUS
EFIAPI
Udp4Configure (IN EFI_UDP4_PROTOCOL     *This,IN EFI_UDP4_CONFIG_DATA  *UdpConfigData OPTIONAL)
{// 根据是否有配置存在两种情况,没有数据相当于重置if (UdpConfigData != NULL) {if (Instance->Configured) {//// The instance is already configured, try to do the re-configuration.//if (!Udp4IsReconfigurable (&Instance->ConfigData, UdpConfigData)) {//// If the new configuration data wants to change some unreconfigurable// settings, return EFI_ALREADY_STARTED.//Status = EFI_ALREADY_STARTED;goto ON_EXIT;}//// Save the reconfigurable parameters.//Instance->ConfigData.TypeOfService   = UdpConfigData->TypeOfService;Instance->ConfigData.TimeToLive      = UdpConfigData->TimeToLive;Instance->ConfigData.DoNotFragment   = UdpConfigData->DoNotFragment;Instance->ConfigData.ReceiveTimeout  = UdpConfigData->ReceiveTimeout;Instance->ConfigData.TransmitTimeout = UdpConfigData->TransmitTimeout;} else {//// Construct the Ip configuration data from the UdpConfigData.//Udp4BuildIp4ConfigData (UdpConfigData, &Ip4ConfigData);//// Configure the Ip instance wrapped in the IpInfo.//Status = IpIoConfigIp (Instance->IpInfo, &Ip4ConfigData);if (EFI_ERROR (Status)) {if (Status == EFI_NO_MAPPING) {Instance->IsNoMapping = TRUE;}goto ON_EXIT;}Instance->IsNoMapping = FALSE;//// Save the configuration data.//CopyMem (&Instance->ConfigData, UdpConfigData, sizeof (Instance->ConfigData));IP4_COPY_ADDRESS (&Instance->ConfigData.StationAddress, &Ip4ConfigData.StationAddress);IP4_COPY_ADDRESS (&Instance->ConfigData.SubnetMask, &Ip4ConfigData.SubnetMask);//// Try to allocate the required port resource.//Status = Udp4Bind (&Udp4Service->ChildrenList, &Instance->ConfigData);if (EFI_ERROR (Status)) {//// Reset the ip instance if bind fails.//IpIoConfigIp (Instance->IpInfo, NULL);goto ON_EXIT;}//// Pre calculate the checksum for the pseudo head, ignore the UDP length first.//CopyMem (&LocalAddr, &Instance->ConfigData.StationAddress, sizeof (IP4_ADDR));CopyMem (&RemoteAddr, &Instance->ConfigData.RemoteAddress, sizeof (IP4_ADDR));Instance->HeadSum = NetPseudoHeadChecksum (LocalAddr,RemoteAddr,EFI_IP_PROTO_UDP,0);Instance->Configured = TRUE;}} else {//// UdpConfigData is NULL, reset the instance.//Instance->Configured  = FALSE;Instance->IsNoMapping = FALSE;//// Reset the Ip instance wrapped in the IpInfo.//IpIoConfigIp (Instance->IpInfo, NULL);//// Cancel all the user tokens.//Instance->Udp4Proto.Cancel (&Instance->Udp4Proto, NULL);//// Remove the buffered RxData for this instance.//Udp4FlushRcvdDgram (Instance);}
}

根据输入参数的不同,以及是否已经配置过,会走到不同的流程,此外,UDP4还会配置进一步调用IP4的接口进行配置。

Udp4.Transmit

对应的实现是Udp4Transmit(),其代码实现:

EFI_STATUS
EFIAPI
Udp4Transmit (IN EFI_UDP4_PROTOCOL          *This,IN EFI_UDP4_COMPLETION_TOKEN  *Token)
{//// Validate the Token, if the token is invalid return the error code.//Status = Udp4ValidateTxToken (Instance, Token);if (EFI_ERROR (Status)) {goto ON_EXIT;}if (EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token)) ||EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token))){//// Try to find a duplicate token in the two token maps, if found, return// EFI_ACCESS_DENIED.//Status = EFI_ACCESS_DENIED;goto ON_EXIT;}TxData = Token->Packet.TxData;//// Create a net buffer to hold the user buffer and the udp header.//Packet = NetbufFromExt ((NET_FRAGMENT *)TxData->FragmentTable,TxData->FragmentCount,UDP4_HEADER_SIZE,0,Udp4NetVectorExtFree,NULL);if (Packet == NULL) {Status = EFI_OUT_OF_RESOURCES;goto ON_EXIT;}//// Store the IpIo in ProtoData.//Udp4Service                       = Instance->Udp4Service;*((UINTN *)&Packet->ProtoData[0]) = (UINTN)(Udp4Service->IpIo);Udp4Header = (EFI_UDP_HEADER *)NetbufAllocSpace (Packet, UDP4_HEADER_SIZE, TRUE);ASSERT (Udp4Header != NULL);ConfigData = &Instance->ConfigData;//// Fill the udp header.//Udp4Header->SrcPort  = HTONS (ConfigData->StationPort);Udp4Header->DstPort  = HTONS (ConfigData->RemotePort);Udp4Header->Length   = HTONS ((UINT16)Packet->TotalSize);Udp4Header->Checksum = 0;UdpSessionData = TxData->UdpSessionData;IP4_COPY_ADDRESS (&Override.Ip4OverrideData.SourceAddress, &ConfigData->StationAddress);if (UdpSessionData != NULL) {//// Set the SourceAddress, SrcPort and Destination according to the specified// UdpSessionData.//if (!EFI_IP4_EQUAL (&UdpSessionData->SourceAddress, &mZeroIp4Addr)) {IP4_COPY_ADDRESS (&Override.Ip4OverrideData.SourceAddress, &UdpSessionData->SourceAddress);}if (UdpSessionData->SourcePort != 0) {Udp4Header->SrcPort = HTONS (UdpSessionData->SourcePort);}if (UdpSessionData->DestinationPort != 0) {Udp4Header->DstPort = HTONS (UdpSessionData->DestinationPort);}CopyMem (&Source, &Override.Ip4OverrideData.SourceAddress, sizeof (IP4_ADDR));CopyMem (&Destination, &UdpSessionData->DestinationAddress, sizeof (IP4_ADDR));//// calculate the pseudo head checksum using the overridden parameters.//HeadSum = NetPseudoHeadChecksum (Source,Destination,EFI_IP_PROTO_UDP,0);} else {//// UdpSessionData is NULL, use the address and port information previously configured.//CopyMem (&Destination, &ConfigData->RemoteAddress, sizeof (IP4_ADDR));HeadSum = Instance->HeadSum;}//// calculate the checksum.//Udp4Header->Checksum = Udp4Checksum (Packet, HeadSum);if (Udp4Header->Checksum == 0) {//// If the calculated checksum is 0, fill the Checksum field with all ones.//Udp4Header->Checksum = 0xffff;}//// Fill the IpIo Override data.//if (TxData->GatewayAddress != NULL) {IP4_COPY_ADDRESS (&Override.Ip4OverrideData.GatewayAddress, TxData->GatewayAddress);} else {ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));}Override.Ip4OverrideData.Protocol      = EFI_IP_PROTO_UDP;Override.Ip4OverrideData.TypeOfService = ConfigData->TypeOfService;Override.Ip4OverrideData.TimeToLive    = ConfigData->TimeToLive;Override.Ip4OverrideData.DoNotFragment = ConfigData->DoNotFragment;//// Save the token into the TxToken map.//Status = NetMapInsertTail (&Instance->TxTokens, Token, Packet);//// Send out this datagram through IpIo.//IpDestAddr.Addr[0] = Destination;Status             = IpIoSend (Udp4Service->IpIo,Packet,Instance->IpInfo,Instance,Token,&IpDestAddr,&Override);
}

Udp4.Receive

对应的实现是Udp4Receive(),其代码实现:

EFI_STATUS
EFIAPI
Udp4Receive (IN EFI_UDP4_PROTOCOL          *This,IN EFI_UDP4_COMPLETION_TOKEN  *Token)
{if (EFI_ERROR (NetMapIterate (&Instance->RxTokens, Udp4TokenExist, Token)) ||EFI_ERROR (NetMapIterate (&Instance->TxTokens, Udp4TokenExist, Token))){//// Return EFI_ACCESS_DENIED if the specified token is already in the TxTokens or// RxTokens map.//Status = EFI_ACCESS_DENIED;goto ON_EXIT;}Token->Packet.RxData = NULL;//// Save the token into the RxTokens map.//Status = NetMapInsertTail (&Instance->RxTokens, Token, NULL);if (EFI_ERROR (Status)) {Status = EFI_NOT_READY;goto ON_EXIT;}//// If there is an icmp error, report it.//Udp4ReportIcmpError (Instance);//// Try to deliver the received datagrams.//Udp4InstanceDeliverDgram (Instance);//// Dispatch the DPC queued by the NotifyFunction of Token->Event.//DispatchDpc ();
}

同其它的网络协议中的Receive一样,重点是注册Token。

Udp4.Poll

对应的实现是Udp4Poll(),其代码实现就是调用下一层的Poll:

EFI_STATUS
EFIAPI
Udp4Poll (IN EFI_UDP4_PROTOCOL  *This)
{Ip       = Instance->IpInfo->Ip.Ip4;//// Invode the Ip instance consumed by the udp instance to do the poll operation.//return Ip->Poll (Ip);
}

代码示例

DHCP和DNS等都是使用UDP的,后面会进一步说明。

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

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

相关文章

基础+常用的数据结构

基础 java基础 JDK 和 JRE JDK&#xff0c;它是功能齐全的 Java SDK&#xff0c;是提供给开发者使用&#xff0c;能够创建和编译 Java 程序的开发套件。它包含了 JRE,同时还包含了编译 java 源码的编译器 javac 以及一些其他工具比如 javadoc&#xff08;文档注释工具&#…

舵机使用总结

文章目录 1 舵机简介2 注意事项3 编写驱动程序3.1 使用STM32作为控制器3.1.1 计算高电平对应程序中的取值范围3.1.2 编写控制程序 1 舵机简介 舵机使用PWM控制&#xff0c;周期为20ms&#xff0c;通过改变高电平占空比来驱动&#xff0c;高电平通常为1~2ms&#xff08; 或 0.5 …

Find My卡片正成为消费电子香饽饽,伦茨科技ST17H6x可以帮到您

今年CES许多公司发布支持苹果Find My的卡片产品&#xff0c;这种产品轻薄可充电&#xff0c;放在钱包、背包或者手提包可以防丢查找&#xff0c;在智能化加持下&#xff0c;防丢卡片使得人们日益关心自行车的去向。最新的防丢卡片与苹果Find My结合&#xff0c;智能防丢&#x…

vue生命周期图示

详见&#xff1a;官网介绍

聊聊Java虚拟机(一)—— 类加载子系统

1. 前言 ​ 虚拟机就是一款用来执行虚拟计算机指令的计算机软件。它相当于一台虚拟计算机。大体上&#xff0c;虚拟机分为系统虚拟机和程序虚拟机。系统虚拟机就相当于一台物理电脑&#xff0c;里面可以安装操作系统&#xff1b;程序虚拟机是为了执行单个计算机程序而设计出来…

海外媒体发稿:出口贸易媒体发稿推广8种方式让您事半功倍-华媒舍

出口贸易已成为越来越多企业的发展方向。要让更多潜在客户了解并选择你的产品&#xff0c;仅靠传统的销售手段已远远不够。作为一名出口贸易从业人员&#xff0c;如何利用媒体发稿推广&#xff0c;事半功倍地扩大市场影响&#xff0c;成为摆在我们面前的一大任务。本文将为您介…

Vulnhub-TECH_SUPP0RT: 1渗透

文章目录 一、前言1、靶机ip配置2、渗透目标3、渗透概括 开始实战一、信息获取二、使用smb服务获取信息三、密码破解四、获取webshell五、反弹shell六、web配置文件获取信息七、提权 一、前言 由于在做靶机的时候&#xff0c;涉及到的渗透思路是非常的广泛&#xff0c;所以在写…

【c++】C++输入输出

C输入&输出 新生婴儿会以自己独特的方式向这个崭新的世界打招呼&#xff0c;C刚出来后&#xff0c;也算是一个新事物&#xff0c;那C是否也应该向这个美好的世界来声问候呢&#xff1f;我们来看下C是如何来实现问候的 #include<iostream> // std是C标准库的命名空间名…

全网最高质量文章:重新学习Java中的HashMap!!

前言 本文参考了美团技术团队的科普文章Java 8系列之重新认识HashMap - 知乎 (zhihu.com) 这篇文章的质量极其高&#xff0c;高到很有可能是全网介绍HashMap这个知识点最优秀的文章&#xff0c;没有之一&#xff01;&#xff01;&#xff01;因此&#xff0c;我决定在我自己的…

idea消除代码区黄色警告

文章目录 前言一、修改配置总结 前言 idea的检查项较多&#xff0c;导致警告比较多看上去很不雅观。 一、修改配置 在idea中打开Settings 取消Warning和Weak Warning的勾选 总结 回到顶部

多线程-线程状态和线程安全(加锁-synchronized 关键字)

目录 1.线程状态 示例&#xff1a; 1.1线程状态和状态转移的意义 2.线程安全 2.1观察线程不安全 2.2线程不安全的原因 3.synchronized 关键字 - 监视器锁 monitor lock 3.1synchronized 的特性 1. 互斥 2.可重⼊ 应用示例&#xff1a; 3.2synchronized 使⽤⽰例 1.…

conda国内加速

1、配置国内源 conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ 2、显示源地址 conda config --set show_channel_urls yes

【Arduino】基于 I2C 模块通过 I2C 接口驱动 LCD1602 字符液晶显示模块显示文本:只需两个引脚即可控制 LCD1602 液晶屏

引言 LCD1602是一种16列2行的字符液晶显示模块&#xff0c;常用于Arduino等嵌入式系统的用户接口。为了简化连接和编程&#xff0c;我们将使用I2C接口&#xff0c;这只需要Arduino的两个模拟输入引脚。 步骤 安装 LiquidCrystal_I2C 库 为了在Arduino中使用I2C模块驱动LCD显…

Linux的Shell程序(全面超详细的介绍)

文章目录 前言1.Shell概述1.1概述 2.Shell解析器3.Shell脚本入门4.Shell中的变量4.1 系统变量4.2 自定义变量4.3 特殊变量&#xff1a;$n4.4 特殊变量&#xff1a;$#4.5 特殊变量&#xff1a;\$*、$4.6 特殊变量&#xff1a;$&#xff1f; 5.运算符6.条件判断7.流程控制7.1 if …

Hive-SQL语法大全

Hive SQL 语法大全 基于语法描述说明 CREATE DATABASE [IF NOT EXISTS] db_name [LOCATION] path; SELECT expr, ... FROM tbl ORDER BY col_name [ASC | DESC] (A | B | C)如上语法&#xff0c;在语法描述中出现&#xff1a; []&#xff0c;表示可选&#xff0c;如上[LOCATI…

【操作系统和计网从入门到深入】(五)软硬链接和动静态库

前言 这个专栏其实是博主在复习操作系统和计算机网络时候的笔记&#xff0c;所以如果是博主比较熟悉的知识点&#xff0c;博主可能就直接跳过了&#xff0c;但是所有重要的知识点&#xff0c;在这个专栏里面都会提到&#xff01;而且我也一定会保证这个专栏知识点的完整性&…

通信入门系列——连续卷积定理、循环卷积、离散卷积定理

本节目录 一、连续卷积定理 1、时域卷积定理 2、频域卷积定理 二、循环卷积 三、离散卷积定理本节内容 一、连续卷积定理 卷积定理在信号分析中占有重要的地位&#xff0c;包括时域卷积定理和频域卷积定理。在信号分析领域&#xff0c;通常采用基于卷积定理的时频域分析&#…

Zuul1.x 高并发下阻塞分析以及解决方案

背景 由于最近博主在压测接口的时候发现我接口出现卡死状态&#xff0c;最开始以为是我自己接口出现问题&#xff0c;单独压测我自己的服务&#xff08;不经过网关&#xff09;200/qps/10 次循环 是没问题&#xff0c;但是加上网关&#xff08;zuul 1.x&#xff09; 去发现 经…

编曲学习:Cubase12导入Cubasis工程的方法!

Steinberg 发布 Cubasis 3 项目导入器&#xff0c;可将 Cubasis 的项目导入到 Cubase 使用https://m.midifan.com/news_body.php?id35635 我偶然看到这个文章&#xff0c;不过发现Cubase12默认好像没有这个选项&#xff0c;心想着要是移动端能和PC端同步&#xff0c;感觉会挺…

【网站项目】基于jsp的199旅游景点管理系统

&#x1f64a;作者简介&#xff1a;多年一线开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…