TCP相關實驗

目錄

TCP相關實驗

理解CLOSE_WAIT狀態

理解???TIME_WAIT狀態

解決TIME_WAIT狀態引起的bind失敗的方法

理解listen的第二個參數

?編輯

使用Wireshark分析TCP通信流程

TCP與UDP

TCP與UDP對比

用UDP實現可靠傳輸(經典面試題)


TCP相關實驗

理解CLOSE_WAIT狀態

當客戶端和服務器在進行TCP通信時,如果客戶端調用close函數關閉對應的文件描述符,此時客戶端底層操作系統就會向服務器發起FIN請求,服務器收到該請求后會對其進行ACK響應。

但如果當服務器收到客戶端的FIN請求后,服務器端不調用close函數關閉對應的文件描述符,那么服務器就不會給客戶端發送FIN請求,相當于只完成了四次揮手當中的前兩次揮手(只是客戶端一方的意愿),此時客戶端和服務器的連接狀態分別會變為FIN_WAIT_2和CLOSE_WAIT。

我們可以編寫一個簡單的TCP套接字來模擬出該現象,實際我們只需要編寫服務器端的代碼,而采用一些網絡工具來充當客戶端向我們的服務器發起連接請求。

服務器的初始化需要進行套接字的創建、綁定以及監聽,然后主線程就可以通過調用accept函數從底層獲取建立好的連接了。獲取到連接后主線程創建新線程為該連接提供服務,而新線程只需執行一個死循環邏輯即可。

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>using namespace std;const uint16_t Serverport = 8080;
const int backlog = 5;void* Routine(void* args)
{pthread_detach(pthread_self());int fd = *(int*)args;delete (int*)args;while(true){std::cout << "socket " << fd << " is serving the client" << std::endl;sleep(1);}return nullptr;
}int main()
{//創建套接字int listensockfd = socket(AF_INET, SOCK_STREAM, 0);if(listensockfd < 0){perror("create sockfd fail!!!");exit(-1);}struct sockaddr_in Server;// bzero(&Server, sizeof(Server));memset(&Server, 0, sizeof(Server));Server.sin_family = AF_INET;Server.sin_addr.s_addr = INADDR_ANY;Server.sin_port = htons(Serverport);socklen_t len = sizeof(Server);//bindint n = bind(listensockfd, (const sockaddr*)&Server, len);if(n < 0){perror("bind fail!!!");exit(-1);}//listenif(listen(listensockfd, backlog) < 0){perror("listen fail!!!");exit(-1);}cout<<"success"<<endl;struct sockaddr_in Client;memset(&Client, 0, sizeof(Client));len = sizeof(Client);for(;;){//acceptint sockfd = accept(listensockfd, (struct sockaddr*)&Client, &len);cout << sockfd << endl;if(sockfd < 0) {cout << "try request connect" << endl;continue;}cout<< "get a new link" << endl;pthread_t tid;int* p = new int(sockfd);pthread_create(&tid, nullptr, Routine, (void*)p); }return 0;
}

代碼編寫完畢后運行服務器,并用telnet工具連接我們的服務器,此時通過以下監控腳本就可以看到兩條狀態為ESTABLISHED的連接。

while :; do sudo netstat -ntp|head -2&&sudo netstat -ntp | grep 8081; sleep 1; echo "##################"; done

現在我們讓telnet退出,就相當于客戶端向服務器發起了連接斷開請求,但此時服務器端并沒有調用close函數關閉對應的文件描述符,所以當telnet退出后,客戶端維護的連接的狀態會變為FIN_WAIT_2,而服務器維護的連接的狀態會變為CLOSE_WAIT。

理解???TIME_WAIT狀態

當客戶端和服務器在進行TCP通信時,客戶端調用close函數關閉對應的文件描述符,如果服務器收到后也調用close函數進行了關閉,那么此時雙方將正常完成四次揮手。但主動發起四次揮手的一方在四次揮手后,不會立即進入CLOSED狀態,而是進入短暫的TIME_WAIT狀態等待若干時間,最終才會進入CLOSED狀態。

