微信原生組件|基於小程式實現音視頻通話

来源:https://www.cnblogs.com/zegodeveloper/archive/2022/09/21/16715772.html
-Advertisement-
Play Games

1 微信小程式原生推拉流組件功能簡介 本文將介紹如何使用微信小程式原生推拉流組件 <live-pusher> 和 <live-player> 進行推拉流,快速實現一個簡單的實時音視頻通話。 由於微信小程式原生推拉流組件使用起來比較複雜,推薦開發者使用即構封裝的音視頻SDK <zego-push> 和 ...


1 微信小程式原生推拉流組件功能簡介

本文將介紹如何使用微信小程式原生推拉流組件 <live-pusher> 和 <live-player> 進行推拉流,快速實現一個簡單的實時音視頻通話。

由於微信小程式原生推拉流組件使用起來比較複雜,推薦開發者使用即構封裝的音視頻SDK <zego-push> 和 <zego-player> 組件實現視頻通話,可參考 實現視頻通話

2 實現微信小程式音視頻通話的前提條件

在實現基本的實時音視頻功能之前,請確保:

3 即構音視頻SDK實現流程

用戶通過 ZEGO Express SDK 即構音視頻SDK進行視頻通話的基本流程為:

用戶 A、B 加入房間,用戶 B 預覽並將音視頻流推送到 ZEGO 雲服務(推流),用戶 A 收到用戶 B 推送音視頻流的通知之後,在通知中播放用戶 B 的音視頻流(拉流)。

下載.png

3.1 配置微信小程式後臺

在初始化 音視頻SDK 前,需要在 微信公眾平臺 中進行如下配置:

  • 伺服器功能變數名稱配置:在“小程式後臺 > 開發管理 > 開發設置 > 伺服器功能變數名稱”中,按照協議分類,將即構 Server 地址、LogUrl、以及用戶業務需要用到的地址填到指定的“request合法功能變數名稱”或“socket合法功能變數名稱”中。
    下載1.png

  • 相關功能開啟:在“小程式後臺 > 開發管理 > 介面設置 > 介面許可權”中,打開 實時播放音視頻流實時錄製音視頻流 功能開關。
    下載2.png

3.2 即構音視頻SDK初始化

1. 創建音視頻通話界面

根據音視頻場景需要,為您的項目創建音視頻通話的用戶界面。我們推薦您在項目中添加如下元素:

  • 本地預覽視窗
  • 遠端視頻視窗
  • 結束按鈕
    下載3.png

小程式推流組件 <live-pusher> 中的 "video-width" 和 "video-height" 存在相容性問題,可能會出現設置不生效的情況。

參考界面代碼:

<view wx:if="{{canShow== 1}}" class="">
  <view class="containerBase">
    <live-pusher class="testpusher" 
    wx:if="{{pusher.url}}" 
    url="{{pusher.url}}"  
    mode="{{pusher.mode}}"
    autopush="{{pusher.autopush}}"
    enable-camera="{{pusher.enableCamera}}"
    enable-mic="{{pusher.enableMic}}"
    muted="{{!pusher.enableMic}}"
    enable-agc="{{pusher.enableAgc}}"
    enable-ans="{{pusher.enableAns}}"
    zoom="{{pusher.enableZoom}}"
    min-bitrate="{{pusher.minBitrate}}"
    max-bitrate="{{pusher.maxBitrate}}"
    video-width="{{pusher.videoWidth}}"
    video-height="{{pusher.videoHeight}}"
    beauty="{{pusher.beautyLevel}}"
    whiteness="{{pusher.whitenessLevel}}"
    orientation="{{pusher.videoOrientation}}"
    device-position="{{pusher.frontCamera}}"
    remote-mirror="{{pusher.enableRemoteMirror}}"
    local-mirror="{{pusher.localMirror}}"
    background-mute="{{pusher.enableBackgroundMute}}"
    audio-quality="{{pusher.audioQuality}}"
    audio-volume-type="{{pusher.audioVolumeType}}"
    audio-reverb-type="{{pusher.audioReverbType}}"
    waiting-image="{{pusher.waitingImage}}"
    beauty-style="{{pusher.beautyStyle}}"
    filter="{{pusher.filter}}"
    bindstatechange="onPushStateChange" 
    bindaudiovolumenotify="bindaudiovolumenotify"  
    bindnetstatus="onPushNetStateChange"
    waiting-image="https://storage.zego.im/downloads/pause_publish.png"></live-pusher>
      <live-player  wx:for="{{playerList}}" wx:key="streamID" id="{{item.id}}" 
      src= "{{item.url}}"
      mode= "RTC"
      autoplay= "{{item.autoplay}}"
      mute-audio= "{{item.muteAudio}}"
      mute-video= "{{item.muteVideo}}"
      orientation= "{{item.orientation}}"
      object-fit= "{{item.objectFit}}"
      min-cache= "{{item.minCache}}"
      max-cache= "{{item.maxCache}}"
      sound-mode= "{{item.soundMode}}"
      enable-recv-message= "{{item.enableRecvMessage}}"
      auto-pause-if-navigate= "{{item.autoPauseIfNavigate}}"
      auto-pause-if-open-native= "{{item.autoPauseIfOpenNative}}" enable-metadata="true" bindmetadatachange="binddatachange"  bindstatechange="onPlayStateChange" bindnetstatus="onPlayNetStateChange"></live-player>
  </view>
  <view class="index-container">
    <view class='input-container'>
      <input value="{{roomID}}" bindinput="bindKeyInput" placeholder="請輸入房間 ID" placeholder-style='color: #b3b3b3; font-size: 14px;' class="room-input" />
      <text class="tip"></text>
    </view>
    <view class="button-container">
      <button bindtap="openRoom" data-role="1" data-option="videoAndAudio" hover-class="none" class="openRoom">
        加入房間(推流)
      </button>
     
      
      <button bindtap="logout" hover-class="none">退出房間</button>
    </view>
  </view>
</view>
<view class="settings">
  <button wx:if="{{canShow==0}}" open-type="openSetting" bindopensetting="settingCallback">
    授權使用攝像頭和麥克風
  </button>
</view>

2. 創建音視頻SDK引擎

創建 ZegoExpressEngine 引擎實例,將申請到的 AppID 傳入參數 “appID”,將獲取到的 Server 地址傳入參數 “server”。

// 初始化實例
zg = new ZegoExpressEngine(appID, server);

如果需要註冊回調,開發者可根據實際需要,實現 ZegoEvent 中的某些方法,創建引擎後可通過調用 on 介面設置回調。

zg.on('roomStateUpdate', (roomID, state, errorCode, extendedData) => {
    if (state == 'DISCONNECTED') {
        // 與房間斷開了連接
	// ...
    }

    if (state == 'CONNECTING') {
        // 與房間嘗試連接中
	// ...
    }

    if (state == 'CONNECTED') {
        // 與房間連接成功
	// ...
    }
})

3.3 登錄音視頻房間

1. 獲取房間登錄 Token

登錄房間需要用於驗證身份的 Token,獲取方式請參考 用戶許可權控制。如需快速調試,建議使用控制台生成的臨時 Token,生成臨時 Token 的具體操作請參考 控制台 - 項目管理

2. 登錄音視頻房間

您可以調用 SDK 的 loginRoom 介面,傳入房間 ID 參數 “roomID”、“token” 和用戶參數 “user”,登錄房間。您可通過監聽 roomStateUpdate 回調實時監控自己在本房間內的連接狀態,具體請參考 4.1 常見通知回調 中的“4.1.1 我在房間內的連接狀態變化通知”。

roomID 和 user 的參數由您本地生成,但是需要滿足以下條件:

  • 同一個 AppID 內,需保證 “roomID” 全局唯一。
  • 同一個 AppID 內,需保證 “userID” 全局唯一,建議開發者將 “userID” 與自己業務的賬號系統進行關聯。

為避免錯過任何通知,您需要在登錄房間前先設置所有的監聽回調(如房間狀態、用戶狀態、流狀態、推拉流狀態等),具體請參考 4.1 常見通知回調

