Telephony中ITelephony的AIDL调用关系

以Android14.0源码讲解

ITelephony来自framework下的com.android.internal.telephony包下

frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl

这个接口用于与Phone交互的界面,主要由TelephonyManager类使用,一些地方仍在直接使用它,如果可能的话,请清理它们,并使用TelephonyManager。意思就是不让你直接使用ITelephony 接口,要用里面的方法的话就使用TelephonyManager来获取。

 /**
94   * Interface used to interact with the phone.  Mostly this is used by the
95   * TelephonyManager class.  A few places are still using this directly.
96   * Please clean them up if possible and use TelephonyManager instead.
97   *
98   * {@hide}
99   */
100  interface ITelephony {

里面包含了384个方法,我们找一些常见的方法来学习。

1.通话相关

 /**/*拨一个号码。这不会打电话。它显示拨号程序屏幕。*/
108      @UnsupportedAppUsage
109      void dial(String number);
110  
111      /**拨打指定号码。*/
116      @UnsupportedAppUsage
117      void call(String callingPackage, String number);
350  
351      /**返回特定Subscription的Call State。*/
354      int getCallStateForSubscription(int subId, String callingPackage, String featureId);

之前Android9.0还能 endCall(),answerRingingCall(),isRinging(String callingPackage)等可以控制通话的这些方法已经被移除了,只能获取通话的相关状态了。

在 Android 14 中,ITelephony 接口中的一些与呼叫控制相关的方法,如 endCall() 和 answerRingingCall(),被移除的原因主要是为了增强用户隐私和安全性,并遵循更严格的权限管理政策。以下是移除这些方法背后的主要原因:

  1. 用户隐私和安全
    Android 操作系统近年来越来越重视用户隐私和安全性。方法如 endCall() 和 answerRingingCall(),允许应用程序以编程方式挂断电话或接听来电,这涉及对电话呼叫进行直接控制。虽然这些方法过去在某些场景下(例如自动应答、自动挂断电话)很有用,但它们也存在潜在的隐私风险:

滥用风险:应用可以在用户不知情或未授权的情况下控制呼叫,导致隐私问题。例如,某些恶意应用可以滥用这些API来挂断重要的电话或在不需要用户参与的情况下接听电话。
侵犯用户选择:这些方法绕过了用户的决定,让应用能够控制电话呼叫流程,而不经过用户的同意或操作。
为了解决这些问题,Google 在 Android 14 中移除了这些容易被滥用的方法,进一步保护用户的隐私。

  1. 权限管理的演变
    随着 Android 的发展,Google 越来越强调权限的精细化管理,以确保用户对应用行为的更大控制。过去,某些方法可能只需要少量的权限(例如 READ_PHONE_STATE),但实际上,它们赋予应用极高的权力来控制用户的电话。

为了解决这种过度授权的问题,Google 逐步将与电话呼叫相关的操作限制给了系统应用或经过特别授权的应用,而普通第三方应用程序则无法再获得这些权限。

  1. 推动使用更安全的API
    Google 提供了更安全且受限的替代方案来处理呼叫控制功能。例如,通过官方的 TelecomManager API,应用程序可以注册为默认电话应用来控制电话呼叫。要成为默认电话应用,用户必须显式授予权限,这样能够更好地避免未经授权的操作。这种设计确保了只有用户明确选择的应用程序才能执行类似的操作,减少滥用的可能性。

例如:
TelecomManager API:可以用来控制电话呼叫,但仅限于注册为默认电话应用的程序,并且权限控制更严格。

TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE);
telecomManager.endCall();  // 需要应用是默认电话应用
CallScreeningService:用于筛选和监控来电,同样受限于特定的系统权限。
  1. 限制低层次的接口调用
    ITelephony 是 Android 系统中的一个底层接口,通常不面向普通应用程序开发者,而是为系统应用或有特殊权限的应用设计的。过去,一些开发者通过反射等手段调用这些内部API,而这些API绕过了系统层级的限制和权限检查。在 Android 14 中,Google 通过移除这些方法,加强了对底层接口的封装,减少了未授权调用的风险。

  2. 提高系统稳定性
    随着 Android 系统的演变,Google 也希望简化并优化底层系统接口,以提高系统的稳定性和一致性。减少对底层 ITelephony API 的依赖,可以避免未来版本中出现的不兼容性问题,并减少系统维护的复杂度。

