Go紅隊開發—web網絡編程

文章目錄

  • web網絡編程
    • Req
      • 快速請求
    • 調試
      • DevMode
      • DebugLog
      • TraceInfo瓶頸分析
    • 控制請求與響應
      • 控制請求的字段內容
      • 控制調試打印的內容
      • 分開dump請求與響應部分
      • 請求體設置
    • 作用范圍級別
      • 設置參數查詢
      • URL 路徑參數
      • 表單請求設置
      • 請求頭設置
    • 判斷響應狀態碼
    • 解析數據
      • SetSuccessResult
      • gjson
      • 響應數據解析練習
    • Cookie
      • 默認行為
      • 禁?Cookie
      • 存儲Cookie
    • 證書校驗
      • 無視風險
      • 配置證書
    • Auth身份認證
    • 文件上傳下載
      • 上傳文件
      • 下載文件
      • 多線程下載練習

web網絡編程

tips:這一章的鋪墊比較重要,這章過后就是一些安全工具如何編寫以及一些poc、exp的工具編寫。

Req

簡單的請求

  • 客戶端創建
  • 請求設置

func reqHttp() {client := req.C()       //客戶端創建res, err := client.R(). //請求設置,這個點的意思鏈式調用,后續在控制請求中會講Get("https://httpbin.org/uuid")if err != nil {log.Println("請求失敗:", err)}fmt.Println(res)}

快速請求

  • MustGet
    測試使用可以,正式開發不建議使用,可控性差

// 快速使用,一般用在test的時候func testHttp() {resp := req.MustGet("https://httpbin.org/uuid") //這里就是發起了一次請求fmt.Println(resp.String())                      //第一種打印*req.Response類型的響應體fmt.Println(string(resp.Bytes()))               //第二種打印*req.Response類型的響應體}

調試

DevMode

DevMode是直接開啟全局調試,自動打印出來

// 調試模式func devModeReq() {//使用req進行請求,所以req的調試模式也是在req啟動req.DevMode()                                   //開啟調試模式,就會打印出來請求的過程以及響應內容req.SetCommonBasicAuth("username", "password"). //設置用戶名和密碼SetTimeout(5 * time.Second). //設置超時時間SetUserAgent("my-ua")resp := req.MustGet("https://httpbin.org/uuid")//沒有開啟調試模式但是想打印的話就正常打印fmt.Println(resp.String())        //第一種打印*req.Response類型的響應體fmt.Println(string(resp.Bytes())) //第二種打印*req.Response類型的響應體}

DebugLog

DebugLog 是跟蹤請求的過程

他可以看到你整個請求的過程,重定向的信息等等

// 查看請求的過程發生了什么func deLog() {client := req.C().EnableDebugLog() //開啟DebugLogclient.R().Get("http://baidu.com/s?wd=req")}

TraceInfo瓶頸分析

trace跟蹤信息

func traceReq() {// Enable trace at request levelclient := req.C()resp, err := client.R().EnableTrace(). //開啟瓶頸分析Get("https://api.github.com/users/imroc")if err != nil {log.Fatal(err)}trace := resp.TraceInfo()  //trace跟蹤信息fmt.Println(trace.Blame()) //分析總結(請求減慢的原因歸咎)fmt.Println(trace) // 打印內容}

控制請求與響應

控制請求的字段內容

這里做一個了解,后面會詳細說一下作用范圍,這里就過一遍即可,知道哪些字段可控(其實都可控)

func controlReq() {client := req.C().SetUserAgent("my-ua"). //設置ua頭,在client中設置,在下面的R()中設置不了//EnableDumpAllToFile("log.txt") //將請求的信息寫到該文件中//捕獲請求和響應,// 想要看的更加詳細就可以開啟dump所有內容,// 就能夠看到我們是不是真的改變了請求內容EnableDumpAll()parms := map[string]string{"a": "123","b": "hello",}resp, err := client.R(). //拿到請求體SetPathParam("usernamae", "imroc"). //設置請求路徑,用username作為占位符SetPathParam("xxx", "test").        //再次設置,用xxx作為占位符SetQueryParam("a", "12").           //設置請求參數,a=12SetQueryParams(parms).              //用map來作為請求參數,用于多個參數的時候SetHeader("mycookie", "test").      //設置請求頭SetHeader("mysession", "test2").    //設置請求頭SetBody("body=world").              //設置請求體Get("https://httpbin.org/uuid")//以上設置暫時在初期階段夠用了。if err != nil {log.Println("請求出錯:", err)}fmt.Println(resp)}

控制調試打印的內容

  • SetCommonDumpOptions:控制輸出的內容
  • EnableDumpAllToFile:dump到文件中

輸出的log2.txt文件內容如下

// 控制調試打印的內容// 全局應用func controlDevOptions() {client := req.C()opt := &req.DumpOptions{Output:         os.Stdout, //標準輸出RequestHeader:  false,     //不輸出請求頭RequestBody:    false,     //不輸出請求體ResponseHeader: true,      //輸出響應頭ResponseBody:   true,      //輸出響應體Async:          false,     //不進行異步輸出}client.SetCommonDumpOptions(opt).EnableDumpAllToFile("log2.txt")client.R().Get("https://httpbin.org/uuid")}

分開dump請求與響應部分

  • var bufReq, bufResp bytes.Buffer:需要用變量來接收請求與響應不同部分
  • SetDumpOptions:控制dump導出部分
// 分別打印請求與響應部分// 在R中控制調試打印的內容,只作用與本次R請求,與上面的client直接全局設置的不同func controlReqRespOutPut() {client := req.C()var bufReq, bufResp bytes.Bufferclient.R().// 不開啟的話就會導致無法轉儲出去單獨打印請求體響應體EnableDump(). //EnableDump啟用轉儲,包括請求和響應的所有內容。SetDumpOptions(&req.DumpOptions{RequestOutput:  &bufReq,  //將請求體輸出到這里ResponseOutput: &bufResp, //將響應體輸出到這里RequestHeader:  true,RequestBody:    true,ResponseHeader: true,ResponseBody:   true,}).SetBody("body=hello+world!").Get("https://httpbin.org/uuid")fmt.Println("請求體")fmt.Println(bufReq.String())fmt.Println("響應體")fmt.Println(bufResp.String())}

請求體設置

SetBody 可以接受任意類型

PS: EnableDumpAllWithoutResponse因為開啟了調試模式,所以會打印很多東西,但是可以忽略響應打印出來結果,所以這函數就是忽略響應結果,只看請求。

在編寫一些poc/exp的時候可能需要在body部分,需要加入一些結構體或者map數據,他會根據你設置的content-type來判斷解析成json還是xml格式,如果都不設置的話他會默認將這些類型解析為json數據body。

400

添加上content-type類型后就自動轉了,比較方便(后面還有更方便的)
400

  • SetBodyXXX
    不用設置content-type也能直接轉想要的格式了
    400

以下是代碼:

// 設置請求體中的一些騷姿勢func setbodyHttp() {type test struct {Name stringAge  int}t := test{Name: "zhangsan",Age:  123,}client := req.C().DevMode().EnableDumpAllWithoutResponse()client.R().SetBody(t).SetContentType("application/xml").Get("https://httpbin.org/uuid")client2 := req.C().DevMode().EnableDumpAllWithoutResponse()client2.R().SetBodyXmlMarshal(t).Get("https://httpbin.org/uuid")}

作用范圍級別

在設置請求參數的時候,有分兩種作用范圍設置,一種全局(客戶端級別),另一種就是只作用與這一個client的對象(請求級別)。

以下就盡量快速過為妙,上面學的時候已經有很多函數都用過了,沒必要花太多時間,知道即可,下面作為一個知識字典,以后方便查閱。


請記住:

  • 用req.C().R()來設置的請求級別
  • 用req.C()來設置的是全局級別(也即客戶端級別client)

設置參數查詢

請求級別:

  • SetQueryParam:SetQueryParam("test", "123") 即 httpxxx?test=123
    設置多個的時候僅僅對最后一個設置的生效
  • SetQueryParams:SetQueryParams接收map類型設置多個變量,由于map的鍵唯一,所以多個同名的話也只會作用一個。
  • SetQueryString:直接給查詢參數即可,SetQueryString("test1=123&test2=456")
    這個不會產生什么重復覆蓋問題,字符串給啥他就拼接到你url中去
  • AddQueryParam:AddQueryParam("key", "value1") 如果該參數已存在,它不會覆蓋原有的值,這個一般用在你臨時需要添加什么查詢參數的時候可以用,而且不會覆蓋原有的同名鍵值

全局級別:

  • SetCommonQueryParam
  • SetCommonQueryParams
  • SetCommonQueryString
  • AddCommonQueryParam

URL 路徑參數

請求級別:

  • SetPathParam:SetPathParam("username", "zhangsan")
  • SetPathParams:接收map類型

全局級別:

  • SetCommonPathParam
  • SetCommonPathParams

表單請求設置

請求級別:

  • SetFormData:接收map類型,但是同一個鍵只能有一個
  • SetFormDataFromValues::
    代碼如下所示,但是注意的是url.Values是net/url包,所以要注意這個url不是我定義的,拿來就用即可。
client3 := req.C().DevMode().EnableDumpAllWithoutResponse()v := url.Values{"p": []string{"hello", "world", "!"},
}
client3.R().SetFormDataFromValues(v).Post("https://httpbin.org/post")

  • multipart ?式提交表單:EnableForceMultipart
client3 := req.C().DevMode().EnableDumpAllWithoutResponse()
v := url.Values{"p": []string{"hello", "world", "!"},
}
client3.R().EnableForceMultipart().SetFormDataFromValues(v).Post("https://httpbin.org/post")


全局級別:

  • SetCommonFormData
  • SetCommonFormDataFromValues

請求頭設置

請求級別:

  • SetHeader:自動將你傳進的header鍵的首字母大寫
  • SetHeaders:接收map類型,自動將你傳進的header鍵的首字母大寫
  • SetHeaderNonCanonical:你給什么樣就輸出什么樣,不會自動給你首字母大寫
  • SetHeadersNonCanonical:你給什么樣就輸出什么樣,不會自動給你首字母大寫
  • SetHeaderOrder:控制header的順序,因為有的服務端可能會對header的順序判斷是否允許請求,設置了SetHeaderOrder,他就會按照你給定的順序進行排序請求過去。
request.SetHeaderOrder( "cookie", "ssession", "test","ua", 
)

全局級別

  • SetCommonHeaderNonCanonical
  • SetCommonHeadersNonCanonical

判斷響應狀態碼

沒啥好說的,都到這了這些應該對于各位師傅來說都是一眼就學會的基操了。

  • resp.IsSuccessState
  • resp.IsErrorState
  • resp.StatusCode
// 判斷響應狀態碼func judgeStatusCode() {client := req.C()resp, err := client.R().Get("http://www.baidu.com")if err != nil {log.Println("請求失敗:", err)}if resp.IsSuccessState() {fmt.Println("ok")}if resp.IsErrorState() {fmt.Println("error")}//可以通過響應對應的代碼判斷if resp.StatusCode != http.StatusOK { //http有一個const變量,里面有很多對應的響應碼,自行查看即可(文件:http\status.go)fmt.Println("error")}//打印響應體fmt.Println(resp.Bytes())  //bytes打印fmt.Println(resp.String()) //string打印}

解析數據

SetSuccessResult

  • SetSuccessResult:SetSuccessResult會?動解析你給的結構體或map

  • SetErrorResult
    這里一同把SetErrorResult也講了,可以自定義錯誤解析到你自己定義的錯誤中去,沒啥好說感覺也時,定義好了就直接給到SetErrorResult即可(詳細見代碼處)

運行結果如下圖所示:

  // 接收響應回來的json數據type user struct {Login string `json:login`Id    int    `json:id`Name  string `json:name`}// 接收錯誤響應type errorResp struct {Mess string `json:message`}// 解析響應的json數據// SetSuccessResult?動解析結構體或mapfunc getHttpJson() {client := req.C()var respUser uservar respError errorRespres, err := client.R().SetSuccessResult(&respUser). //接收響應回來的json數據,同時也可以解析mapSetErrorResult(&respError).  //若請求錯誤將錯誤信息存儲到該結構體中Get("https://api.github.com/users/whoisdhan")if err != nil {log.Println("代碼請求出錯:", err)return}if res.IsSuccessState() {fmt.Println("請求成功")fmt.Println(respUser.Login)fmt.Println(respUser.Id)fmt.Println(respUser.Name)} else if res.IsErrorState() {fmt.Println("網絡請求出錯:", respError.Mess)} else {fmt.Println("未知錯誤:", res.StatusCode)}}

gjson

  • 字段名.:表示讀取該字段,字段名.0表示讀取該鍵名下的數據的第一個字段,字段名.1讀取第二個
  • *?:gjson支持通配符:像在linux命令行中使用的通配符那樣用就行,a*a收尾為a的都匹配
  • #:表示該字段所有元素。
    • 在結尾:字段名1.字段名2.# 表示返回字段名2的數組長度
    • 在中間,即#后面還有內容:字段名1.#.字段名2 表示返回字段名2所有元素
    • 原因:這也是為啥我叫他#的意思是表示該字段所有元素,在結尾就是返回長度,不在就是返回整個數組列表

gjson解析json數據可提取指定字段,不用定義結構體

gjson非常適合提取幾個字段出來之類的,方便的一筆。

// 由于我們的http請求回來的數據能方便的轉為string所以也能用gjson// 模擬數據const httpjson = `{"name":{"first":"Tom", "last": "Anderson"},"age": 37,"children": ["Sara", "Alex", "Jack"],"fav.movie": "Dear Hunter","friends": [{"first": "Dale", "last":"Murphy", "age": 44, "nets": ["ig", "fb", "tw"]},{"first": "Roger", "last": "Craig", "age": 68, "nets": ["fb", "tw"]},{"first": "Jane", "last": "Murphy", "age": 47, "nets": ["ig", "tw"]}]}`func gjsonHttp() {//先簡單用正常的httpjson數據嘗試一下client := req.C()resp, _ := client.R().Get("https://api.github.com/users/whoisdhan")login := gjson.Get(resp.String(), "login")id := gjson.Get(resp.String(), "id")name := gjson.Get(resp.String(), "name")fmt.Println(login, id, name)//用httpjson測試數據fmt.Println(gjson.Get(httpjson, "name.first"))fmt.Println(gjson.Get(httpjson, "friends.#.first"))fmt.Println(gjson.Get(httpjson, "friends.#.nets"))fmt.Println(gjson.Get(httpjson, "fav\\.movie")) //字段存在符號的話用\\轉義}

響應數據解析練習

這里的解析可能有點主觀了,可以按照自己的想法,盡量不要被我帶偏,我的不一定是最佳的

// 練習
// 1. 獲取?戶信息 https://api.github.com/users/{username}
// 2. 獲取倉庫列表信息 https://api.github.com/users/{username}/repos
// 3. 用戶與倉庫列表信息:普通讀取json、Unmarshal轉結構體?式解析、gjson讀取
// 4. 用戶與倉庫列表信息:格式化輸出到控制臺
// 5. 倉庫列表信息保存到本地 JSON ?件中。
//這里就固定拿倉庫列表信息如下信息:
// (也可以隨便獲取你想要的字段)
//  用戶信息{login、id、url、name、email}
//  倉庫列表信息{name、owner.login、description}
  • myMarshalIndent:為了方便格式化,我自己寫了一個格式化函數方便不同方式保存的時候進行格式化
    格式化都需要先轉到map中存儲才能夠格式化正常的json數據出來,格式化好了就任意你想干啥就干啥了
  • 需要注意的細節:
    有的json他是列表包裹著[],所以轉的時候要注意判斷用map[string]interface{}還是[]map[string]interface{}
  • json.MarshalIndent:他可以對map列表進行格式化的,因為那樣也時一個正常的json數據,只不過你需要用[]map[string]interface{}列表類型進行存儲(這里是一個很重要的細節,開發過程中如果不注意很容易導致崩潰)

我用了三個函數完成這個練習:

  • normalGetJson:普通讀取,就是解析出來后整個data就直接write到文件中
  • UnmarshalToStruct:Unmarshal轉結構體?式解析,這里使用了上面學到的SetSuccessResult,給到結構體后進行解析,這樣也很方便
  • gjsonOutput:這里就是使用gjson了,gjson很方便,但是面對比較多字段提取的時候還是比較繁瑣。
    這里復習了一個細節:map想要后續不斷地賦值的話就需要進行make空間出來才行,如果是[]map的話需要給定一個空間范圍,0也行,你要說明這是一個切片。如果你只是單單定義一個map變量或者map切片變量,后續是無法使用的。
    還有一個細節就是:make出來的空間是固定的,如果你要make出來的空間作為一個臨時變量賦值給其他變量的時候要注意了,用了一個空間就不要繼續用了,因為你將一個空間給了多個變量的話,那么那些變量都指向你這一個空間,那么他們的值其實都一樣。

運行結果:這里就放幾個保存出來的文件截圖

  • normalGetJson:

  • UnmarshalToStruct

  • gjsonOutput

示例代碼:

// 用戶信息{login、id、url、name、email}// 倉庫列表信息{name、owner.login、description}func gjsonOutput(username string) {//gjson就十分簡單了,只需要拿到響應json數據即可取出來看mapUserData := make(map[string]interface{}) //用戶信息存儲//倉庫信息存儲,// 由于倉庫是數組列表所以要給一個初始長度// 因為可能倉庫為空的,所以就初始化為0即可mapReposData := make([]map[string]interface{}, 0)fmt.Println("-------------------用戶信息-------------------")client := req.C()resp, err := client.R().SetPathParam("username", username).Get("https://api.github.com/users/{username}")if err != nil {log.Println("代碼請求失敗:", err)}//gjson想要寫入文件就只能轉儲到struct或者map中mapUserData["login"] = gjson.Get(resp.String(), "login").String()mapUserData["id"] = gjson.Get(resp.String(), "id").String()mapUserData["url"] = gjson.Get(resp.String(), "url").String()mapUserData["name"] = gjson.Get(resp.String(), "name").String()mapUserData["email"] = gjson.Get(resp.String(), "email").String()data, err := json.MarshalIndent(mapUserData, "", "\t")if err != nil {log.Println("格式化失敗:", err)}fmt.Println(string(data))// test := gjson.Get(resp.String(), "login")// fmt.Println(test)fmt.Println("---------------------------------------------")fmt.Println("-------------------倉庫信息-------------------")client = req.C()resp, err = client.R().SetPathParam("username", username).Get("https://api.github.com/users/{username}/repos")if err != nil {log.Println("代碼請求失敗:", err)}// 倉庫列表信息{name、owner.login、description}arr := gjson.Get(resp.String(), "#.name")for i, j := range arr.Array() {//一定要放到這里來,因為make指向同一個空間,// 如果你在for外面定義mapTmpRepos造成取到的值會全部變成最后一個值,// 因為同一個空間他會同步改變你map切片append里面所有的內容,// 因為同一個空間嘛mapTmpRepos := make(map[string]interface{})mapTmpRepos["name"] = j.String()mapTmpRepos["login"] = gjson.Get(resp.String(), (strconv.Itoa(i) + ".owner.login")).String()mapTmpRepos["description"] = gjson.Get(resp.String(), (strconv.Itoa(i) + ".description")).String()mapReposData = append(mapReposData, mapTmpRepos)}data, err = json.MarshalIndent(mapReposData, "", "\t")if err != nil {fmt.Println("格式化失敗:", err)}fmt.Println(string(data)) //格式化的內容打印到終端file, err := os.OpenFile("gjson.json", os.O_CREATE|os.O_RDWR|os.O_RDWR, 0666)if err != nil {log.Println("打開文件失敗:", err)}_, err = file.Write(data)if err != nil {log.Println("導出json失敗:", err)}}

Cookie

請求級別:

  • SetCookies
SetCookies(&http.Cookie{Name:  "hacker",Value: "aaa",
})

全局級別:

  • SetCommonCookies
SetCommonCookies(&http.Cookie{Name:  "Global",Value: "dddd",
})

默認行為

就是當你請求的服務端響應了set-cookie回來后,當你再次請求的時候就會攜帶上這個set-cookie回來的cookie鍵值去請求。

禁?Cookie

  • SetCookieJar(nil)

存儲Cookie

安裝

go get -u github.com/juju/persistent-cookiejar

使用

func saveCookies() {jar, err := cookiejar.New(&cookiejar.Options{Filename: "cookies.json",})if err != nil {log.Println("保存失敗")}defer jar.Save()client := req.C().SetCookieJar(jar).DevMode()client.R().Get("http://www.baidu.com")}

證書校驗

無視風險

針對一些不安全的網站請求可能會請求失敗,所以需要忽略證書的校驗

兩種方式可以忽略:

client := req.C().DevMode().EnableDumpAllWithoutResponse()
//忽略證書風險
//第一種
//client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
//第二種
client.TLSClientConfig.InsecureSkipVerify = true
_, err := client.R().Get("https://self-signed.badssl.com/")
if err != nil {fmt.Println("代碼請求失敗:", err)
}

配置證書

配置好風險站點的證書即可訪問風險站點
證書需要訪問網站的時候在瀏覽器的小鎖中下載即可(具體不演示了,自行百度網站證書如何下載)

  • 證書文件配置
client := req.C().DevMode().EnableDumpAllWithoutResponse()
//可以同時配置多個網站的證書
client.SetRootCertsFromFile("cert1.crt","cert2.crt","cert3.crt")
  • 證書內容配置
    在下載了證書之后,可以編輯證書將里面的內容配置進來也行
client := req.C().DevMode().EnableDumpAllWithoutResponse()
client.SetRootCertFromString("-----BEGIN CERTIFICATE-----")

Auth身份認證

  • SetBasicAuth
  • SetDigestAuth
    區別:
    一個Basic認證,一個Digest認證
    Digest認證比較安全,請求被攔截了攻擊者無法直接獲取密碼
    但是Basic的請求被攔截了就是直接獲取到密碼
    服務端支持哪一個?
    檢查WWW-Authenticate響應頭,返回Basic 還是 Digest就知道支持哪一個。
func authHttp() {client := req.C().DevMode().EnableDumpAllWithoutResponse()client.R().SetBasicAuth("username", "password").Get("https://httpbin.org/uuid")client2 := req.C().DevMode().EnableDumpAllWithoutResponse()client2.R().SetDigestAuth("username2", "password2").Get("https://httpbin.org/uuid")}

文件上傳下載

上傳文件

  • SetFile
  • SetFiles:接收map類型,上傳多個文件
  • SetUploadCallback:顯示上傳進度
func uploadFileHttp() {//簡單上傳client := req.C().DevMode().EnableDumpAllWithoutResponse()callback := func(info req.UploadInfo) { //顯示上傳進度fmt.Printf("\n文件名:%q\n已上傳:%.2f%%\n",info.FileName,float64(info.UploadedSize)/float64(info.FileSize)*100.0)}client.R().//SetFile("filename", "cookies.json").SetFiles(map[string]string{"test":  "test.txt","test2": "test2.txt","test3": "test3.txt",}).SetUploadCallback(callback). //使用該函數:顯示上傳進度Post("https://httpbin.org/post")}

下載文件

  • SetOutputFile:這里是指定下載的文件路徑
  • SetOutputDirectory:設置下載的默認路徑,即SetOutputFile可以只給文件名,自動存在該路徑下
  • SetDownloadCallback:顯示下載進度
func downloadFileHttp() {client := req.C() //.DevMode().EnableDumpAllWithoutResponse()callback := func(info req.DownloadInfo) {if info.Response.Response != nil { //響應不為空fmt.Printf("\n已下載:%.2f%%\n",float64(info.DownloadedSize)/float64(info.Response.ContentLength)*100.0)}}  client.R().//這里是指定文件路徑SetOutputFile("./baidu.html").Get("http://127.0.0.1/xxx.txt")client2 := req.C().//這里可以設置默認下載目錄,后面直接download的時候就不用指定路徑了,給文件名即可SetOutputDirectory("./").DevMode().EnableDumpAllWithoutResponse()client2.R().SetOutputFile("baidu2.html").SetDownloadCallback(callback).Get("http://127.0.0.1/xxx.txt")}

多線程下載練習

  • NewParallelDownload:創建一個多線程下載客戶端
  • 其他函數在注釋中標明了。
func threatDownload() {client := req.C()err := client.NewParallelDownload("http://xxxxx.xxxx.xxx/xxx.iso").SetConcurrency(5).               //設置 5 個線程 并行下載SetFileMode(0777).               //設置 文件權限(可讀可寫可執行)。SetOutputFile("xxx.iso").        //設定最終 存儲文件名SetSegmentSize(1024 * 1024 * 5). //每個線程下載 5MB 的數據塊SetTempRootDir("./tmp").         //這個是下載的時候指定的臨時存儲目錄Do()if err != nil {log.Println("下載失敗:", err)return}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/73361.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/73361.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/73361.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PawSQL for TDSQL:騰訊云TDSQL數據庫性能優化全攻略

TDSQL 作為騰訊云推出的分布式數據庫,憑借其高擴展性、高可用性和高性能等優勢,廣泛應用于金融、互聯網、政務等領域。隨著業務的不斷增長和數據量的爆炸式增長,如何優化 TDSQL 數據庫的性能,成為眾多企業和開發者面臨的挑戰。本文…

67.Harmonyos NEXT 圖片預覽組件之性能優化策略

溫馨提示:本篇博客的詳細代碼已發布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下載運行哦! Harmonyos NEXT 圖片預覽組件之性能優化策略 文章目錄 Harmonyos NEXT 圖片預覽組件之性能優化策略效果預覽一、性能優化概述1. 性能優化的關鍵指標…

C語言中的字符串與數組的關系

在C語言中,字符串和數組之間有著緊密的關系。理解它們的區別和聯系對于編寫高效且可靠的代碼至關重要。在本篇博文中,我們將詳細分析字符串和數組在C語言中的概念、它們的關系以及如何在編程中應用它們。 一、字符串與數組的基礎知識 1.1 數組概念 在C語言中,數組是一組相…

56.HarmonyOS NEXT 登錄模塊開發教程(十):總結與展望

溫馨提示:本篇博客的詳細代碼已發布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下載運行哦! HarmonyOS NEXT 登錄模塊開發教程(十):總結與展望 文章目錄 HarmonyOS NEXT 登錄模塊開發教程(十&a…

添加 ChatGPT/Grok/Gemini 到瀏覽器搜索引擎

添加 ChatGPT/Grok/Gemini 到瀏覽器搜索引擎 添加 ChatGPT/Grok/Gemini 到瀏覽器搜索引擎如何添加步驟 1: 打開瀏覽器設置步驟 2: 添加新搜索引擎步驟 3: 保存設置 注意事項 添加 ChatGPT/Grok/Gemini 到瀏覽器搜索引擎 在使用 ChatGPT/Grok/Gemini 進行對話時,每次…

【數據分享】2000—2024年我國省市縣三級逐月歸一化植被指數(NDVI)數據(Shp/Excel格式)

之前我們分享過2000—2024年逐月歸一化植被指數(NDVI)柵格數據(可查看之前的文章獲悉詳情),該數據來源于NASA定期發布的MOD13A3數據集!很多小伙伴拿到數據后反饋柵格數據不太方便使用,問我們能不…

oracle中OS BLOCK的含義

在Oracle數據庫中,OS BLOCK(操作系統數據塊)是指操作系統層面上的數據塊,它與Oracle數據庫內部的邏輯存儲單元BLOCK(數據塊)有所區別但密切相關。以下是對OS BLOCK的詳細解釋: 定義與概念 OS BL…

深入理解Linux網絡隨筆(七):容器網絡虛擬化--Veth設備對

深入理解Linux網絡隨筆(七):容器網絡虛擬化 微服務架構中服務被拆分成多個獨立的容器,docker網絡虛擬化的核心技術為:Veth設備對、Network Namespace、Bridg。 Veth設備對 veth設備是一種 成對 出現的虛擬網絡接口&…

電氣制作行業

電氣制作是一個涉及多種技能和工藝的領域,主要包括電氣設備的組裝、布線、調試等工作。以下是電氣制作的一般流程和相關要點: 設計與規劃 - 需求分析:明確電氣設備的功能、性能要求,以及使用環境、安全標準等因素。 - 電路設計…

【Flutter】數據庫實體類構造函數加密注意事項

源代碼: AccountEntity( {required String account, required String password,}) : account encrypter.encrypt(account,iv: iv).base64, password encrypter.encrypt(password,iv: iv).base64,; 解密代碼: static final encrypter Encrypter(AES…

PMP沖刺每日一題(30)

試題1 標題:在項目執行期間,一名團隊成員識別到由以前未被識別為項目相關方的職能經理提交了新需求。項目經理應該怎么做? A、與項目發起人開會,獲得反饋 B、啟動實施整體變更控制過程 C、對需求執行成本效益分析 D、將該職能經理添加進溝通…

一文講通鎖標記對象std::adopt_lock盲點

一文講通鎖標記對象std::adopt_lock盲點 1. 核心概念2. 代碼詳解1. 單個鎖2. 多重鎖(可以用來預防死鎖)3. 條件變量的互斥控制4. 復雜示例: 多生產者-多消費者模型(超綱了, 可不看,哈哈哈哈) 3. 小結 1. 核心概念 在C中, std::adopt_lock是一…

LVI-SAM、VINS-Mono、LIO-SAM算法的閱讀參考和m2dgr數據集上的復現(留作學習使用)

ROS一鍵安裝參考: ROS的最簡單安裝——魚香一鍵安裝_魚香ros一鍵安裝-CSDN博客 opencv官網下載4.2.0參考:https://opencv.org/releases/page/3/ nvidia驅動安裝:ubuntu18.04 安裝顯卡驅動 - 開始戰斗 - 博客園 cuda搭配使用12 cuda安裝1:Ub…

基于jspm校園安全管理系統(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 隨著信息時代的來臨,過去信息校園安全管理方式的缺點逐漸暴露,本次對過去的校園安全管理方式的缺點進行分析,采取計算機方式構建校園安全管理系統。本文通過閱讀相關文獻,研究國內外相關技術,提出了一種集安全教…

基于NXP+FPGA軌道交通3U機箱結構牽引控制單元

基于NXPFPGA軌道交通異步電機牽引控制單元(TCU-IM) 異步電機牽引控制單元(TCU-IM)用于牽引逆變器-異步電機構成的牽引電傳動系統,可采用車控或架控方式。執行高性能異步電機復矢量控制策略,具有響應迅速、有效可靠的防空轉滑行控制…

《CircleCI:CircleCI:解鎖軟件開發持續集成(CI)和持續部署(CD)高效密碼》:此文為AI自動生成

《CircleCI:CircleCI:解鎖軟件開發持續集成(CI)和持續部署(CD)高效密碼》:此文為AI自動生成 一、CircleCI 初印象 在當今軟件開發的快節奏賽道上,持續集成(CI&#xff…

基于MySQL有用戶管理的音樂播放器

基于MySQL的音樂器 帶有用戶登錄功能驗證用戶身份,用戶注冊等操作還有用戶音樂列表,以及增刪查改操作 INSERT into users(username,passwd,phone_number,created_time,role) VALUES(‘張三’,‘123456’,‘123’,‘2025-3-11’,‘1’) 三張表&#xf…

差分專題練習 ——基于羅勇軍老師的《藍橋杯算法入門C/C++》

一、1.重新排序 - 藍橋云課 算法代碼&#xff1a; #include <bits/stdc.h> using namespace std; const int N 1e5 3;int a[N], d[N], cnt[N];int main() {int n; scanf("%d", &n);for (int i 1; i < n; i) scanf("%d", &a[i]);int m…

AI+視頻監控電力巡檢:EasyCVR視頻中臺方案如何賦能電力行業智能化轉型

隨著電力行業的快速發展&#xff0c;電力設施的安全性、穩定性和運維效率變得至關重要。傳統視頻監控系統在實時性、智能化及多系統協同等方面面臨嚴峻挑戰。EasyCVR視頻中臺解決方案作為一種先進的技術手段&#xff0c;在電力行業中得到了廣泛應用&#xff0c;為電力設施的監控…

【哈希表與字符串的算法之路:思路與實現】—— LeetCode

文章目錄 兩數之和面試題01.02.判定是否為字符重排存在重復元素存在重復元素||字母異位詞分組最長公共前綴和最長回文子串二進制求和字符串相乘 兩數之和 這題的思路很簡單&#xff0c;在讀完題目之后&#xff0c;便可以想到暴力枚舉&#xff0c;直接遍歷整個數組兩遍即可&…