HarmonyOS实战开发-编写一个分布式邮件系统

news2024/9/21 23:30:20

概述

本篇Codelab是基于TS扩展的声明式开发范式编程语言编写的一个分布式邮件系统,可以由一台设备拉起另一台设备,每次改动邮件内容,都会同步更新两台设备的信息。效果图如下:

说明: 本示例涉及使用系统接口,需要手动替换Full SDK才能编译通过。

搭建OpenHarmony开发环境

完成本篇Codelab我们首先要完成开发环境的搭建,本示例以Hi3516DV300开发板为例,参照以下步骤进行:

  1. 获取OpenHarmony系统版本:标准系统解决方案(二进制)。

      以3.0版本为例:

2.搭建烧录环境。

  • 完成DevEco Device Tool的安装
  • 完成Hi3516开发板的烧录

3.搭建开发环境。

  • 开始前请参考工具准备,完成DevEco Studio的安装和开发环境配置。
  • 开发环境配置完成后,请参考使用工程向导创建工程(模板选择“Empty Ability”),选择JS或者eTS语言开发。
  • 工程创建完成后,选择使用真机进行调测。

分布式组网

本章节以系统自带的音乐播放器为例,介绍如何完成两台设备的分布式组网。

  1. 硬件准备:准备两台烧录相同的版本系统的Hi3516DV300开发板A、B、一根网线及TYPE-C转USB线。
  2. 保证开发板A、B上电开机状态,网线两端分别连接开发板A、B的网口,将TYPE-C转USB线先连接A,使用hdc_std.exe,在命令行输入hdc_std shell ifconfig eth0 192.168.3.125,设置成功后,将TYPE-C转USB线连接B,在命令行输入hdc_std shell ifconfig eth0 192.168.3.126即可。
  3. 将设备A,B设置为互相信任的设备。
  • 找到系统应用“音乐”。

  • 设备A打开音乐,点击左下角流转按钮,弹出列表框,在列表中会展示远端设备的id。

  • 选择远端设备B的id,另一台开发板(设备B)会弹出验证的选项框。

  • 设备B点击允许,设备B将会弹出随机PIN码,将设备B的PIN码输入到设备A的PIN码填入框中。

  1. 配网完毕。

代码结构解读

本篇Codelab只对核心代码进行讲解,首先来介绍下整个工程的代码结构:

  • MainAbility:存放应用主页面。
    • pages/index.ets:应用主页面。
  • model:存放获取组网内的设备列表相关文件。
    • RemoteDeviceModel.ets:获取组网内的设备列表。
  • ServiceAbility:存放ServiceAbility相关文件。
    • service.ts:service服务,用于跨设备连接后通讯。
  • resources :存放工程使用到的资源文件。
    • resources/rawfile:存放工程中使用的图片资源文件。
  • config.json:配置文件。

实现页面布局和样式

在本章节中,您将学会如何制作一个简单的邮件界面。

  1. 实现主页面布局和样式。
  • 在MainAbility/pages/index.ets 主界面文件中布局整个邮件页面,包括收件人、发件人、主题、内容等等,代码如下:
@Entry
@Component
struct Index {
  private imageList: any[]= []
  @Provide dataList: string[]= ['xiaohua@128.com','xiaoming@128.com','假期温馨提示','2022年新春佳节即将来临,请同学们细读节前相关温馨提示,保持办公场所环境整洁,假期期间注意信息及个人安全,预祝全体同学新春快乐,虎虎生威!']

  dialogController: CustomDialogController = new CustomDialogController({
    builder: CustomDialogExample({ cancel: this.onCancel, confirm: this.onAccept }),
    cancel: this.existApp,
    autoCancel: true
  })

  build() {
    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
      Column() {
        Row() {
          Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween, alignItems: ItemAlign.Center }) {
            Text('✕').fontSize(20).fontColor('#000000')
            Button('发送').width(70).fontSize(14).fontColor('#ffffff').backgroundColor('#fc4646')
              .onClick(() => {
                RegisterDeviceListCallback();
                this.dialogController.open();
              })
          }
          .height(50)
          .padding({ top: 10, right: 15, bottom: 10, left: 15 })
        }

        Column() {
          Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
            Text('收件人').width(70).height(30).fontSize(15).fontColor('#969393')
            Text(this.dataList[0]).width('100%').height(30).fontSize(15).fontColor('#000000')
          }
          .padding({ top: 5, right: 15, bottom: 5, left: 15 })