2.网络模式相关

553      /*返回subId的数据网络类型*/
559      int getDataNetworkTypeForSubscriber(int subId, String callingPackage,String callingFeatureId);561      /** 返回subId的语音网络类型*/
569      int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage,String callingFeatureId);902       /*将网络选择模式设置为自动。*/
905      void setNetworkSelectionModeAutomatic(int subId);//让radio连接到输入网络,并将选择模式更改为手动。boolean setNetworkSelectionModeManual(int subId, in OperatorInfo operatorInfo, boolean persisSelection);//执行radio扫描并返回可用网络列表。也就是 获取搜网后的结果CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage,String callingFeatureId);//发起搜网请求
931      int requestNetworkScan(int subId, in boolean renounceFineLocationAccess,
932              in NetworkScanRequest request, in Messenger messenger, in IBinder binder,
933  	    in String callingPackage, String callingFeatureId);//停止搜网
941      void stopNetworkScan(int subId, int scanId);

设置网络模式(即,4G/3G/2G, 3G/2G, 2G only等) setPreferredNetworkType(int subId, int networkType);方法也移除了,普通应用不再能够直接更改这些设置。目前,只有具有 系统级权限 或 运营商特权 的应用程序才可能具有修改网络模式的权限。

3.手机相关

/*getDeviceId(String callingPackage);方法已经废弃使用getDeviceIdWithFeature代替,
返回Phone的唯一设备ID,例如IMEI GSM 和 CDMA phone的MEID。如果设备ID不可用,则返回null。*/
1317      String getDeviceIdWithFeature(String callingPackage, String callingFeatureId);//通过slot 获取IMEI
1328      String getImeiForSlot(int slotIndex, String callingPackage, String callingFeatureId);//返回给定插槽的MEID。
1356      String getMeidForSlot(int slotIndex, String callingPackage, String callingFeatureId);// 获取设备软件版本号
1374      String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage,
1375              String callingFeatureId);

IMEI:用于 GSM 和 LTE 网络的 15 位数字唯一标识符,广泛应用于全球。
MEID:主要用于 CDMA 网络的 14 位十六进制标识符。常见于美国和一些其他地区的移动通信网络。CDMA 网络由运营商如 Verizon(美国)或 KDDI(日本)使用。

在现代智能手机上,特别是双模设备(支持 GSM 和 CDMA 网络)的手机,可能同时有 IMEI 和 MEID 号码,分别用于不同的网络协议。随着 LTE 网络的发展,CDMA 技术的使用逐渐减少,因此 IMEI 逐渐成为全球统一的设备标识方法。

4.SIM卡相关

//提供一个pin来解锁特定subId的SIM卡。
163      boolean supplyPinForSubscriber(int subId, String pin);//提供puk以解锁SIM卡,并将SIM卡pin设置为新pin。
173      boolean supplyPukForSubscriber(int subId, String puk, String pin);// 判断是否插入了Sim卡
575      @UnsupportedAppUsage
576      boolean hasIccCard();//获取默认eUICC卡的卡ID。如果没有eUICC,则返回{@link#INVALID_CARD_ID}。
1732      int getCardIdForDefaultEuicc(int subId, String callingPackage);

接下来看一下这些方法是如何实现的?

查看源码,PhoneInterfaceManager类继承了接口ITelephony.Stub ,相当于实现了ITelephony的所有方法。
在这里插入图片描述
先了解一下AIDL机制:

那为什么源码中PhoneInterfaceManager 可以 extends ITelephony.Stub 接口?在 Java 和 Android 中,PhoneInterfaceManager 类通过 extends 继承 ITelephony.Stub 的情况,其实并不是简单的继承接口。这里涉及到了AIDL(Android Interface Definition Language)的特殊机制。1. AIDL(Android Interface Definition Language)
在 Android 中,AIDL 是一种允许不同进程之间通信的机制,常用于实现跨进程调用(IPC)。当您定义一个 AIDL 接口时,系统会自动为您生成一个 Stub 类。这些生成的代码通常会继承 android.os.Binder 类,并实现您定义的接口方法。例如,假设 ITelephony.aidl 是一个定义了远程接口的 AIDL 文件:interface ITelephony {void setPreferredNetworkType(int type);
}
在编译时,Android SDK 会自动生成一个 ITelephony.Stub 类,这个类是 Binder 的子类,并且它实现了 ITelephony 接口。Stub 类有助于在服务器端处理来自客户端的远程方法调用。2. ITelephony.Stub 是什么?
ITelephony.Stub 是 Android 自动生成的一个 抽象类,它既实现了 ITelephony 接口的所有抽象方法,又继承了 Binder 类。ITelephony 接口:定义了远程接口的行为(如 setPreferredNetworkType())。
ITelephony.Stub 类:这个抽象类实现了 IBinder 接口,并负责处理客户端的远程调用请求。它还提供了方法用于将远程请求分发给实际的 PhoneInterfaceManager 实现。
简而言之,Stub 类是 Binder 的子类,封装了 AIDL 中的绑定逻辑,用来处理跨进程调用。3. 为什么 PhoneInterfaceManager 可以 extends ITelephony.Stub?
由于 ITelephony.Stub 是一个抽象类,而不是一个接口,Java 中允许类继承另一个类。因此,PhoneInterfaceManager 继承 ITelephony.Stub 是合理的:java
public class PhoneInterfaceManager extends ITelephony.Stub {@Overridepublic void setPreferredNetworkType(int type) {// 具体的实现逻辑}
}
4. ITelephony.Stub 和 IPC 通信
PhoneInterfaceManager 继承 ITelephony.Stub 是为了处理来自其他进程的远程调用。由于 Android 系统中的很多服务(如电话、消息等)运行在单独的进程中,它们需要通过 AIDL 机制提供远程调用接口给其他进程使用。通过继承 ITelephony.Stub,PhoneInterfaceManager 类能够作为服务端,处理来自客户端(如应用层)发起的远程调用请求。5. 总结
PhoneInterfaceManager 继承 ITelephony.Stub,是因为 Stub 实际上是一个 AIDL 自动生成的 抽象类,它既实现了接口,又继承了 Binder 类,负责管理远程调用。
AIDL 机制允许跨进程调用,Stub 类帮助处理这些调用,因此 PhoneInterfaceManager 继承了 Stub,从而可以提供远程方法调用的服务。
这不是普通的类继承接口的情况,而是 AIDL 机制下的 自动生成类的继承。

看完小插曲,回到PhoneInterfaceManager类的学习,通过查看这个所在路径packages/services/Telephony/src/com/android/phone/PhoneInterfaceManager.java,发现它是运行在phone进程下的,它是PhoneGlobals在onCreate方法中初始化的 phoneMgr = PhoneInterfaceManager.init(this),并且这个只能初始化一次。

2439      /**
2440       * Initialize the singleton PhoneInterfaceManager instance.
2441       * This is only done once, at startup, from PhoneApp.onCreate().
2442       */
2443      /* package */ static PhoneInterfaceManager init(PhoneGlobals app) {
2444          synchronized (PhoneInterfaceManager.class) {
2445              if (sInstance == null) {
2446                  sInstance = new PhoneInterfaceManager(app);
2447              } else {
2448                  Log.wtf(LOG_TAG, "init() called multiple times!  sInstance = " + sInstance);
2449              }
2450              return sInstance;
2451          }
2452      }
2453  
2454      /** Private constructor; @see init() */
2455      private PhoneInterfaceManager(PhoneGlobals app) {
2456          mApp = app; //得到PhoneGlobals的引用
2457          mCM = PhoneGlobals.getInstance().mCM; //CallManager引用
2458          mImsResolver = ImsResolver.getInstance(); //获取ImsResolver实例对象
2459          mSatelliteController = SatelliteController.getInstance();//获取SatelliteController实例对象
2460          mUserManager = (UserManager) app.getSystemService(Context.USER_SERVICE);
2461          mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
2462          mMainThreadHandler = new MainThreadHandler(); //获取MainThreadHandler对象
2463          mTelephonySharedPreferences = PreferenceManager.getDefaultSharedPreferences(mApp);
2464          mNetworkScanRequestTracker = new NetworkScanRequestTracker();
2465          mPhoneConfigurationManager = PhoneConfigurationManager.getInstance();
2466          mRadioInterfaceCapabilities = RadioInterfaceCapabilityController.getInstance();
2467          mNotifyUserActivity = new AtomicBoolean(false);
2468          PropertyInvalidatedCache.invalidateCache(TelephonyManager.CACHE_KEY_PHONE_ACCOUNT_TO_SUBID);
2469          publish();
2470          CarrierAllowListInfo.loadInstance(mApp);
2471      }7      private void publish() {
2488          if (DBG) log("publish: " + this);
2489  //将当前对象注册为 Telephony 服务,使其可以被系统访问和使用。ServiceManager.addService("phone", this);
2490          TelephonyFrameworkInitializer
2491                  .getTelephonyServiceManager()
2492                  .getTelephonyServiceRegisterer()
2493                  .register(this);
2494      }

先看getCallStateForSubscription方法是如何实现的?

3219      @Override
3220      public int getCallStateForSubscription(int subId, String callingPackage, String featureId) {//判断权限
3221          if (CompatChanges.isChangeEnabled(
3222                  TelecomManager.ENABLE_GET_CALL_STATE_PERMISSION_PROTECTION,
3223                  Binder.getCallingUid())) {
3224              // Check READ_PHONE_STATE for API version 31+
3225              if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(mApp, subId, callingPackage,
3226                      featureId, "getCallStateForSubscription")) {
3227                  throw new SecurityException("getCallState requires READ_PHONE_STATE for apps "
3228                          + "targeting API level 31+.");
3229              }
3230          }
3231          final long identity = Binder.clearCallingIdentity();
3232          try {
3233              Phone phone = getPhone(subId);	//返回CALL_STATE状态
3234              return phone == null ? TelephonyManager.CALL_STATE_IDLE :
3235                      PhoneConstantConversions.convertCallState(phone.getState());
3236          } finally {
3237              Binder.restoreCallingIdentity(identity);
3238          }
3239      }

