主頁 > 後端開發 > [Computer Networks]一個http請求的完成的全程序

[Computer Networks]一個http請求的完成的全程序

2023-01-04 07:11:44 後端開發

摘要

本文主要講述了一個 http request 請求從發出到收到 response 的整個生命周期,希望可以通過對整個流程的一個描述來梳理清楚五層網路協議的定義以及各層之間是如何協作的,

使用Golang發起一個HTTP請求

對于后端來說通過 http 請求來進行遠程呼叫是再尋常不過的事了,以 Golang 的 resty 包為例,我們通過下面這個陳述句來發起一個請求并獲得所請求的服務器的 response,簡單起見這里我們使用 GET 方法進行請求:

client := resty.New()
headers := map[string]string{
	"Connection": "Keep-Alive",
}
resp1, _ := client.R().
	EnableTrace().
	SetHeaders(headers).
	Get("https://httpbin.org/get")

fmt.Println("Request Trace Info:")
ti := resp1.Request.TraceInfo()
fmt.Println("  DNSLookup     :", ti.DNSLookup)
fmt.Println("  TCPConnTime   :", ti.TCPConnTime)
fmt.Println("  TLSHandshake  :", ti.TLSHandshake)
fmt.Println("  IsConnReused  :", ti.IsConnReused)
fmt.Println("  RemoteAddr    :", ti.RemoteAddr.String())

我們在應用層發起請求,應用層是用戶的,所以 HTTP 報文的內容都是一些人類可閱讀的 ASCII 碼點,但計算機只懂得二進制,光纖中認識光信號,所以這個 HTTP 報文還需要經過一一些處理才能穿越那些物理鏈路發送到我們的目的服務器上,首先來講講 HTTP 報文格式
image

在我們這個例子里我們的請求方法是 GET,GET 和 POST 是最常見的 HTTP 方法,除此以外還包括 DELETE、HEAD、OPTIONS、PUT、TRACE,我們沒有傳頭部欄位,也就是 HEADER , HTTP 的頭部可以分為兩種,一種是通用頭部如 Cache-Control、 Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via 等,可以通過它傳遞一些資訊,對通用頭部的擴展要求通訊雙方都支持此擴展,如果存在不支持的通用頭部,一般將會作為物體頭部處理,物體頭域包含關于物體的原資訊,物體頭包括 Allow、Content-Base、Content-Encoding、Content-Language、Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、Etag、Expires、Last-Modified、extension-header,extension-header 允許客戶端定義新的物體頭,但是這些域可能無法為接受方識別,
在這個請求里我們也沒有訊息體,URL為 https://httpbin.org/get, 是一個很簡單的 GET 請求,
http 回應報文結構和請求差不多,區別在于狀態行,狀態碼(Status-Code)主要用于機器理解,短語(Reason-Phrase)Status-Code 提供一個簡單的文本描述,主要幫助用戶理解:

  • 1xx : 資訊回應類,表示接收到請求并且繼續處理
  • 2xx : 處理成功回應類,表示動作被成功接收、理解和接受
  • 3xx : 重定向回應類,為了完成指定的動作,必須接受進一步處理
  • 4xx : 客戶端錯誤,客戶請求包含語法錯誤或者是不能正確執行
  • 5xx : 服務端錯誤,服務器不能正確執行一個正確的請求

幾個常見的狀態碼和短語:

  • 200 OK 最好的情況,即處理成功
  • 404 Not Found 不希望看到的回應之一,即找不到所請求的資源
  • 500 Internal Server Error 不希望看到的回應之二,服務端發生了錯誤

說完了 HTTP 報文,接下來我們來實踐一下,看看上面那段代碼發起一個 HTTP 請求,它的運行結果如下:
image

可以看到我們這個請求是成功了的,對方服務器回傳了 200, 短語是 OK,意味著目標服務器成功處理了我們的請求,
輸出的Request Trace Info資訊可以幫助我們理解整個請求的程序,我們一行一行地看:

DNSLookup