          Text().width('100%').height(1).backgroundColor('#f8f6f6')

          Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
            Text('发件人').width(70).height(30).fontSize(15).fontColor('#969393')
            Text(this.dataList[1]).width('100%').height(30).fontSize(15).fontColor('#000000')
          }
          .padding({ top: 5, right: 15, bottom: 5, left: 15 })

          Text().width('100%').height(1).backgroundColor('#f8f6f6')

          Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
            Text('主题').width(50).height(30).fontSize(15).fontColor('#969393')
            Text(this.dataList[2]).width('100%').height(30).fontSize(15).fontColor('#000000')
          }
          .padding({ top: 5, right: 15, bottom: 5, left: 15 })

          Text().width('100%').height(1).backgroundColor('#f8f6f6')
          TextArea({ placeholder: 'input your word', text: this.dataList[3]}).height('100%').width('100%')
            .onChange((value: string) => {
              this.dataList[3] = value
              if(mRemote){
                sendMessageToRemoteService(JSON.stringify(this.dataList));
              }
                onDisconnectService();
          })
        }
      }

      Column() {
        Flex({ direction: FlexDirection.Row }) {
          List() {
            ForEach(this.imageList, (item) => {
              ListItem() {
                Image(item).width(50).height(50).objectFit(ImageFit.Contain)
              }.editable(true)
            }, item => item)
          }
          .listDirection(Axis.Horizontal) // 排列方向
          .divider({ strokeWidth: 2, color: 0xFFFFFF, startMargin: 20, endMargin: 20 }) // 每行之间的分界线
        }.width('100%').height(50).backgroundColor('#ccc')

        Text().width('100%').height(1).backgroundColor('#f8f6f6')
        Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) {
          Flex({ direction: FlexDirection.Row, alignItems: ItemAlign.Center }) {
            Button({ stateEffect: false }) {
              Image($rawfile('icon_photo.png')).width(20).height(20)
            }.backgroundColor('#ffffff').margin({ right: 20 })
            .onClick(() => {
              RegisterDeviceListCallback();
              this.dialogController.open();
            })

            Button({ stateEffect: false }) {
              Image($rawfile('icon_at.png')).width(20).height(20)
            }.backgroundColor('#ffffff')
          }

          Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.End }) {
            Button({ stateEffect: false }) {
              Image($rawfile('icon_distributed.png')).width(20).height(20)
            }.backgroundColor('#ffffff')
            .onClick(() => {
               this.getDeviceList()
            })

            Button({ stateEffect: false }) {
              Image($rawfile('icon_timer.png')).width(20).height(20)
            }.backgroundColor('#ffffff').margin({ left: 10, right: 10 })

            Button({ stateEffect: false }) {
              Image($rawfile('icon_enclosure.png')).width(20).height(20)
            }.backgroundColor('#ffffff')
          }
        }.height(50).padding(15)
      }
    }.width('100%').padding({ top: 5, bottom: 15 })
  }
}

在入口组件的生命周期函数aboutToAppear()中调用订阅事件。如果Ability是被其他设备拉起的,在aboutToAppear()中调用featureAbility.getWant(),可通过want中的参数重新初始化dataList数组,入口组件的生命周期函数aboutToAppear()代码如下:

async aboutToAppear() {
    this.subscribeEvent();
    let self = this;
    // 当被拉起时,通过want传递的参数同步对端界面UI
    await featureAbility.getWant((error, want) => {
      var status = want.parameters;
      if (want.parameters.dataList) {
        self.dataList = JSON.parse(status.dataList)
        // 远端被拉起后,连接对端的service
        if (want.parameters.remoteDeviceId) {
          let remoteDeviceId = want.parameters.remoteDeviceId
          onConnectRemoteService(remoteDeviceId)
        }
      }
    });
  }

2.给"发送"按钮添加点击事件。

点击"发送"按钮,调用拉起弹窗函数,弹窗中显示可拉起的同局域网下的设备,代码如下:

Button('发送').width(70).fontSize(14).fontColor('#ffffff').backgroundColor('#fc4646')
      .onClick(() => {
        RegisterDeviceListCallback();
        this.dialogController.open();
      })

3.给内容区域Textarea添加onChange事件。

内容区域文字变化会调用onChange()方法,每一次的变化都会调用sendMessageToRemoteService()方法去同步另一个设备的数据。其中onChange()和sendMessageToRemoteService()方法代码如下:

TextArea({ placeholder: 'input your word', text: this.dataList[3]}).height('100%').width('100%')
    .onChange((value: string) => {
      this.dataList[3] = value
      if(mRemote){
        sendMessageToRemoteService(JSON.stringify(this.dataList));
      }
      onDisconnectService();
  })
  async function sendMessageToRemoteService(dataList) {
  if (mRemote == null) {
    prompt.showToast({
      message: "mRemote is null"
    });
    return;
  }
  let option = new rpc.MessageOption();
  let data = new rpc.MessageParcel();
  let reply = new rpc.MessageParcel();
  data.writeStringArray(JSON.parse(dataList));
  prompt.showToast({
    message: "sendMessageToRemoteService" + dataList,
    duration: 3000
  });

  await mRemote.sendRequest(1, data, reply, option);
  let msg = reply.readInt();

}

拉起远端FA及连接远端Service服务

在本章节中,您将学会如何拉起在同一组网内的设备上的FA,并且连接远端Service服务。

  1. 调用featureAbility.startAbility()方法,拉起远端FA,并同步界面UI。
  2. 点击"分布式拉起"按钮,调用RegisterDeviceListCallback()发现设备列表,并弹出设备列表选择框CustomDialogExample,选择设备后拉起远端FA。CustomDialogExample()代码如下:
// 设备列表弹出框
@CustomDialog
struct CustomDialogExample {
  @State editFlag: boolean = false
  @Consume imageIndexForPosition : number[]
  @Consume pictureList: string[]
  controller: CustomDialogController
  cancel: () => void
  confirm: () => void
  build() {
    Column() {
      List({ space: 10, initialIndex: 0 }) {
        ForEach(DeviceIdList, (item) => {
          ListItem() {
            Row() {
              Text(item)
                .width('87%').height(50).fontSize(10)
                .textAlign(TextAlign.Center).borderRadius(10).backgroundColor(0xFFFFFF)
                .onClick(() => {
                  onStartRemoteAbility(item,this.imageIndexForPosition,this.pictureList);
                  this.controller.close();
                })
              Radio({value:item})
                .onChange((isChecked) => {
                  onStartRemoteAbility(item,this.imageIndexForPosition,this.pictureList);
                  this.controller.close();
                }).checked(false)
            }
          }.editable(this.editFlag)
        }, item => item)
      }
    }.width('100%').height(200).backgroundColor(0xDCDCDC).padding({ top: 5 })
  }
}

点击Text组件或者Radio组件都会调用onStartRemoteAbility()方法拉起远端FA,onStartRemoteAbility()代码如下:

function onStartRemoteAbility(deviceId,imageIndexForPosition,pictureList: string[]) {
  AuthDevice(deviceId);
  let numDevices = remoteDeviceModel.deviceList.length;
  if (numDevices === 0) {
    prompt.showToast({
      message: "onStartRemoteAbility no device found"
    });
    return;
  }

  var params = {
    imageIndexForPosition: JSON.stringify(imageIndexForPosition),
    pictureList : JSON.stringify(pictureList),
    remoteDeviceId : localDeviceId
  }
  var wantValue = {
    bundleName: 'com.huawei.cookbook',
    abilityName: 'com.example.openharmonypicturegame.MainAbility',
    deviceId: deviceId,
    parameters: params
  };
  featureAbility.startAbility({
    want: wantValue
  }).then((data) => {
    // 拉起远端后,连接远端service
    onConnectRemoteService(deviceId)
  });
}

2.调用featureAbility.connectAbility方法,连接远端Service服务,连接成功后返回remote对象。

在featureAbility.startAbility()成功的回调中调用onConnectRemoteService()方法,onConnectRemoteService()方法代码如下:

// 连接远端Service
async function onConnectRemoteService(deviceId) {
  // 连接成功的回调
  async function onConnectCallback(element, remote) {
     mRemote = remote;
  }
  // Service异常死亡的回调
  function onDisconnectCallback(element) {
  }
  // 连接失败的回调
  function onFailedCallback(code) {
    prompt.showToast({
      message: "onConnectRemoteService onFailed: " + code
    });
  }
  let numDevices = remoteDeviceModel.deviceList.length;
  if (numDevices === 0) {
    prompt.showToast({
      message: "onConnectRemoteService no device found"
    });
    return;
  }
  connectedAbility = await featureAbility.connectAbility(
    {
      deviceId: deviceId,
      bundleName: "com.huawei.cookbook",
      abilityName: "com.example.openharmonypicturegame.ServiceAbility",
    },
    {
      onConnect: onConnectCallback,
      onDisconnect: onDisconnectCallback,
      onFailed: onFailedCallback,
    },
  );
}