// 登錄房間,成功則返回 true
const result = await zg.loginRoom(roomID, token, {userID, userName});

3.4 將自己的音視頻流推送到 ZEGO 即構音視頻雲

3.4.1 初始化小程式組件實例

調用 initContext 介面初始化小程式組件。

小程式組件中用於存儲推流屬性 pusher 和拉流屬性列表 playerList 兩個欄位需要傳給 SDK,SDK 後續將通過傳入的兩個欄位對相應的推拉流作狀態及視圖更新處理。

zg.initContext({
     wxContext: this,
     pushAtr: "pusher", // 對象名,對象屬性與 live-pusher 中的屬性為映射關係
     playAtr: "playerList" // 對象名,對象屬性與 live-player 中的屬性為映射關係
})

即構音視頻SDK 在內部會對推拉流實例進行操作以及視圖更新,開發者無需保存推拉流實例和調用小程式 setData 介面更新視圖,避免與 SDK 發生衝突。後續可通過 getPusherInstancegetPlayerInstance 介面獲取推拉流實例。

3.4.2 創建對應業務場景的 WXML

根據您的業務場景需求,編寫 WXML 文件,創建推拉流組件 <live-pusher> 和 <live-player>。

  • <live-pusher> 組件用於小程式的實時推送音視頻流功能。
  • <live-player> 組件用戶小程式的實時播放音視頻流功能。

WXML 的具體含義與用法請參考微信官網文檔中的介紹 WXML

WXML 中的 pusher 與 playerList,必須與初始化小程式組件 initContext 中定義的這兩個欄位屬性名保持一致,後續 SDK 調用推拉流介面之後才能正確地進行狀態及視圖更新。

bindstatechange 表示播放狀態變化事件;bindaudiovolumenotify 表示播放音量大小通知;bindnetstatus 表示網路狀態通知。

<live-pusher class="testpusher" 
    wx:if="{{pusher.url}}" 
    url="{{pusher.url}}"  
    mode="{{pusher.mode}}"
    autopush="{{pusher.autopush}}"
    enable-camera="{{pusher.enableCamera}}"
    enable-mic="{{pusher.enableMic}}"
    muted="{{!pusher.enableMic}}"
    enable-agc="{{pusher.enableAgc}}"
    enable-ans="{{pusher.enableAns}}"
    enable-ear-monitor="{{pusher.enableEarMonitor}}"
    auto-focus="{{pusher.enableAutoFocus}}"
    zoom="{{pusher.enableZoom}}"
    min-bitrate="{{pusher.minBitrate}}"
    max-bitrate="{{pusher.maxBitrate}}"
    video-width="{{pusher.videoWidth}}"
    video-height="{{pusher.videoHeight}}"
    beauty="{{pusher.beautyLevel}}"
    whiteness="{{pusher.whitenessLevel}}"
    orientation="{{pusher.videoOrientation}}"
    aspect="{{pusher.videoAspect}}"
    device-position="{{pusher.frontCamera}}"
    remote-mirror="{{pusher.enableRemoteMirror}}"
    local-mirror="{{pusher.localMirror}}"
    background-mute="{{pusher.enableBackgroundMute}}"
    audio-quality="{{pusher.audioQuality}}"
    audio-volume-type="{{pusher.audioVolumeType}}"
    audio-reverb-type="{{pusher.audioReverbType}}"
    waiting-image="{{pusher.waitingImage}}"
    beauty-style="{{pusher.beautyStyle}}"
    filter="{{pusher.filter}}"
    bindstatechange="onPushStateChange" 
    bindaudiovolumenotify="bindaudiovolumenotify"  
    bindnetstatus="onPushNetStateChange"
    waiting-image="https://storage.zego.im/downloads/pause_publish.png">