要讓客戶端和服務器繼續完成后兩次揮手,就需要服務器端調用close函數關閉對應的文件描述符。雖然服務器代碼當中沒有調用close函數,但因為文件描述符的生命周期是隨進程的,當進程退出的時候,該進程所對應的文件描述符都會自動關閉。

因此只需要在telnet退出后讓服務器進程退出就行了,此時服務器進程所對應的文件描述符會自動關閉,此時服務器底層TCP就會向客戶端發送FIN請求,完成剩下的兩次揮手。

四次揮手后客戶端維護的連接就會進入到TIME_WAIT狀態,而服務器維護的連接則會立馬進入到CLOSED狀態。

主動斷開連接的一方,在最后四次揮手完完成之后,要進入TIME_WAIT狀態,等待若干時長之后,自動釋放

解決TIME_WAIT狀態引起的bind失敗的方法

主動發起四次揮手的一方在四次揮手后,會進入TIME_WAIT狀態。如果在有客戶端連接服務器的情況下服務器進程退出了,就相當于服務器主動發起了四次揮手,此時服務器維護的連接在四次揮手后就會進入TIME_WAIT狀態。

在該連接處于TIME_WAIT期間,如果服務器想要再次重新啟動,就會出現綁定失敗的問題。

因為在TIME_WAIT期間,這個連接并沒有被完全釋放,也就意味著服務器綁定的port和ip正在被使用,此時服務器想要繼續綁定該端口號啟動,就只能等待TIME_WAIT結束。

但當服務器崩潰后最重要實際是讓服務器立馬重新啟動,如果想要讓服務器崩潰后在TIME_WAIT期間也能立馬重新啟動,需要讓服務器在調用socket函數創建套接字后,繼續調用setsockopt函數設置端口復用,這也是編寫服務器代碼時的推薦做法。

setsockopt函數

setsockopt函數可以設置端口復用,該函數的函數原型如下:

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

參數說明:

  • sockfd:需要設置的套接字對應的文件描述符。
  • level:被設置選項的層次。比如在套接字層設置選項對應就是SOL_SOCKET。
  • optname:需要設置的選項。該選項的可取值與設置的level參數有關。
  • optval:指向存放選項待設置的新值的指針。
  • optlen:待設置的新值的長度。

返回值說明:

  • 設置成功返回0,設置失敗返回-1,同時錯誤碼會被設置。

我們這里要設置的就是監聽套接字,將監聽套接字在套接字層設置端口復用選項SO_REUSEADDR,該選項設置為非零值表示開啟端口復用。

int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

此時當服務器崩潰后我們就可以立馬重新啟動服務器,而不用等待TIME_WAIT結束。

連接是由TCP管理的

從上面的實驗中可以看到,即便通信雙方對應的進程都退出了,但服務器端依然存在一個處于TIME_WAIT狀態的連接,這也更加說明了進程管理和連接管理是兩個相對獨立的單元。連接是由TCP自行管理的,連接不一定會隨進程的退出而關閉。

理解listen的第二個參數

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>using namespace std;const uint16_t Serverport = 8081;
const int backlog = 1;int main()
{//創建套接字int listensockfd = socket(AF_INET, SOCK_STREAM, 0);if(listensockfd < 0){perror("create sockfd fail!!!");exit(-1);}int opt = 1;setsockopt(listensockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));struct sockaddr_in Server;// bzero(&Server, sizeof(Server));memset(&Server, 0, sizeof(Server));Server.sin_family = AF_INET;Server.sin_addr.s_addr = INADDR_ANY;Server.sin_port = htons(Serverport);socklen_t len = sizeof(Server);//bindint n = bind(listensockfd, (const sockaddr*)&Server, len);if(n < 0){perror("bind fail!!!");exit(-1);}//listenif(listen(listensockfd, backlog) < 0){perror("listen fail!!!");exit(-1);}cout<<"success"<<endl;struct sockaddr_in Client;memset(&Client, 0, sizeof(Client));len = sizeof(Client);for(;;){}return 0;
}

運行服務器后使用netstat -nltp命令,可以看到該服務器當前正處于監聽狀態。

我們分別使用三個客戶機對服務器發出三次SYN請求,前二次建立連接成功,第三次請求連接時,客戶端沒有繼續新增狀態為ESTABLISHED的連接,而是新增了一個狀態為SYN_SENT的連接。