在配置文件config.json需要设置ServiceAbility的属性visible为true,代码如下:

"abilities": [
      ...
      {
        "visible": true,
        "srcPath": "ServiceAbility",
        "name": ".ServiceAbility",
        "icon": "$media:icon",
        "srcLanguage": "ets",
        "description": "$string:description_serviceability",
        "type": "service"
      }
],

同时,Service侧也需要在onConnect()时返回IRemoteObject,从而定义与Service进行通信的接口。onConnect()需要返回一个IRemoteObject对象,OpenHarmony提供了IRemoteObject的默认实现,通过继承rpc.RemoteObject来创建自定义的实现类。

Service侧把自身的实例返回给调用侧的代码如下:

import rpc from "@ohos.rpc";
import commonEvent from '@ohos.commonEvent';
class FirstServiceAbilityStub extends rpc.RemoteObject{
    constructor(des) {
        if (typeof des === 'string') {
            super(des);
        } else {
            return null;
        }
    }
    onRemoteRequest(code, data, reply, option) {
        if (code === 1) {
            let arr = data.readIntArray();
            reply.writeInt(100);
            // 发布公共事件相关流程
	    ...


        } else {
        }
        return true;
    }
}

export default {
    // 创建Service的时候调用,用于Service的初始化
    onStart() {
    },
    // 在Service销毁时调用。Service应通过实现此方法来清理任何资源,如关闭线程、注册的侦听器等。
    onStop() {
    },
    // 在Ability和Service连接时调用,该方法返回IRemoteObject对象,开发者可以在该回调函数中生成对应Service的IPC通信通道
    onConnect(want) {
        try {
            let value = JSON.stringify(want);
        } catch(error) {
        }
        return new FirstServiceAbilityStub("[pictureGame] first ts service stub");
    },
    // 在Ability与绑定的Service断开连接时调用
    onDisconnect(want) {
        let value = JSON.stringify(want);
    },
    // 在Service创建完成之后调用,该方法在客户端每次启动该Service时都会调用
    onCommand(want, startId) {
        let value = JSON.stringify(want);
    }
};

RPC跨设备通讯

在本章节中,您将学会在成功连接远端Service服务的前提下,如何利用RPC进行跨设备通讯。

  1. 成功连接远端Service服务的前提下,在正文部分增删文字,都会完成一次跨设备通讯,假如在设备A端输入文字,消息的传递是由设备A端的FA传递到设备B的Service服务,发送消息的方法sendMessageToRemoteService()代码如下:
// 连接成功后发送消息
async function sendMessageToRemoteService(imageIndexForPosition) {
  if (mRemote == null) {
    prompt.showToast({
      message: "mRemote is null"
    });
    return;
  }
  let option = new rpc.MessageOption();
  let data = new rpc.MessageParcel();
  let reply = new rpc.MessageParcel();
  data.writeIntArray(JSON.parse(imageIndexForPosition));
  await mRemote.sendRequest(1, data, reply, option);
  let msg = reply.readInt();
}

2.在B端的Service接收消息,当A端成功连接B端Service服务后,在A端会返回一个remote对象,当A端remote对象调用sendRequest()方法后,在B端的Service中的onRemoteRequest()方法中会接收到发送的消息,其中继承rpc.RemoteObject的类和onRemoteRequest()方法代码如下:

class FirstServiceAbilityStub extends rpc.RemoteObject{
    constructor(des) {
        if (typeof des === 'string') {
            super(des);
        } else {
            return null;
        }
    }

    onRemoteRequest(code, data, reply, option) {
        if (code === 1) {
            // 从data中接收数据
            let arr = data.readIntArray();
            // 回复接收成功标识
            reply.writeInt(100);
            // 发布公共事件相关流程
           ...

        } else {
        }
        return true;
    }
}

FA订阅公共事件