把TelephonyManager.CALL_STATE_*转成PhoneConstants.State

frameworks/base/telephony/java/com/android/internal/telephony/PhoneConstantConversions.java39      /**
40       * Convert the TelephonyManager.CALL_STATE_* constants into the
41       * {@link PhoneConstants.State} enum for the public API.
42       */
43      public static PhoneConstants.State convertCallState(int state) {
44          switch (state) {
45              case TelephonyManager.CALL_STATE_RINGING:
46                  return PhoneConstants.State.RINGING;
47              case TelephonyManager.CALL_STATE_OFFHOOK:
48                  return PhoneConstants.State.OFFHOOK;
49              default:
50                  return PhoneConstants.State.IDLE;
51          }
52      }
53  

再来看一个getDataNetworkTypeForSubscriber方法

5361      @Override
5362      public int getDataNetworkTypeForSubscriber(int subId, String callingPackage,
5363              String callingFeatureId) {
5364          String functionName = "getDataNetworkTypeForSubscriber";
5365          if (!TelephonyPermissions.checkCallingOrSelfReadNonDangerousPhoneStateNoThrow(
5366                  mApp, functionName)) {
5367              if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
5368                      mApp, subId, callingPackage, callingFeatureId, functionName)) {
5369                  return TelephonyManager.NETWORK_TYPE_UNKNOWN;
5370              }
5371          }
5372  
5373          final long identity = Binder.clearCallingIdentity();
5374          try {
5375              final Phone phone = getPhone(subId);//也是通过phone对象来获取DataNetworkType
5376              if (phone != null) {
5377                  return phone.getServiceState().getDataNetworkType();
5378              } else {
5379                  return TelephonyManager.NETWORK_TYPE_UNKNOWN;
5380              }
5381          } finally {
5382              Binder.restoreCallingIdentity(identity);
5383          }
5384      }