HTTP 報文里包含了目的服務器的地址,也就是我們上面輸入的 URL,一個 URL 由協議頭(HTTP、HTTPS、SFTP 等)+ 域名 + 資源路徑組成,在我們這個例子里協議頭為https(HTTPS = HTTP + SSL(TLS),它和 HTTP 的區別在于加了一道身份驗證所以更安全),域名是 httpbin.org ,資源路徑是 /get,也就是我們以 HTTPS 協議所約定的方式去獲取 httpbin.org 所映射的服務器上的 /get 路徑下的資源,
域名由字串組成,機器是無法讀懂的,所以我們需要一個服務去將它決議成機器能讀懂的地址,也就是 IP,而這個服務就是 DNS(Domain Name System)域名系統,它是用于實作域名和IP地址相互映射的一個分布式資料庫,這里輸出的 DNSLookup 的值就是本次請求里花費在 DNS 決議上的時間,
域名決議的程序大致如下:
image
完整的DNS決議程序有以下幾個步驟:
(1)查看瀏覽器快取(我們這里是直接通過后端來發起請求,所以沒有這一步)
當用戶通過瀏覽器訪問某域名時,瀏覽器首先會在自己的快取中查找是否有該域名對應的 IP 地址(若曾經訪問過該域名且沒有清空快取便存在),
(2)查看系統快取
當瀏覽器快取中無域名對應 IP 則會自動檢查用戶計算機系統 Hosts 檔案 DNS 快取是否有該域名對應 IP,
(3)查看路由器快取
當瀏覽器及系統快取中均無域名對應 IP 則進入路由器快取中檢查,以上三步均為客服端的 DNS 快取,
(4)查看ISP DNS 快取
當在用戶客服端查找不到域名對應 IP 地址,則將進入 ISP DNS 快取中進行查詢,比如你用的是電信的網路,則會進入電信的 DNS 快取服務器中進行查找,
(5)詢問根域名服務器
當以上均未完成,則進入根服務器進行查詢,全球僅有 13 臺根域名服務器,1 個主根域名服務器,其余 12 為輔根域名服務器,根域名收到請求后會查看區域檔案記錄,若無則將其管轄范圍內頂級域名(如.com、.cn等)服務器 IP 告訴本地 DNS 服務器,
(6)詢問頂級域名服務器
頂級域名服務器收到請求后查看區域檔案記錄,若無記錄則將其管轄范圍內權威域名服務器的 IP 地址告訴本地 DNS 服務器,
(7)詢問權威域名(主域名)服務器
權威域名服務器接受到請求后查詢自己的快取,如果沒有則進入下一級域名服務器進行查找,并重復該步驟直至找到正確記錄,
(8)保存結果至快取
本地域名服務器把回傳的結果保存到快取,以備下一次使用,同時將該結果反饋給客戶端,客戶端通過這個 IP 地址即可訪問目標Web服務器,至此,DNS 遞回查詢的整個程序結束,
通過域名決議服務我們獲得了目標服務器的 IP,它會在網路層被用到,

TCPConnTime

建立 TCP (Transmission Control Protocol) 連接所花的時間,TCP 屬于傳輸層協議,除了 TCP 外還有 UDP 也是常用的傳輸層協議,本文的傳輸層協議選擇了 TCP, 我們在應用層準備好了 HTTP 報文,然后選擇一個 TCP server 去傳輸這個 HTTP 報文給目標服務器,TCP server 通過什么方式進行與目標服務器的溝通對于應用層來說是透明的(即不可見),應用層只需要等待 TCP server 回傳的目標服務器的應答結果就好了,
TCP 是面向連接的協議,而 UDP 是無連接的,使用 TCP 進行通信的雙方在互相發送訊息之前要先建立一個連接,這個連接建立的程序被稱為三次握手,我們先來看看 TCP 報文格式:
image
埠(port),主要分為物理埠邏輯埠,我們一般說的都是邏輯端口,用于區分不同的服務,因為網路中一臺主機只有一個 IP,但是一個主機可以提供多個服務,埠號就用于區分一個主機上的不同服務,一個IP地址的埠通過16bit進行編號,最多可以有65536個埠,標識是從0~65535,
埠號分為系統埠(System Ports)0~ 1023、用戶埠(User Ports)1024~ 49151和動態埠號(Dynamic Ports)49152~65535,我們自己的服務一般都系結在注冊埠上,
系統埠(0~ 1023):也叫做公認埠(Well Known Ports),它們緊密系結(binding)于一些服務,通常這些埠的通訊明確表明了某種服務的協議,任何TCP/IP實作所提供的服務都用0-1023之間的埠號,我們的私用埠號不應該使用這個區間內的埠,除非你向IANA注冊了,例如:80埠實際上總是 http 通訊、443對應著 https(在本文中我們使用的就是 https 協議,在最后一行輸出的 RemoteAddr 可以看到目標服務器的埠號就是443)、21對應著 ftp,25對應著 smtp,110對應著 pop3 等,