在本章节中,您将学会如何通过CommonEvent订阅公共事件,详细信息请参考CommonEvent开发指南。在九宫格组件PictureGrid的生命周期函数aboutToAppear()中,调用订阅公共事件方法subscribeEvent(),用来订阅"publish_moveImage"公共事件,subscribeEvent()代码如下:

 subscribeEvent(){
    let self = this;
    // 用于保存创建成功的订阅者对象,后续使用其完成订阅及退订的动作
    var subscriber; 
    // 订阅者信息
    var subscribeInfo = {
      events: ["publish_moveImage"],
      priority: 100

    };

    // 设置有序公共事件的结果代码回调
    function SetCodeCallBack(err) {
    }
    // 设置有序公共事件的结果数据回调
    function SetDataCallBack(err) {
    }
    // 完成本次有序公共事件处理回调
    function FinishCommonEventCallBack(err) {
    }
    // 订阅公共事件回调
    function SubscribeCallBack(err, data) {
      let msgData = data.data;
      let code = data.code;
      // 设置有序公共事件的结果代码
      subscriber.setCode(code, SetCodeCallBack);
      // 设置有序公共事件的结果数据
      subscriber.setData(msgData, SetDataCallBack);
      // 完成本次有序公共事件处理
      subscriber.finishCommonEvent(FinishCommonEventCallBack)
      // 处理接收到的数据data
      self.imageIndexForPosition = data.parameters.imageIndexForPosition;
      self.pictureList = [];
      self.imageIndexForPosition.forEach(value => {
        if (value == 9) {
          self.pictureList.push("--")
        } else {
          self.pictureList.push(`picture_0` + value + `.png`)
        }
      });

      self.onFinish();
    }

    // 创建订阅者回调
    function CreateSubscriberCallBack(err, data) {
      subscriber = data;
      // 订阅公共事件
      commonEvent.subscribe(subscriber, SubscribeCallBack);
    }

    // 创建订阅者
    commonEvent.createSubscriber(subscribeInfo, CreateSubscriberCallBack);
 }

在FA中订阅到Service服务发布的"publish_moveImage"事件后,在SubscribeCallBack()回调中重新赋值imageIndexForPosition数组与pictureList数组,从而同步更新界面UI。

service发布公共事件

在本章节中,您将学会如何通过CommonEvent发布公共事件,详细信息请参考CommonEvent开发指南。

当Service服务接收到消息后,在onRemoteRequest()发布公共事件,代码如下:

onRemoteRequest(code, data, reply, option) {
    if (code === 1) {
	// 从data中接收数据
	let arr = data.readIntArray();
	// 回复接收成功标识
	reply.writeInt(100);
	// 公共事件相关信息
	var params ={
	    imageIndexForPosition: arr
	}
	var options = {
            // 公共事件的初始代码
	    code: 1,
            // 公共事件的初始数据			
	    data: 'init data',、
            // 有序公共事件 	        
	    isOrdered: true, 	
	    bundleName: 'com.huawei.cookbook',
	    parameters: params

        }
	// 发布公共事件回调
	function PublishCallBack() {
	}
	// 发布公共事件
	commonEvent.publish("publish_moveImage", options, PublishCallBack);

	} else {
	}
	return true;
 }

在接收到消息后,把接收到的图片位置数组放入params中,然后发布名称为"publish_moveImage"的有序公共事件。

总结

  • 应用间跨设别通讯是通过featureAbility.connectAbility连接远端Service服务成功后,再通过RPC相关API来进行消息传递。
  • 应用内Service与FA之间可通过CommonEvent发布与订阅公共事件来完成通讯。
  • 本篇Codelab在设备A与设备B通讯流程如下:
  1. 设备A与设备B在组网成功的前提下,设备A通过featureAbility.startAbility()拉起设备B的Ability,并把设备A的deviceId作为参数传递给设备B的Ability,在设备B接收到参数的同时,通过featureAbility.connectAbility()连接设备A的Service服务,在设备B中返回一个remote对象,该remote对象可将设备B的消息发送到设备A的Service服务。
  2. 在设备A侧,拉起设备B的Ability的成功回调中,设备A通过featureAbility.connectAbility()连接设备B的Service服务,在设备A中返回一个remote对象,该remote对象可将设备A的消息发送到设备B的Service服务。
  3. 设备A侧通过remote.sendRequest()将消息发送到设备B侧Service服务,设备B侧的Service服务中的onRemoteRequest()接收消息。
  4. 设备B侧的Service中的onRemoteRequest()接收到消息后,通过CommonEvent发布公共事件,将该消息发布出去。
  5. 设备B侧的Ability订阅该事件,用来接收发布的消息并做最后的处理。