?

而對于剛才狀態為SYN_SENT的連接,由于服務器長時間不對其進行應答,三次握手失敗后該連接會被自動釋放。

總結一下上面的實驗現象:

  • 無論有多少客戶端向服務器發起連接請求,最終在服務器端最多只有2個連接會建立成功。
  • 當發來第3個連接請求時,服務器只是收到了該客戶端發來的SYN請求,但并沒有對其進行響應。
  • 當發來更多的連接請求時,服務器會直接拒絕這些連接請求。

listen的第二個參數

實際TCP在進行連接管理時會用到兩個連接隊列:

  • 全連接隊列(accept隊列)。全連接隊列用于保存處于ESTABLISHED狀態,但沒有被上層調用accept取走的連接。
  • 半連接隊列。半連接隊列用于保存處于SYN_SENT和SYN_RCVD狀態的連接,也就是還未完成三次握手的連接。

而全連接隊列的長度實際會受到listen第二個參數的影響,一般TCP全連接隊列的長度就等于listen第二個參數的值加一。

因為我們實驗時設置listen第二個參數的值為2,此時在服務器端全連接隊列的長度就為3,因此服務器最多只允許有三個處于ESTABLISHED狀態的連接。

如果將剛才代碼中listen的第二個參數值設置為3,此時服務器端最多就允許存在4個處于ESTABLISHED狀態的連接。在服務器端已經有4個ESTABLISHED狀態的連接的情況下,再有客戶端發來建立連接請求,此時客戶端就會新增狀態為SYN_SENT的連接,該連接實際就是放在半連接隊列當中的。

為什么底層要維護連接隊列?

如果沒有連接隊列,當上層將連接處理完之后就需要重新等待客戶端建立新的連接,這樣效率太低了

為什么連接隊列不能太長?

全連接隊列不能太長,系統一般設置為5

雖然維護連接隊列能讓服務器處于幾乎滿載工作的狀態,但連接隊列也不能設置得太長。

  • 如果隊列太長,也就意味著在隊列較尾部的連接需要等待較長時間才能得到服務,此時客戶端的請求也就遲遲得不到響應。
  • 此外,服務器維護連接也是需要成本的,連接隊列設置的越長,系統就要花費越多的成本去維護這個隊列。
  • 但與其與其維護一個長連接,造成客戶端等待過久,并且占用大量暫時用不到的資源,還不如將部分資源節省出來給服務器使用,讓服務器更快的為客戶端提供服務。

因此雖然需要維護連接隊列,但連接隊列不能維護的太長。

全連接隊列的長度

全連接隊列的長度由兩個值決定:

  • 用戶層調用listen時傳入的第二個參數backlog。
  • 系統變量net.core.somaxconn,默認值為128。

通過以下命令可以查看系統變量net.core.somaxconn的值。

sudo sysctl -a | grep net.core.somaxconn

全連接隊列的長度實際等于listen傳入的backlog和系統變量net.core.somaxconn中的較小值加一。

SYN洪水攻擊

連接正常建立的過程:

  • 當客戶端向服務器發起連接建立請求后,服務器會對其進行SYN+ACK響應,并將該連接放到半連接隊列(syns queue)當中。
  • 當服務器發出的SYN+ACK得到客戶端響應后,就會將該連接由半連接隊列移到全連接隊列(accept queue)當中。
  • 此時上層就可以通過調用accept函數,從全連接隊列當中獲取建立好的連接了。

連接建立異常:

  • 但如果客戶端在發起連接建立請求后突然死機或掉線,那么服務器發出的SYN+ACK就得不到對應的ACK應答。
  • 這種情況下服務器會進行重試(再次發送SYN+ACK給客戶端)并等待一段時間,服務器并不會長時間維護,最終服務器會因為收不到ACK應答而將這個連接丟棄,這段時間長度就稱為SYN timeout。
  • 在SYN timeout時間內,這個連接會一直維護在半連接隊列當中。

此時服務器雖然需要短暫維護這些異常連接,但這種情況畢竟是少數,不會對服務器造成太大影響。