</live-pusher>
<live-player  wx:for="{{playerList}}" wx:key="streamID" id="{{item.id}}" 
      src= "{{item.url}}"
      mode= "RTC"
      autoplay= "{{item.autoplay}}"
      mute-audio= "{{item.muteAudio}}"
      mute-video= "{{item.muteVideo}}"
      orientation= "{{item.orientation}}"
      object-fit= "{{item.objectFit}}"
      min-cache= "{{item.minCache}}"
      max-cache= "{{item.maxCache}}"
      sound-mode= "{{item.soundMode}}"
      enable-recv-message= "{{item.enableRecvMessage}}"
      auto-pause-if-navigate= "{{item.autoPauseIfNavigate}}"
      auto-pause-if-open-native= "{{item.autoPauseIfOpenNative}}" enable-metadata="true" bindmetadatachange="binddatachange"  bindstatechange="onPlayStateChange" bindnetstatus="onPlayNetStateChange">
</live-player>

3.4.3 推送音視頻流到 ZEGO 即構音視頻雲

必須完成初始化小程式組件實例和創建業務場景的 WXML 之後,才能調用 SDK 介面創建推流和拉流實例。

用戶調用 SDK 的 createPusher 介面創建推流實例,並通過調用實例對象上的 start 介面,傳入流 ID 參數 “streamID”。您可通過監聽 publisherStateUpdate 回調知曉推流是否成功,具體請參考 4.1 常見通知回調 中的“4.1.4 用戶推送音視頻流的狀態通知”。