为了帮助大家更深入有效的学习到鸿蒙开发知识点,小编特意给大家准备了一份全套最新版的HarmonyOS NEXT学习资源,获取完整版方式请点击→《HarmonyOS教学视频

HarmonyOS教学视频

鸿蒙语法ArkTS、TypeScript、ArkUI等.....视频教程

鸿蒙生态应用开发白皮书V2.0PDF:

获取白皮书完整版方式请点击→《鸿蒙生态应用开发白皮书V2.0PDF》

鸿蒙 (Harmony OS)开发学习手册

一、入门必看

  1. 应用开发导读(ArkTS)
  2. ……

二、HarmonyOS 概念

  1. 系统定义
  2. 技术架构
  3. 技术特性
  4. 系统安全
  5. ........

三、如何快速入门?《做鸿蒙应用开发到底学习些啥?》

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

四、开发基础知识

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

五、基于ArkTS 开发

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

更多了解更多鸿蒙开发的相关知识可以参考:《鸿蒙 (Harmony OS)开发学习手册

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

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

相关文章

基于springboot的牙科就诊管理系统

技术:springbootmysqlvue 一、系统背景 当前社会各行业领域竞争压力非常大,随着当前时代的信息化,科学化发展,让社会各行业领域都争相使用新的信息技术,对行业内的各种相关数据进行科学化,规范化管理。这样…

mysql分页查询多用GitCode平台

目录 一、在GitCode平台AI搜索结果(这个更优) 二、在百度搜索输入“mysql Java分页查询”的输出结果: 三、推荐的文章 四、GitCode的使用 1)如搜索jdk11可以直接下载jdk11的包 2)搜索开源项目 3)如搜…

学浪怎么下载视频_学浪下载视频方法

越来越多人购买了学浪课程,但是苦于学浪视频官方不提供下载选项,于是就有人做了学浪视频下载软件,这里就教大家如何下载你已经购买的学浪课程 本次教程用到的软件都在下面的链接 链接:https://pan.baidu.com/s/1y7vcqILToULrYAp…

网络: 数据链路层

数据链路层: 数据帧的封装与传输 以太网数据帧 源地址和目的地址是指网卡的硬件地址(也叫MAC地址), 长度是48位,是在网卡出厂时固化的;帧协议类型字段有三种值,分别对应IP、ARP、RARP;帧末尾是CRC校验码 以太网 "以太网" 不是一种具体的网络, 而是一种技术标准; 既…

【机器学习】一文搞懂算法模型之:Transformer

Transformer 1、引言2、Transformer2.1 定义2.2 原理2.3 算法公式2.3.1 自注意力机制2.3.1 多头自注意力机制2.3.1 位置编码 2.4 代码示例 3、总结 1、引言 小屌丝:鱼哥, 你说transformer是个啥? 小鱼:嗯… 啊… 嗯…就是… 小屌…

Rust基本类型

数值类型 整数类型 无符号整数只能取正数和0,有符号整数可以取正数负数和0。isize和usize类型取决于程序运行的计算机CPU类型,若CPU是32位的,则这两个类型是32位的,若CPU是64位的,则它们是64位的。rust整型 默认使用…

CentOS/RHEL 6.5 上 NFS mount 挂起kernel bug

我本身有四台机器做WAS集群,挂载nfs,其中随机一台客户端计算机端口关闭释放将进入不良状态,对 NFSv4 挂载的任何访问都将挂起(例如“ls,cd 或者df均挂起”)。这意味着没有人并且所有需要访问共享的用户进程…

C语言例:设 int x; 则表达式 (x=4*5,x*5),x+25 的值

代码如下&#xff1a; #include<stdio.h> int main(void) {int x,m;m ((x4*5,x*5),x25);printf("(x4*5,x*5),x25 %d\n",m);//x4*520//x*5100//x2545return 0; } 结果如下&#xff1a;

wy的leetcode刷题记录_Day92

wy的leetcode刷题记录_Day92 声明 本文章的所有题目信息都来源于leetcode 如有侵权请联系我删掉! 时间&#xff1a;2024-3-22 前言 目录 wy的leetcode刷题记录_Day92声明前言2617. 网格图中最少访问的格子数题目介绍思路代码收获 695. 岛屿的最大面积题目介绍思路代码收获 2…

图论必备:Dijkstra、Floyd与Bellman-Ford算法在最短路径问题中的应用