但如果有一個惡意用戶故意大量模擬這種情況:向服務器發送大量的連接建立請求,但在收到服務器發來的SYN+ACK后故意不對其進行ACK應答。

  • 此時服務器就需要維護一個非常大的半連接隊列,并且這些連接最終都不會建立成功,也就不會被移到全連接隊列當中供上層獲取,最后會導致半連接隊列越來越長。
  • 當半連接隊列被占滿后,新來的連接就會直接被拒絕,哪怕是正常的連接建立請求,此時就會導致正常用戶無法訪問服務器。
  • 這種向服務器發送大量SYN請求,但并不對服務器的SYN+ACK進行ACK響應,最終可能導致服務器無法對外提供服務,這種攻擊方式就叫做SYN洪水攻擊(SYN Flood)。

如何解決SYN洪水攻擊?

首先這一定是一個綜合性的解決方案,TCP作為傳輸控制協議需要對其進行處理,而上層應用層也要盡量避免遭到SYN洪水攻擊。

  • 比如應用層可以記錄,向服務器發起連接建立請求的主機信息,如果發現某個主機多次向服務器發起SYN請求,但從不對服務器的SYN+ACK進行ACK響應,此時就可以對該主機進行黑名單認證,此后該主機發來的SYN請求一概不進行處理。

TCP為了防范SYN洪水攻擊,引入了syncookie機制:

  • 現在核心的問題就是半連接隊列被占滿了,但不能簡單的擴大半連接隊列,就算半連接隊列再大,惡意用戶也能發送更多的SYN請求來占滿,并且維護半連接隊列當中的連接也是需要成本的。
  • 因此TCP引入了syncookie機制,當服務器收到一個連接建立請求后,會根據這個SYN包計算出一個cookie值,將其作為將要返回的SYN+ACK包的初始序號,然后將這個連接放到一個暫存隊列當中。
  • 當服務器收到客戶端的ACK響應時,會提取出當中的cookie值進行對比,對比成功則說明是一個正常連接,此時該連接就會從暫存隊列當中移到全連接隊列供上層讀取。

白話解釋:

想象服務器是個餐廳,半連接隊列是 “等叫號” 的區域。惡意用戶瘋狂發 SYN 請求,就像一堆 “假顧客” 來拿號,把等叫號的地方全占滿了。正常顧客(真實連接請求)反而沒地方,而且服務器維護這些 “假顧客”(半連接)還得花精力(成本),光擴大等號區也沒用,惡意用戶能一直塞假號

服務器收到連接請求(SYN)時,不再直接把請求放進 “等叫號區(半連接隊列)”,而是算個 “驗證碼(cookie 值)”,把它當 SYN+ACK 包的初始序號,然后把這請求臨時擱到一個 “暫存區”。

等客戶端回復 ACK 時,服務器會檢查這個 “驗證碼” 對不對:

  • 要是正常客戶端(真實用戶),會乖乖帶著正確驗證碼回復,服務器驗證通過,就把這連接從 “暫存區” 挪到 “已連接隊列”,正常提供服務(就像真顧客拿號、叫號、入座)。
  • 要是惡意請求(假顧客),不會回復 ACK 或者回復的驗證碼不對,這些請求就一直堆在 “暫存區”,不會占滿關鍵的半連接隊列,服務器還能正常接待真顧客。

注意:syncookie機制會跳過半連接隊列,將連接放到臨時隊列+驗證cookie的方式解決SYN洪水

引入了syncookie機制的好處:

  • 引入syncookie機制后,這些異常連接就不會堆積在半連接隊列隊列當中了,也就不會出現半連接隊列被占滿的情況了。
  • 對于正常的連接,一般會立即對服務器的SYN+ACK進行ACK應答,因此正常連接會很快建立成功。
  • 而異常的連接,不會對服務器的SYN+ACK進行ACK應答,因此異常的連接最終都會堆積到暫存隊列當中。

使用Wireshark分析TCP通信流程

在使用Wireshark時可以通過設置過濾器,來抓取滿足要求的數據包。
針對IP進行過濾:

  • 抓取指定源地址的包:ip.src == 源IP地址
  • 抓取指定目的地址的包:ip.dst == 目的IP地址
  • 抓取源或目的地址滿足要求的包:ip.addr == IP地址等價于ip.src == 源IP地址 or ip.dst == 目的IP地址
  • 抓取除指定IP地址之外的包:!(表達式)