互聯網號碼分配局(英語:Internet Assigned Numbers Authority,縮寫 IANA),是一家互聯網地址指派機構,管理國際互聯網中使用的 IP 地址、域名和許多其它引數的機構, IP 地址、自治系統成員以及許多頂級和二級域名分配的日常職責由國際互聯網注冊中心(IR)和地區注冊中心承擔,IANA 是由 ICANN 管理的,

用戶埠(1024~ 49151):也叫做注冊埠(Registered Ports),從1024到49151,它們松散地系結于一些服務,也就是說有許多服務系結于這些埠,這些埠同樣用于許多其它目的,例如:許多系統處理動態埠從1024左右開始,
動態埠(49152~65535):也叫做私有或動態埠(Private or Ephemeral Ports),從49152到65535,理論上,不應為服務分配這些埠,實際上,機器通常從1024起分配動態埠,只要運行的程式向系統提出訪問網路的申請,那么系統就可以從這些埠號中分配一個供該程式使用,比如 49152 埠就是分配給第一個向系統發出申請的程式,在關閉程式行程后,就會釋放所占用的埠號,
所以當 client 準備發出網路請求的時候,client 所在的行程首先要向系統申請一個埠號作為源埠號,系統會隨機從49152~65535中分配一個可用的埠號給這行程,這樣當目標服務器處理完請求要給我們回傳資料的時候才能通過這個源埠號找到發出請求的這個埠所對應的服務并把 response 交給這個服務,可以說 HTTP 報文是面向服務的,它是服務與服務之間的交流,傳輸層是面向行程的,兩個埠號標識了兩個行程,一個行程里可能會有許多個服務(路由,或者說 API),

序列號

在一個 TCP 連接中傳送的位元組流中的每一個位元組都按順序編號,這個編號就類似于陣列的下標,陣列里每個元素都有自己的下標,例如,一報文段的序號是 101,共有 100 位元組的資料,這就表明:本報文段的資料的第一個位元組的序號是 101,最后一個位元組的序號是 200,顯然,下一個報文段的資料序號應當從 201 開始,即下一個報文段的序號欄位值應為 201,

確認號

期望收到對方下一個報文段的第一個資料位元組的序號,若確認號為 N,則表明:到序號 N-1 為止的所有資料都已正確收到,

標志位欄位

比較常見的標志位 SYNACKFIN 會在 TCP 連接建立與釋放的時候使用到,也就是我們常說的三次握手四次揮手,先講講三次握手建立連接:
image

三次握手的程序如圖所示,連接的建立一般都是由客戶端主動發起的,客戶端發送一個報文給服務器,告訴它我想要和你建立一個連接進行資料交換,進行資料交換之前有些事情需要先同步(synchronize,也就是 SYN 標志位,SYN=1 表示這是一個用于同步資訊的報文),雙方得約定好初始序列號(Init Sequense Number,ISN)、視窗大小等資訊,連接建立好了之后交換資料的時候才好判斷資料的起始與結束,通過 seq 欄位來告訴對方本報文的序列號,
第一次握手SYN = 1,客戶端告訴服務器自己的初始序列號是 x (seq = x),服務器收到了這個報文可以確定客戶端發送正常,自己接收正常,
第二次握手,服務器端發出報文 SYN = 1, ACK = 1(僅當 ACK=1 時,確認號欄位才有效,TCP規定,在連接建立后所有報文的傳輸都必須把ACK置1),表示這是一個應答報文,并且告訴了客戶端自己的初始序列號是 y (seq = y),以及確認自己收到了客戶端的前 x 個位元組的資訊,接下來希望收到 x + 1 的資訊(ack = x + 1),但是客戶端剛剛的報文明明沒有攜帶資訊,為什么說收到了前 x 個位元組的資訊呢,因為 TCP 規定,SYN報文段(SYN= 1 的報文段)不能攜帶資料,但需要消耗掉一個序號,
第二次握手成功之后客戶端確認了自己發送正常,接收正常,服務器發送正常,接收正常,服務器端確認了客戶端發送正常,自己接收正常,所以還需要第三次握手來讓服務器端確認自己發送正常以及客戶端接收正常,
第三次握手,雙方已經同步完序列號資訊了,所以第三次握手不用 SYN 標志位了,客戶端應答(ACK = 1)服務器第二次握手時發來的報文,表示自己收到了服務器端的前 y 個位元組的資訊(ACK = y + 1),告訴服務器端自己這個報文的起始序號是 x + 1(seq = x + 1),當服務器收到這個報文后服務器就可以確認自己發送正常以及客戶端接收正常了,連接就建立完成了可以進行資訊傳輸了,
如果第三次握手的報文因為各種各樣的原因丟了,服務器端沒有收到,那么服務器就會進行首次重傳,若等待一段時間仍未收到客戶確認包,就進行第二次重傳,如果重傳次數超過系統規定的最大重傳次數,則系統將該連接資訊從半連接佇列中洗掉,每次重傳等待的時間不一定相同,一般會是指數增長,
說完了三次握手建立連接再來說說四次揮手斷開連接:
image
四次揮手的程序如圖所示,
第一次揮手,客戶端發出一個 FIN 報文,消耗一個序列號,告訴服務器端自己沒有要發送的資料了,連接可以斷開了,
第二次揮手,服務器端服務器端發送一個 ACK 報文表示收到了客戶端要斷開連接的報文(ACK = 1, ack = u + 1),序號 u 之前的資料都以及收到了,此時連接進入半關閉狀態,但是服務器端可能還有資料沒有發完,所以可以繼續發送資料,直到服務器端發完了要發的資料,發送第三次揮手的報文,
第三次揮手,服務器端發送 FIN 報文,消耗一個序列號(seq = w),告訴客戶端自己的資料也發完了,因為前面是服務器端單向發資料給客戶端,所以 ack 還是為 u + 1,
第四次揮手,客戶端收到了服務器端要斷開連接的報文,回復一個 ACK 報文,讓服務器端知道自己收到了它的 FIN 報文,服務器端收到報文后立馬斷開了連接,