&#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;アンビバレント—Uru 0:24━━━━━━️&#x1f49f;──────── 4:02 &#x1f504; ◀️ ⏸ ▶️ ☰ …

Javaweb学习记录(二)web开发入门(请求响应)

第一个基于springboot的web请求程序 通过创建一个带有springboot的spring项目&#xff0c;项目会自动生成一个程序启动类&#xff0c;该类启动时会启动该整个项目&#xff0c;而我们需要写一个web请求类&#xff0c;要求在本地浏览器上发送请求后&#xff0c;浏览器显示Hello&…

python --- 练习题3

目录 1、猜数字游戏&#xff08;使用random模块完成&#xff09; &#xff1a;继上期题目&#xff0c;附加 2、用户登录注册案例 3、求50~150之间的质数是那些&#xff1f; 4、打印输出标准水仙花数&#xff0c;输出这些水仙花数 5、验证:任意一个大于9的整数减去它的各位…

【数据库系统】数据库完整性和安全性

第六章 数据库完整性和安全性 基本内容 安全性&#xff1b;完整性&#xff1b;数据库恢复技术&#xff1b;SQL Server的数据恢复机制&#xff1b; 完整性 实体完整性、参照完整性、用户自定义完整性 安全性 身份验证权限控制事务日志&#xff0c;审计数据加密 数据库恢复 冗余…

中国贸易金融跨行交易区块链平台CTFU、区块链福费廷交易平台BCFT、中国人民银行贸易金融区块链平台CTFP、银行函证区块链服务平台BPBC

中国人民银行贸易金融区块链平台CTFP介绍 贸易金融的发展概况及存在的问题 1.1 贸易金融的概况 贸易金融是指商业银行在贸易双方债权债务关系的基础上&#xff0c;为国内或跨国的商品和服务贸易提供的贯穿贸易活动整个价值链、全程全面性的综合金融服务。伴随全球化的进程&a…

互联网思维:息共享、开放性、创新和快速反应、网络化、平台化、数据驱动和用户体验 人工智能思维:模拟人、解放劳动力、人工智能解决方案和服务

互联网思维&#xff1a;信息共享、开放性、创新和快速反应、网络化、平台化、数据驱动和用户体验 互联网思维是指一种以互联网为基础的思考方式&#xff0c;强调信息共享、开放性、创新和快速反应的特点。这种思维方式注重网络化、平台化、数据驱动和用户体验&#xff0c;以适…

simulink里枚举量的使用--在m文件中创建枚举量实践操作(推荐)

本文将介绍一种非常重要的概念&#xff0c;枚举量&#xff0c;以及它在simulink状态机中的使用&#xff0c;并且给出模型&#xff0c;方便大家学习。 枚举量&#xff1a;实际上是用一个名字表示了一个变量&#xff0c;能够比较方便的表示标志信息 A.简单举例&#xff1a; 1&a…

Hack The Box-Analytics

目录 信息收集 namp whatweb WEB 信息收集 feroxbuster RCE漏洞 提权 get user get root 信息收集 namp 端口信息探测┌──(root㉿ru)-[~/kali/hackthebox] └─# nmap -p- 10.10.11.233 --min-rate 10000 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-…

经典双指针问题

思路;先找到第一个包含m家店的区间&#xff08;l-r&#xff09;&#xff0c;然后开始进行双指针&#xff08;l&#xff0c;r&#xff09;滑动(如下滑动) while(r<n){while(vis[a[l]]>1)//当前l-r之间a[l]店铺有多个&#xff08;大于一个&#xff09;&#xff0c;那即可去…

macOS下Java应用的打包和安装程序制作

macOS应用程序结构 macOS通常以dmg或pkg作为软件发行包&#xff0c;安装到/Applications下后&#xff0c;结构比较统一。 info.plist里的CFBundleExecutable字段可以指定入口&#xff0c;如果不指定&#xff0c;则MacOS下必须存在同名可执行文件。即abc.app下必须存在abc.app/…

从原理到实践:深入探索Linux安全机制(一)

前言 本文将从用户和权限管理、文件系统权限、SELinux、防火墙、加密和安全传输、漏洞管理和更新等几个Linux安全机制中的重要方面&#xff0c;深入探索其工作原理和使用方法。在当今数字化时代&#xff0c;网络安全问题备受关注&#xff0c;Linux作为广泛应用的操作系统之一&…