針對協議進行過濾:

  • 抓取指定協議的包:協議名(只能小寫)
  • 抓取多種指定協議的包:協議名1 or 協議名2
  • 抓取除指定協議之外的包:not 協議名 或 !協議名

針對端口進行過濾(以TCP協議為例):

  • 抓取指定端口的包:tcp.port == 端口號
  • 抓取多個指定端口的包:tcp.port >= 2048(抓取端口號高于2048的包)
  • 針對長度和內容進行過濾:抓取指定長度的包:udp.length < 30 http.content_length <= 20
  • 抓取指定內容的包:http.request.urimatches "指定內容"

針對長度和內容進行過濾:

  • 抓取指定長度的包:udp.length < 10 http.content_length <= 20
  • 抓取指定內容的包:http.request.urimaches?”指定內容

抓包示例

這里我們抓取指定源IP地址或目的IP地址的數據包。

當我們用telnet命令連接該服務器后,就可以抓取到三次握手時雙方交互的數據包。

而當我們退出telnet命令后,就可以抓取到四次揮手時雙方交互的數據包。(此處四次揮手時進行了捎帶應答,第二次揮手和第三次揮手合并在了一起)

TCP與UDP

TCP與UDP對比

TCP協議

TCP協議叫做傳輸控制協議(Transmission Control Protocol),TCP協議是一種面向連接的、可靠的、基于字節流的傳輸層通信協議。

TCP協議是面向連接的,如果兩臺主機之間想要進行數據傳輸,那么必須要先建立連接,當連接建立成功后才能進行數據傳輸。其次,TCP協議是保證可靠的協議,數據在傳輸過程中如果出現了丟包、亂序等情況,TCP協議都有對應的解決方法。

UDP協議

UDP協議叫做用戶數據報協議(User Datagram Protocol),UDP協議是一種無需建立連接的、不可靠的、面向數據報的傳輸層通信協議。

使用UDP協議進行通信時無需建立連接,如果兩臺主機之間想要進行數據傳輸,那么直接將數據發送給對端主機就行了,但這也就意味著UDP協議是不可靠的,數據在傳輸過程中如果出現了丟包、亂序等情況,UDP協議本身是不知道的。

TCP/UDP對比

TCP協議雖然是保證可靠性的協議,但不能說TCP就一定比UDP好,因為TCP保證可靠性也就意味著TCP需要做更多的工作,而UDP不保證可靠性也就意味著UDP足夠簡單。

  • TCP常用于可靠傳輸的情況,應用于文件傳輸,重要狀態更新等場景。
  • UDP常用于對高速傳輸和實時性較高的通信領域,例如早期的QQ、視頻傳輸等。另外UDP可以用于廣播。

也就是說,UDP和TCP沒有誰最好,只有誰最合適,網絡通信時具體采用TCP還是UDP完全取決于上層的應用場景。

用UDP實現可靠傳輸(經典面試題)

當面試官讓你用UDP實現可靠傳輸時,你一定要立馬想到TCP協議,因為TCP協議就是當前比較完善的保證可靠性的協議,面試官讓你用UDP這個不可靠的協議來實現可靠傳輸,無非就是讓你在應用層來實現可靠性,此時就可以參考TCP協議保證可靠性的各種機制。

例如:

  • 引入序列號,保證數據按序到達。
  • 引入確認應答,確保對端接收到了數據。
  • 引入超時重傳,如果隔一段時間沒有應答,就進行數據重發。

但TCP保證可靠性的機制太多了,當你被面試官問到這個問題時,最好與面試官進一步進行溝通,比如問問這個用UDP實現可靠傳輸的應用場景是什么。因為TCP保證可靠性的機制太多了,但在某些場景下可能只需要引入TCP的部分機制就行了,因此在不同的應用場景下模擬實現UDP可靠傳輸的側重點也是不同的。

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

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

相關文章

Spring Boot項目初始化:官方與阿里云服務地址對比指南

服務提供商 官方&#xff08;start.spring.io Spring&#xff09; 官方提供的服務&#xff0c;由Pivotal&#xff08;VMware&#xff09;維護&#xff0c;是標準的初始化工具。 阿里云&#xff08;start.aliyun.com&#xff09; 阿里云提供的國內鏡像服務&#xff0c;針對中國開…