“streamID” 由您本地生成,但是需要保證:

  • 同一個 AppID 下,“streamID” 全局唯一。如果同一個 AppID 下,不同用戶各推了一條 “streamID” 相同的流,後推流的用戶推流失敗。
  • “streamID” 長度不超過 256 位元組的字元串。僅支持數字,英文字元和 “~”,“!”,“@”,“$”,“%”,“^”,“&”,“*”,“(”,“)”,“_”,“+”,“=”,“-”,“`”,“;”,“’”,“,”,“.”,“<”,“>”,“/”,“\”。
// 推流方登錄房間成功後觸發推流
 const pusher = zg.createPusher();
 pusher.start("streamID_xxx");

3.5 拉取其他用戶的音視頻

進行視頻通話時,我們需要拉取到其他用戶的音視頻。

用戶先調用 getPlayerInstance介面,根據傳入的流 ID 參數 “streamID”,獲取 streamID 對應的拉流實例,然後通過調用拉流實例對象的 play 介面開始拉流。您可通過監聽 playerStateUpdate 回調知曉是否成功拉取音視頻,具體請參考 4.1 常見通知回調 中的“4.1.5 用戶拉取音視頻流的狀態通知”。

遠端用戶推送的 “streamID” 可以從 roomStreamUpdate 回調中獲得,具體回調設置請參考 4.1 常見通知回調 中的“4.1.3 房間內流狀態變更的通知”。

// 在 SDK 的回調 roomStreamUpdate 中獲取拉流 streamID
// 當用戶加入或離開房間時,該事件被觸發
zg.on("roomStreamUpdate", (roomID, updateType, streamList) => {
    console.log("roomStreamUpdate", roomID, updateType, streamList);
    if (updateType === "ADD") {
        streamList.forEach(i => {
              zg.getPlayerInstance(i.streamID).play();
        })
    } else {
       streamList.forEach(i => {
              zg.getPlayerInstance(i.streamID).stop();
       })
    }
});

4 小程式音視頻通話的常用功能

4.1 常見音視頻房間通知回調

4.1.1 我在房間內的連接狀態變化通知

roomStateUpdate:本地調用 loginRoom 加入房間時,您可通過監聽該回調實時監控自己在本房間內的連接狀態。

用戶可以在回調中根據不同狀態處理業務邏輯。

zg.on('roomStateUpdate', (roomID, state, errorCode, extendedData) => {
    if (state == 'DISCONNECTED') {
        // 與房間斷開了連接
	// ...
    }

    if (state == 'CONNECTING') {
        // 與房間嘗試連接中
	// ...
    }

    if (state == 'CONNECTED') {
        // 與房間連接成功
	// ...
    }
})

4.png

4.1.2 其他用戶進出房間的通知

roomUserUpdate:同一房間內的其他用戶進出房間時,您可通過此回調收到通知。登錄房間後,當房間內有用戶新增或刪除時,SDK 會通過該回調通知。

只有調用 loginRoom 介面登錄房間時傳入 ZegoRoomConfig 配置,且 “userUpdate” 參數取值為 “true” 時,用戶才能收到 roomUserUpdate 回調。

// 用戶狀態更新回調
zg.on('roomUserUpdate', (roomID, updateType, userList) => {
    console.warn(
        `roomUserUpdate: room ${roomID}, user ${updateType === 'ADD' ? 'added' : 'left'} `,
        JSON.stringify(userList),
    );
});

4.1.3 房間內流狀態變更的通知

roomStreamUpdate:流狀態更新回調。登錄房間後,當房間內有用戶新推送或刪除音視頻流時,SDK 會通過該回調通知。

// 流狀態更新回調
zg.on('roomStreamUpdate', async (roomID, updateType, streamList, extendedData) => {
    if (updateType == 'ADD') {
        // 流新增,開始拉流
    } else if (updateType == 'DELETE') {
        // 流刪除,停止拉流
    }
});

4.1.4 用戶推送音視頻流的狀態通知

  • 推流狀態事件

微信小程式會在 <live-pusher> 的 bindstatechange 綁定的方法中通知出推流狀態事件,開發者需要:

a. 在 bindstatechange 綁定的回調函數中,調用 SDK 的 updatePlayerState 介面將推流狀態事件透傳給 SDK。

b. 在 SDK 的 publisherStateUpdate 回調中處理推流的開始、失敗狀態。

// live-pusher 綁定推流事件
onPushStateChange(e) {
    // 透傳推流事件給 SDK
    zg.updatePlayerState(this.data.publishStreamID, e);
},

// 推流後,伺服器主動推過來的,流狀態更新
// NO_PUBLISH:未推流狀態,PUBLISH_REQUESTING:正在請求推流狀態,PUBLISHING:正在推流狀態
// state: "PUBLISHING" | "NO_PUBLISH" | "PUBLISH_REQUESTING";
zg.on("publisherStateUpdate", (result) => {
    console.log("publishStateUpdate", result.state);
});
  • 推流網路事件

微信小程式會在 <live-pusher> 的 bindnetstatus 綁定的方法中通知出推流網路事件,開發者需要在對應的小程式回調中,調用 SDK 的 updatePlayerNetStatus 介面將推流網路事件透傳給 SDK。

// live-pusher 綁定網路狀態事件
onPushNetStateChange(e) {
    //透傳網路狀態事件給 SDK
    zg.updatePlayerNetStatus(this.data.publishStreamID, e);
},


// SDK 推流網路質量回調
zg.on("publishQualityUpdate", (streamID, publishStats) => {
    console.log("publishQualityUpdate", streamID, publishStats);
});

4.1.5 用戶拉取音視頻流的狀態通知

  • 拉流狀態事件

微信小程式會在 <live-player> 的 bindstatechange 綁定的方法中通知出拉流狀態事件,開發者需要:

a. 在 bindstatechange 綁定的回調函數中,調用 SDK 的 updatePlayerState 介面將拉流狀態事件透傳給 SDK。

b. 在 SDK 提供的 playerStateUpdate 回調中處理拉流的開始或失敗狀態。

// live-player 綁定的拉流事件
onPlayStateChange(e) {
    // 透傳拉流事件給 SDK
    zg.updatePlayerState(e.currentTarget.id, e);
},

// 伺服器主動推過來的流的播放狀態
// 視頻播放狀態通知;state: "NO_PLAY" | "PLAY_REQUESTING" | "PLAYING";
zg.on("playerStateUpdate", (result) => {
    console.log("playStateUpdate", result.state);
});
  • 拉流網路事件

微信小程式會在 <live-player> 的 bindnetstatus 綁定的方法中通知出拉流網路事件,開發者需要在對應的小程式回調中,調用 SDK 的 updatePlayerNetStatus 介面將推流網路事件透傳給 SDK。

// live-player 綁定網路狀態事件
onPlayNetStateChange(e) {
    // 透傳網路狀態事件給 SDK
    zg.updatePlayerNetStatus(playStreamID, e);
},

// SDK 拉流網路質量回調
zg.on("playQualityUpdate", (playStreamID, playStats) => {
    console.log("playQualityUpdate", playStreamID, playStats);
});

4.2 停止fang jian音視頻通話

4.2.1 停止推送/拉取音視頻流

1. 停止推流

調用 SDK 的 getPusherInstance 介面獲取推流實例,並調用推流實例的 stop 方法停止推流。

// 停止推流
zg.getPusherInstance().stop();

2. 停止拉流

調用 SDK 的 getPlayerInstance 介面獲取拉流實例,並調用推流實例的 stop 方法停止拉流。

// 停止拉流
zg.getPlayerInstance(streamID).stop();

4.2.2 退出房間

調用 SDK 的 logoutRoom 介面退出房間。

zg.logoutRoom(roomID);

5 調試視頻通話功能

在真機中運行項目,運行成功後,可以看到本端視頻畫面。

為方便體驗,ZEGO 提供了一個 Web 端調試示例 ,在該頁面下,輸入相同的 AppID、RoomID,輸入一個不同的 UserID,即可加入同一房間與真機設備互通。當成功開始音視頻通話時,可以聽到遠端的音頻,看到遠端的視頻畫面。

6 視頻通話 API 調用時序

整個推拉流過程的 API 調用時序可參考下圖:
下載5.png

音視頻場景解決方案分享,更多詳情可搜索官網(https://zegoguanwang.datasink.sensorsdata.cn/t/pB)
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • AU上傳ipa出現下圖紅框提示說明成功上傳,如果App Store後臺沒有出現構建版本, 請登錄 apple賬號對應的郵箱查看反饋,特別留意垃圾郵箱,無論成功還是失敗,apple都會發郵件 一、首先登錄iTunes Connect 後臺、查看ipa構建情況 https://appstoreconne ...
  • 現如今,人們在網上聊天、發帖時越來越愛用表情包,表情包一方面是一種個性化的表達方式,另一方面更能傳達出當下的心理活動,可以說在網路社交中表情包是一個不可或缺的存在。加上近年來元宇宙的興起,3D虛擬形象廣泛應用,用戶可以通過自己的表情來控制虛擬形象的表情,做一系列專屬的表情包,更加生動形象。 那麼,如 ...
  • ##vue+element-ui後臺管理系統模板 前端:基於vue2.0+或3.0+加上element-ui組件框架 後端:springboot+mybatis-plus寫介面 通過Axios調用介面完成數據傳遞 通過router路由完成各頁面的跳轉 ###全局配置 App.vue <templat ...
  • 好家伙,繼續優化, 好家伙,我把我的飛機大戰發給我的小伙伴們玩 期待著略微的贊賞之詞,然後他們用手機打開我的給他們的網址 然後點一下飛機就炸了。 游戲體驗零分 (滑鼠點擊在移動端依舊可以生效) 好了所以我們來優化一下這個觸屏移動事件 由於沒有參考,就去翻文檔了 觸摸事件分三個:touchstart、 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 1. 定義整體結構 先寫出構造函數,將Promise向外暴露 /* 自定義Promise函數模塊:IIFE */ (function (window) { /* Promise構造函數 executor:執行器函數 */ function ...
  • 一.分類 1.1 頁面佈局 首先創建cate的分支 定義基本結構,因為是兩個需要滾動的區域,所以這裡要用到組件 ==scroll== 這個組件如果是y scroll那就要固定高度,x scroll那就要固定寬度 這裡有個問題就是,我們應該把高度限定在整個屏幕高度 這裡用到uniapp一個api == ...
  • 每日3題 1 以下代碼執行後,控制臺中的輸出內容為? class A { static a = "123"; } class B extends A {} console.log(B.a); 2 以下3句語句,哪句是合法的 1.toString(); 1..toString(); 1...toStr ...
  • 1. 安裝sass 較新的版本不需要配置sass-loader等一系列插件,安裝即用。 npm install --save-dev sass 2. 編寫App.tsx中的基本DOM 更改app.css為app.scss,並刪除其中全部內容 使用如下代碼替換app.tsx中的內容 import ". ...
一周排行
    -Advertisement-
    Play Games
  • Github / Gitee QQ群(1群) : 813100564 / QQ群(2群) : 579033769 視頻教學 介紹 MiniWord .NET Word模板引擎,藉由Word模板和數據簡單、快速生成文件。 Getting Started 安裝 nuget link : https:// ...
  • Array.Sort Array類中相當實用的我認為是Sort方法,相比起冗長的冒泡排序,它的出現讓排序更加的簡化 結果如下: 還可以聲明一個靜態方法用來專門調用指定數組排序,從名為 array 的一維數組中 a 索引處開始,到 b 元素 從小到大排序。 註意: a + b 不能大於 array 的 ...
  • 前言 在上一篇文章CLR類型系統概述里提到,當運行時掛起時, 垃圾回收會執行堆棧遍歷器(stack walker)去拿到堆棧上值類型的大小和堆棧根。這裡我們來翻譯BotR里一篇專門介紹Stackwalking的文章,希望能加深理解。 順便說一句,StackWalker在中文里似乎還沒有統一的翻譯,J ...
  • 使用過 nginx 的小伙伴應該都知道,這個中間件是可以設置跨域的,作為今天的主角,同樣的 反向代理中間件的 YARP 毫無意外也支持了跨域請求設置。 有些小伙伴可能會問了,怎樣才算是跨域呢? 在 HTML 中,一些標簽,例如 img、a 等,還有我們非常熟悉的 Ajax,都是可以指向非本站的資源的 ...
  • 什麼是Git Git 是一個開源的分散式版本控制系統,用於敏捷高效地處理任何或小或大的項目。 Git 是 Linus Torvalds 為了幫助管理 Linux 內核開發而開發的一個開放源碼的版本控制軟體。 Git 與常用的版本控制工具 CVS, Subversion 等不同,它採用了分散式版本庫的 ...
  • 首先CR3是什麼,CR3是一個寄存器,該寄存器內保存有頁目錄表物理地址(PDBR地址),其實CR3內部存放的就是頁目錄表的記憶體基地址,運用CR3切換可實現對特定進程記憶體地址的強制讀寫操作,此類讀寫屬於有痕讀寫,多數驅動保護都會將這個地址改為無效,此時CR3讀寫就失效了,當然如果能找到CR3的正確地址... ...
  • 說明 onlyoffice為一款開源的office線上編輯組件,提供word/excel/ppt編輯保存操作 以下操作均基於centos8系統,officeonly鏡像版本7.1.2.23 鏡像下載地址:https://yunpan.360.cn/surl_y87CKKcPdY4 (提取碼:1f92 ...
  • 二叉樹查找指定的節點 前序查找的思路 1.先判斷當前節點的no是否等於要查找的 2.如果是相等,則返回當前節點 3.如果不等,則判斷當前節點的左子節點是否為空,如果不為空,則遞歸前序查找 4.如果左遞歸前序查找,找到節點,則返回,否繼續判斷,當前的節點的右子節點是否為空,如果不為空,則繼續向右遞歸前 ...
  • ##Invalid bound statement (not found)出現原因和解決方法 ###前言: 想必各位小伙伴在碼路上經常會碰到奇奇怪怪的事情,比如出現Invalid bound statement (not found),那今天我就來分析以下出現此問題的原因。 其實出現這個問題實質就是 ...
  • ###一、背景知識 爬蟲的本質就是一個socket客戶端與服務端的通信過程,如果我們有多個url待爬取,只用一個線程且採用串列的方式執行,那隻能等待爬取一個結束後才能繼續下一個,效率會非常低。 需要強調的是:對於單線程下串列N個任務,並不完全等同於低效,如果這N個任務都是純計算的任務,那麼該線程對c ...