首部長度

也叫做資料偏移,它指出 TCP 報文段的資料起始處距離 TCP 報文段的起始處有多遠,也就是 TCP 報文段的首部長度,

視窗大小

報文能不能正常被傳輸、接收,不止取決于通信的雙方,還取決于外部環境,也就是網路環境,因為網路鏈路里不止當前的通信雙方在傳輸資料,而是由很多臺在發送資料的主機在共同使用,所以資料要正常被傳輸,有兩個問題要解決,一個是發送方與接收方速率匹配,接收方能及時處理發送方發送過來的資料,使得發送雙方速率匹配的策略我們稱為流量控制;另一個就是需要有一個良好的網路環境,努力使整體網路環境的通暢的策略我們稱為擁塞控制,流量控制與擁塞控制都是通過設定視窗大小來完成的,當然這兩個概念都是針對 TCP 協議來說的,它是一個無私的協議,UDP 可不管網路是否擁塞,它會一直發向網路中發送資料,
在介紹確認號這個首部欄位的時候我們提到了 TCP 采用的是累積確認的方式,下面我們來具體講解是怎么個累積確認法,引入累積確認主要是為了提高通信效率,如果沒有累積確認的話,接收方收到一個報文之后回復一個 ACK 報文,發送方接到這個 ACK 報文才能發送下一個報文,一包一確認的方式并不是很高明,往返時間越長,通信的效率就越低,而使用累積確認,發送方就可以連續發送一批報文,而不需要等待接收方回復了再發送下一個包,

image