創客匠人創始人IP案例:從個人品牌到企業增長的全鏈路拆解

認知破局&#xff1a;為什么創客匠人創始人IP能撬動企業增長&#xff1f;在知識付費工具競爭同質化的當下&#xff0c;創客匠人創始人老蔣以“IP變現領軍人”的IP形象&#xff0c;為企業打開了差異化增長通道。當同行還在比拼“功能數量”時&#xff0c;老蔣通過《領導者請停止…

UVC(USB Video Class,USB 視頻類)協議

UVC&#xff08;USB Video Class&#xff0c;USB 視頻類&#xff09;協議并非專門僅用于相機&#xff0c;但其核心應用場景集中在視頻采集設備&#xff0c;相機是最典型的代表。其適用設備除了常見的 USB 相機&#xff08;包括 webcam、工業相機、監控攝像頭等&#xff09;&…

如何使用 eBPF 監控 Linux 內存情況:Linux 內存調優之 eBPF 內存監控分析

寫在前面 博文內容整理自 《BPF Performance Tools》 書中 內存部分對書中提到BPF工具配合實際Demo進行說明,以及一些變體的輸出涉及下面一些內存問題的 BPF 觀測 Demo:為什么進程的物理內存占用(RSS)不停增長?哪些代碼路徑會導致缺頁錯誤的發生,缺頁錯誤來自哪些文件?大頁的…

SQL 表結構轉 Go、Java、TS 自定義實體類,支持自編模板

SQL 表結構一鍵轉自定義模型&#xff0c;支持 Golang Template 自由編寫&#xff01; 有沒有想過 —— 一份 SQL 表結構&#xff0c;不止能轉成 Java 實體類、Go struct&#xff0c;甚至可以&#xff1a; ? 一鍵生成 TypeScript 接口? 輸出 Protobuf 定義文件? 輸出任意你…

新型BERT勒索軟件肆虐:多線程攻擊同時針對Windows、Linux及ESXi系統

趨勢科技安全分析師發現&#xff0c;一個代號為BERT&#xff08;內部追蹤名Water Pombero&#xff09;的新型勒索軟件組織正在亞洲、歐洲和美國展開多線程攻擊。該組織主要針對醫療保健、科技和會展服務行業&#xff0c;其活動范圍顯示其正成為勒索軟件生態中的新興威脅力量。攻…

Three.js搭建小米SU7三維汽車實戰(1)搭建開發環境