看了两个方法,可以发现在PhoneInterfaceManager类中,就是通过获取phone对象并调用其方法来实现具体功能的。

既然PhoneInterfaceManager类是作为服务端的,那么客户端是怎么来获取这里面的方法呢,App的开发过程中是没办法获取到ITelephony接口的。可以通过 frameworks/base/telephony/java/android/telephony/TelephonyManager.java类来获取响应的方法,在TelephonyManager类中,ITelephony通过ITelephony.Stub.asInterface获取ITelephony类。
在这里插入图片描述

然后在telephony对象中获取getDataNetworkTypeForSubscriber方法,相当于远程调用了PhoneInterfaceManager中的getDataNetworkTypeForSubscriber()方法

6      public int getDataNetworkType(int subId) {
3147          try{//调用getITelephony()方法得到ITelephony的实例对象
3148              ITelephony telephony = getITelephony();
3149              if (telephony != null) {// 调用ITelephony实例对象的getDataNetworkTypeForSubscriber()方法// 实际上是远程调用了PhoneInterfaceManager中的getDataNetworkTypeForSubscriber()方法
3150                  return telephony.getDataNetworkTypeForSubscriber(subId, getOpPackageName(),
3151                          getAttributionTag());
3152              } else {
3153                  // This can happen when the ITelephony interface is not up yet.
3154                  return NETWORK_TYPE_UNKNOWN;
3155              }
3156          } catch(RemoteException ex) {
3157              // This shouldn't happen in the normal case
3158              return NETWORK_TYPE_UNKNOWN;
3159          } catch (NullPointerException ex) {
3160              // This could happen before phone restarts due to crashing
3161              return NETWORK_TYPE_UNKNOWN;
3162          }
3163      }

至此就完成了,但是getDataNetworkTypeForSubscriber方法中返回的这个数据phone.getServiceState().getDataNetworkType(),其实要深究的话,就要到ServiceState类了