雖然發送方可以一次性發一批報文,但是這個批大小肯定不能是無限大的,得有個規則來約束它,這個大小就是視窗大小,視窗大小以位元組為單位,比如當前視窗大小為1024個位元組,那么在不需要等待接收方確認的情況下發送發可以連續發送報文直到已發送的報文的長度加起來等于1024個位元組,
流量控制:流量控制的程序就是通過不斷的調整視窗大小來給讓接收方能來得及處理接收到的資料,所以視窗大小是由接收方決定的,在三次握手建立 TCP 連接的時候就已經同步好了視窗大小的資訊,并且如果在后續的資料傳輸中因為種種原因需要調整視窗大小也是允許的
擁塞控制:引入擁塞控制這個概念后視窗大小就受到流量控制和擁塞控制這兩個策略共同影響了,通過擁塞控制演算法算出擁塞視窗cwnd 的大小,實際視窗大小 = min(cwndrwndrwnd 就是流量控制中的接收視窗,
擁塞視窗cwnd變化的規則:

  • 只要網路中沒有出現擁塞,cwnd就會增大;
  • 一但網路中出現了擁塞,cwnd就減少;

只要「發送方」沒有在規定時間內接收到 ACK 應答報文,也就是發生了超時重傳,就會認為網路出現了用擁塞,
擁塞控制主要是四個演算法:

  • 慢啟動
  • 擁塞避免
  • 擁塞發生
  • 快速恢復

關于視窗大小的更多細節可以看看這篇博客

校驗和

資料段

segment data,這里放著 HTTP 請求報文,像這樣:
image
(圖片來自這篇博客)

RemoteAddr

這個遠端地址包括了目標服務器的 IP 和埠號, 埠號在上文我們已經說過了,它會被寫在 TCP 報文里,而 IP 則會被寫在網路層的 IP 資料報里,應用層通過 DNS 服務獲得了 IP,然后通過函式呼叫傳參的方式來把這個 IP 傳給實作了傳輸層協議的 server,當然傳輸層的的報文里并不需要用到 IP,但是域名是屬于應用層的東西,如果沒有在應用層完成決議獲得 IP,后面的層因為沒有域名資訊就無法獲得 IP 無法正常作業了,所以 IP 從應用層通過引數傳遞的方式傳給傳輸層,傳輸層再通過引數傳遞的方式傳給真正需要用到它的網路層,讓網路層寫進它自己的報文里,Golang 的 net 包的 net.LookupHost() 函式可以完成域名決議獲得 IP,對 DNS 決議的程序有興趣的朋友可以看看這篇博客,
我們先來看看 IP 資料報的結構:
image

就像 TCP 報文的資料段應用層報文一樣,IP 資料報的資料部分指的就是傳輸層報文,在本文里就是 TCP 報文,如下圖所示:
image

IsConnReused

連接是否復用,說到這個話題又回到了應用層的 HTTP 協議,前面我們說到 HTTP 報文有各種各樣的 HEADER 欄位,其中有一個叫做Connection 的通用 header,它的的取值為Keep-Aliveclose ,當在 header 里加入Connection: Keep-Alive 意味著開啟長連接,
我們把上面例子里的代碼進行一些小小修改,變成這樣:

client := resty.New()
headers := map[string]string{
	"Connection": "Keep-Alive",
}
resp1, _ := client.R().
	EnableTrace().
	SetHeaders(headers).
	Get("https://httpbin.org/get")

// Explore trace info
fmt.Println("Request Trace Info:")
ti := resp1.Request.TraceInfo()
fmt.Println("  DNSLookup     :", ti.DNSLookup)
fmt.Println("  TCPConnTime   :", ti.TCPConnTime)
fmt.Println("  TLSHandshake  :", ti.TLSHandshake)
fmt.Println("  IsConnReused  :", ti.IsConnReused)
fmt.Println("  RemoteAddr    :", ti.RemoteAddr.String())’

resp2, _ := client.R().
	EnableTrace().
	SetHeaders(headers).
	Get("https://httpbin.org/get")

fmt.Println()
fmt.Println("******** Request with Keep-Alive *******")
fmt.Println()
cname, _ := net.LookupCNAME("httpbin.org")
host, _ := net.LookupHost("httpbin.org")
addr, _ := net.LookupAddr("httpbin.org")
ip, _ := net.LookupIP("httpbin.org")
fmt.Println(cname)
fmt.Println(host)
fmt.Println(addr)
fmt.Println(ip)
// Explore trace info
fmt.Println("Request Trace Info:")
ti2 := resp2.Request.TraceInfo()
fmt.Println("  DNSLookup     :", ti2.DNSLookup)
fmt.Println("  TCPConnTime   :", ti2.TCPConnTime)
fmt.Println("  TLSHandshake  :", ti2.TLSHandshake)
fmt.Println("  IsConnReused  :", ti2.IsConnReused)
fmt.Println("  RemoteAddr    :", ti2.RemoteAddr.String())

這次我們主要想看看 Keep-Alive 的實際效果,所以一些沒啥用的資訊就不輸出了,這段代碼的運行效果如下:
image

可以看到在第二次請求的時候 DNSLookup、 TCPConnTime、TLSHandshake 這三個與連接建立有關的時間花費都變成了0啦!IsConnReused 也由 false 變成了 true,說明這次的連接是復用的第一次請求的時候建立的連接,所以不需要再重新建立連接,那些與連接建立的時間花費自然也變成零了(從圖上可以看出來節省了大約2.5s 的時間),
關于長連接有興趣的朋友可以看看這篇博客

總結

實踐很重要,上學的時候學計網總覺得是一知半解的,書上說分層協作各層解耦,層與層之間是透明的,也就是互相看不見,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/541100.html

標籤:其他

上一篇:C++:拷貝建構式

下一篇:轉載自ChatGPT:Python關鍵字 asynico

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more