1.基本概念 ![](https://i-blog.csdnimg.cn/img_convert/a4676122e207e058f3a335df2c99d4f8.png)1) 場景 如何理解場景 場景就是一個三維的世界, 在這個世界中可以放置各種各樣的物體 可以理解成一個**空間**, 或者**容器** 2) 相機 如何理解相機 &#x1f914;**思考: *…

Selenium 原理【selenium】

Selenium 是什么&#xff1f;Selenium 是一個專門用于自動化操作網頁的工具集&#xff0c;它能夠模擬人類在瀏覽器中進行的各種操作&#xff0c;如點擊按鈕、填寫表單、滾動頁面等。借助 Selenium&#xff0c;開發者可以編寫腳本來控制瀏覽器&#xff0c;實現自動化測試、數據采…

【音視頻】HLS-m3u8協議介紹

參考文檔&#xff1a;https://datatracker.ietf.org/doc/html/rfc8216 一、m3u8協議概述 m3u8 協議是基于 M3U 格式擴展而來的一種多媒體播放列表協議&#xff0c;主要用于流媒體的索引和分發&#xff0c;尤其在 HLS&#xff08;HTTP Live Streaming&#xff09;技術中扮演核…

unity入門:動畫等不顯示問題——畫布設置

unity入門&#xff1a;動畫等不顯示問題——畫布設置動畫等不顯示問題大部分原因畫布Canvas總結動畫等不顯示問題大部分原因 1、畫布設置渲染模式不對&#xff0c;下文再講這個問題。 2、在層級雙擊動畫查看動畫大小&#xff0c;有些動畫創建完之后在場景大小實際很小需要在R…

【機器學習筆記 Ⅱ】3 前向傳播

前向傳播&#xff08;Forward Propagation&#xff09;實現詳解 前向傳播是神經網絡中數據從輸入層流向輸出層的過程&#xff0c;通過逐層計算每一層的輸出&#xff0c;最終得到預測結果。以下是其實現原理和步驟的完整解析&#xff1a;1. 前向傳播的核心步驟 (1) 線性變換&…

人體坐姿檢測系統開發實戰(YOLOv8+PyTorch+可視化)

本文將手把手教你構建智能坐姿檢測系統,結合目標檢測與姿態估計技術,實現不良坐姿的實時識別與預警 ### 一、項目背景與價值 現代人每天平均坐姿時間超過8小時,不良坐姿會導致: - 脊椎壓力增加300% - 頸椎病發病率提升45% - 腰椎間盤突出風險增加60% 本系統通過計算機…

卷積神經網絡經典架構演進

LeNet-5 網絡架構 #mermaid-svg-8VgsGVLusLXKY5lE {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-8VgsGVLusLXKY5lE .error-icon{fill:#552222;}#mermaid-svg-8VgsGVLusLXKY5lE .error-text{fill:#552222;stroke:#5…

mybatis/mybatis-plus添加數據,自增id的值為負數

1、問題概述&#xff1f;使用mybatis-plus的insert方法添加數據的時候&#xff0c;數據雖然添加成功了&#xff0c;但是返回值為false&#xff0c;提示添加失敗。當觀察數據的時候&#xff0c;發現數據的自增主鍵id的值盡然為-1&#xff0c;或者無規律的長串負數&#xff0c;如…

商業創業融資項目計劃書PPT模版

創業融資計劃書PPT模版&#xff0c;營銷模式分析PPT模版&#xff0c;創業計劃書PPT模版&#xff0c;互聯網電商創業推廣手冊PPT模版&#xff0c;商業項目計劃書PPT模版&#xff0c;高端商業計劃通用PPT模版&#xff0c;商業計劃書&#xff0c;科技商業PPT模版 商業創業融資項目…

新人如何入門學習 STM32?

作為一個在嵌入式領域摸爬滾打了近10年的老兵&#xff0c;看到這個問題時我的思緒瞬間回到了當年那個懵懂的自己。說實話&#xff0c;2014年那個夏天&#xff0c;24歲的我剛從機械專業畢業卻被調劑到了廈門某馬的電子部門&#xff0c;第一次聽到"STM32"這個詞的時候&…

clickhouse數據庫表和doris數據庫表遷移starrocks數據庫時建表注意事項總結

目錄零、前言一、clickhouse數據庫表在starrocks數據庫建表時問題總結1.1 數據類型類問題&#xff1a;1.2 數據導出階段&#xff1a;二、doris 數據庫表在starrocks數據庫建表時問題總結2.1 properties不支持的屬性&#xff08;直接刪除&#xff09;&#xff1a;2.2 properties…

社區云管家 - 智慧生活新方式 ——仙盟創夢IDE

社區服務熱門推薦數字化時代的社區服務新形態?在數字化浪潮席卷日常生活的今天&#xff0c;一個集多功能于一體的綜合社區官網正成為連接居民與社區服務的核心紐帶。這類平臺以 “一站式解決生活需求” 為核心&#xff0c;將看房、外賣、物業、快遞、求職、生鮮、出行、文具打…

MongoDB GridFS

MongoDB GridFS 引言 MongoDB 是一種高性能、可擴展的文檔存儲系統,它提供了靈活的數據模型和豐富的查詢功能。在處理大量非結構化數據時,MongoDB 的 GridFS 功能尤為突出。GridFS 是一種用于存儲和檢索大文件的解決方案,它可以存儲任意大小的文件,并將其分解為多個較小的…

Linux中程序的limits中的Max open files的配置由哪些參數決定

在 Linux 中&#xff0c;程序的 Max open files&#xff08;最大打開文件數&#xff0c;即 ulimit -n&#xff09;由多個層級的參數共同控制&#xff0c;具體如下&#xff1a; 1. 內核級全局限制&#xff08;系統默認上限&#xff09; 由 /proc/sys/fs/file-max 控制&#xff0…