/frameworks/base/telephony/java/android/telephony/ServiceState.java1770      public @NetworkType int getDataNetworkType() {
1771          final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo(
1772                  NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN);
1773          final NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo(
1774                  NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
1775  
1776          // For legacy mode device, or AP-assisted mode device but IWLAN is out of service, use
1777          // the RAT from cellular.
1778          if (iwlanRegInfo == null || !iwlanRegInfo.isInService()) {
1779              return (wwanRegInfo != null) ? wwanRegInfo.getAccessNetworkTechnology()
1780                      : TelephonyManager.NETWORK_TYPE_UNKNOWN;
1781          }
1782  
1783          // At this point, it must be an AP-assisted mode device and IWLAN is in service. We should
1784          // use the RAT from IWLAN service is cellular is out of service, or when both are in service
1785          // and any APN type of data is preferred on IWLAN.
1786          if (!wwanRegInfo.isInService() || mIsIwlanPreferred) {
1787              return iwlanRegInfo.getAccessNetworkTechnology();
1788          }
1789  
1790          // If both cellular and IWLAN are in service, but no APN is preferred on IWLAN, still use
1791          // the RAT from cellular.
1792          return wwanRegInfo.getAccessNetworkTechnology();
1793      }

它们的调用关系:
1> App获取TelephonyManger的实例对象
2>然后调用TelephonyManger的getDataNetworkType()
3>在方法中通过getITelephony()获取ITelephony 实例
4>ITelephony 跨进程 调用PhoneInterfaceManager类中的getDataNetworkTypeForSubscriber
5>而getDataNetworkTypeForSubscriber方法中又通过phone对象调用ServiceState返回DataNetworkType

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

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

相关文章

【电机控制】相电流重构——单电阻采样方案

【电机控制】相电流重构——单电阻采样方案 文章目录 [TOC](文章目录) 前言一、基于单电阻采样电流重构技术原理分析1.1 单电阻采样原理图1.2 基本电压矢量与电流采样关系 二、非观测区2.1 扇区过渡区2.2 低压调制区 三、非观测区补偿——移相法四、参考文献总结 前言 使用工具…

C++11实践指北

C11:书、在线工具、库。 书 1. 《现代C语言核心特性解析》 覆盖 C11~C20 特性的讲解。 视频跟读:https://www.bilibili.com/video/BV1nN4y1j7fv 现代CPP随笔_0CCh - 每天5分钟了解现代C新特性 2. 《C Primer》第五版 基于 C11 的 C 入门书。 正在看…

java基础面试题一

目录 1、Java语言概述 1.1一个”.java”源文件中是否可以包括多个类?有什么限制 1.2Java 的优势 1.3常用的几个命令行操作都有哪些?(至少4个) 1.4Java 中是否存在内存溢出、内存泄漏?如何解决?举例说明 1. 内存溢出&#xf…

RocketMQ | 源码分析 | Broker控制器的启动

在分布式消息中间件的领域中,RocketMQ 以其高性能、高可靠性和强大的功能占据着重要的地位。而 Broker 作为 RocketMQ 的核心组件之一,其控制器的启动过程涉及到众多关键环节和复杂的逻辑。理解这个过程对于深入掌握 RocketMQ 的运行机制以及在实际应用中…

从0开始深度学习(22)——从全连接层到卷积

多层感知机在处理图像这种高维数据时,因为模型需要大量的数据来训练这么多参数,会导致巨大的计算成本,还会增加过拟合的风险,所以人们选择使用卷积神经网络 1 不变性 在计算机视觉和深度学习领域,特指模型对输入数据中…

MySQL8.0.40编译安装

近期MySQL发布了8.0.40版本,与之前的版本相比,部分依赖包发生了变化,因此重新编译一版,也便于大家参考。 1. 下载源码 选择对应的版本、选择源码、操作系统 如果没有登录或者没有MySQL官网账号,可以选择只下载 2. 进…

系统性能优化——绑核

简要 绑核正如其名,将线程/进程绑定在一个或多个CPU核心。该技术可以使进程或线程在特定的处理器上运行,而不会被操作系统调度到其他处理器上。这里有两层含义。 如果线程被绑定在指定核心上,则只会在该核心上运行,即使其他核心…

2024年CentOS镜像下载地址,包括CentOS官网、国内镜像下载,超详细也

这里给大家提供了4种镜像下载地址,包括CentOS官方镜像下载、阿里云开源镜像站下载、网易开源镜像下载搜狐开源镜像下载。 1.CentOS官网镜像下载 因为服务器在国外所以打开CentOS官方网站的时候可能会比较慢。大家可以选择后面几种国内镜像下载方式。 1.1进入CentO…

【面试经典150】day 8

#1024程序员节 | 征文# 作为一个未来的程序员,现在我要继续刷题了。 力扣时刻。 目录 1.接雨水 2.罗马数字转整数 3.最后一个单词的长度 4.最长公共前缀 5.反转字符串中的单词 1.接雨水 好好好好好好,一开始就接雨水。我记得接了n次了。。。 痛苦战…

矩阵概念 和 性质

目录 一、矩阵因式分解 二、矩阵在图形学的运用 一、矩阵因式分解 1、先将矩阵化为上三角阵,得到U 2、每个主元列以下元素 主元 得到下三角阵 二、矩阵在图形学的运用 二维移动: 子空间H: 零向量属于H 对H中任意向量u、v,uv…

spyglass关于cdc检测的一处bug

最近在使用22版spyglass的cdc检测功能,发现struct_check的cdc检测实际时存在一些bug的。 构造如下电路,当qualifier和destination信号汇聚时,如果des信号完全将qualifier gate住,sg仍然会报ac_sync。当然此问题可以通过后续funct…

共识算法Raft(day11)

引入 在分布式系统中,为了消除单点提高系统可用性,通常会创建副本来进行容错,但这会带来另一个问题就是,如何保证多个副本之间的数据一致性。 为了解决这个问题,计算机行内就提出了共识算法,它允许多个分…

27.9 调用go-ansible执行playbook拷贝json文件重载采集器

本节重点介绍 : go-ansible执行playbook编写分发重载的playbook编译执行 测试停掉一个节点测试停掉的节点再回来 go-ansible执行playbook 新增 goansiblerun/run.go package goansiblerunimport ("context""github.com/apenella/go-ansible/pkg/execute&qu…

【数学二】多元函数积分学-重积分-二重积分定义、性质、计算

考试要求 1、了解多元函数的概念,了解二元函数的几何意义. 2、了解二元函数的极限与连续的概念,了解有界闭区域上二元连续函数的性质. 3、了解多元函数偏导数与全微分的概念,会求多元复合函数一阶、二阶偏导数,会求全微分&#x…

【Dv2Admin】Django配置线上ws反向代理

在 Web 应用程序的部署过程中,安全性、稳定性和实时通信是开发者们普遍关注的重点。Django 是一个非常流行的 Web 框架,常与 Nginx 配合使用,以便实现反向代理、负载均衡以及 SSL 加密等功能。除此之外,实时功能(如 WebSocket)也是现代应用中经常使用的技术。 在项目中实…

分布式文件系统Minio实战

分布式文件存储系统Minio实战 1、分布式文件系统应用场景1.1 Minio介绍1.1.1 Minio优点 1.2 MinIO的基础概念1.3 纠删码EC(Erasure Code)1.4 存储形式1.5 存储方案 2、Minio环境搭建2.1 单机部署2.1.1 non-erasure code mode2.1.2 erasure code mode2.1.…

算法题总结(十九)——图论

图论 DFS框架 void dfs(参数) { if (终止条件) {存放结果;return; }for (选择:本节点所连接的其他节点) {处理节点;dfs(图,选择的节点); // 递归回溯,撤销处理结果 } }深搜三部曲 确认递归函数,参数确认终止条件处理目前搜索节…

【网络协议栈】Tcp协议(下)的可靠性和高效性(超时重传、快速重传、拥塞控制、流量控制)

绪论: 承接上文,上文写到Tcp协议的结构以及对tcp协议的性能优化的滑动窗口,本章我们将继续了解Tcp协议的可靠性和高效性的具体展示。后面我将继续完善网络协议栈的网络层协议敬请期待! 话不多说安全带系好,发车啦(建议…

【Qt】窗口——Qt窗口的概念、常用的窗口函数、菜单栏、工具栏、状态栏、浮动窗口、对话框

文章目录 Qt窗口Qt窗口的概念菜单栏工具栏状态栏浮动窗口对话框 Qt 窗口 Qt窗口的概念 QMainWindow 类概述: QMainWindow 是一个为用户提供主窗口程序的类,它继承自 QWidget 类,并且提供了一个预定义的布局。 菜单栏 菜单栏常用属性&#xf…

C语言初阶:十.结构体基础

♥感谢您阅读本篇文章,文章内容为个人对所学内容的整理总结,欢迎大佬在评论区指点一二。♥ ♥个人主页:折枝寄北-CSDN博客折枝寄北擅长C语言初阶,等方面的知识,折枝寄北关注python,c,java,qt,c语言领域.https://blog.csdn.net/2303_80170533?…