Linux網絡編程——TCP通信的四次揮手

一、前言

上篇文章講到了TCP通信建立連接的“三次握手”的一些細節,本文再對TCP通信斷開連接的“四次揮手”的過程做一些分析了解。

二、TCP斷開連接的“四次揮手”

我們知道TCP在建立連接的時需要“三次握手”,三次握手完后就可以進行通信了。而在通信結束的時候,TCP協議還需要進行“四次揮手”才能斷開連接。如圖

?從圖中看,?TCP的"四次揮手", 是由?兩端分別發送FIN報文, 對端再應答ACK報文?形成的

"四次揮手"的主動發起者, 可以是客戶端, 也可以是服務端,所以, "四次揮手"用主動端和被動端來區分狀態

整個過程是這樣的:

  1. 先發送FIN的一端(主動端A) 在接收到對端(被動端B)的ACK之后, 會進入FIN_WAIT_2狀態, 而不是直接CLOSED
  2. 并且, B端接收到FIN之后, 也沒有直接應答FIN關閉連接. 而是, 進入了CLOSE_WAIT狀態
  3. 然后, B端才發送了FIN報文, 并進入LAST_ACK狀態, 直到收到A端的ACK應答報文
  4. A端收到B端發送的FIN報文, 并發送ACK應答報文之后, 并沒有直接進入CLOSED狀態, 而是先進入了TIME_WAIT狀態

哪一方發送了FIN報文, 就表示這一方想要斷開連接了, 此端應用層不會再向對端發送數據了,這里所謂的不發數據指的是不發用戶數據了,并不代表底層沒有管理報文的交互

問題1、?A端發送FIN報文之后, 為什么B端沒有直接應答FIN, 而是進入了CLOSE_WAIT狀態?

答案是, 為了維護數據傳輸的可靠性

A端向B端發送FIN報文, 表示A端不準備通信了, 實際也就表示A端應用層不會再向B端發送數據了

但是, A端沒有數據發送了, B端卻不一定. 畢竟, TCP協議是存在發送緩沖區的, 也就是說 B端可能還有數據沒有向A端發送呢, 如果直接和A端一起斷開連接, 那么還沒有發送的數據怎么辦?

所以, 雖然A端發送了FIN報文, 想要斷開連接, 但是B端可能并不想現在就斷開連接, 所以就可能不會直接應答FIN

即, 當被動端不想直接斷開連接時, 就只應答ACK報文, 并進入CLOSE_WAIT狀態

直到, 被動端也沒有要發送的數據了, 被動端才會發送FIN報文, 并進入LAST_ACK狀態

如果, 主動端發送FIN報文時, 被動端也想要斷開連接了

那么, B端就可能會應答ACK+FIN的報文

不過, 這并不是一般情況, 我們還是要討論一般情況滴

主動端接收到被動端的FIN報文之后, 向被動端應答最后一個ACK報文, 并進入TIME_WAIT狀態 持續一段時間后, 正式關閉連接

被動端在收到主動端的ACK應答報文之后, 正式關閉連接

“四次揮手”狀態分析

下面就來分析一下這四次揮手時候,每一次的揮手雙方都進入哪一種狀態,這些狀態的意義是什么?

主動端分析


主動段發送FIN報文之后,會進入FIN_WAIT_1狀態,該狀態的作用是

  1. 表示我已經主動發送FIN報文, 告訴對端 自己想要斷開連接
  2. 防止因網絡延遲或其他原因, 我沒有收到對端的ACK應答報文. 出現此情況, 還需要超時重傳FIN報文
  3. 進入FIN_WAIT_1狀態開始, 我就關閉了TCP發送緩沖區, 應用層不會再向對端發送數據, 同時讓對端也了解到這一點
  4. 此狀態持續時間, 取決于什么時候收到對端的ACK應答報文

主動端收到ACK應答報文之后,會進入?FIN_WAIT_2狀態,該狀態的作用是

  1. 表示我已經收到了對端的ACK應答報文
  2. 并了解到 對端還不想關閉連接(因為對端還沒有發送FIN), 所以 我需要保持TCP接收緩沖區不關閉, 保持此端接收數據的功能
  3. 此狀態持續時間, 取決于對端什么時候想要關閉連接, 即 什么時候收到對端發送的FIN報文

主動端接收到對端的FIN報文, 并作出應答之后, 會進入 TIME_WAIT 狀態?,該狀態的作用是

  1. 表示我已經收到了對端發送的FIN報文, 了解到對端數據也發完了, 對端也想要關閉連接了
  2. 此端也已經發送了ACK應答報文, 告訴對端收到了FIN報文
  3. 但是, 此狀態并不會直接結束, 而是會持續一段時間,原因一: 對端發送的數據可能還在傳輸中, 所以并不能馬上關閉連接,還存在著滯留的報文,必須等它們消散。原因二: 對端可能沒有收到我發送的ACK應答, 對端可能還會發送FIN報文, 我還得再次ACK應答, 保證對端收到了ACK之后 正確關閉連接
  4. 此狀態持續時間, 不能過長 不能過短, TCP協議推薦值為2*MSL (后面分析解釋),MSL:一個消息從C到S或者從S到C的最大傳輸單元稱為MSL。

被動端分析?

?被動端收到對端的FIN報文, 并作出應答之后, 會進入?CLOSE_WAIT?狀態,該狀態的作用是

  1. 表示被動端已經收到了對端的FIN報文, 知道了對端要關閉連接 并且已經關閉了發送緩沖區 不再給被動端發送數據了
  2. 不過, 被動端暫時還不想關閉連接, TCP發送緩沖區內還有數據沒有發送完, 所以需要維持CLOSE_WAIT?狀態
  3. 并且, 需要在 對端沒有收到ACK應答, 再次發送FIN報文時, 重新應答ACK報文
  4. 還有, 被動端收到了FIN報文, 也會向應用層關閉發送緩沖區,提醒應用層, 將write()send()緩沖區里已經存在的數據發走之后, 就不要在發送數據了, 發送緩沖區要關閉了,不然, 還一直有數據要發送, 還要一直消耗雙方資源

此狀態持續時間, 取決于什么時候TCP發送緩沖區的數據發完, 并且與close()調用有關 (后面分析解釋)

被動端數據發送完, 調用close()發送FIN報文之后, 會進入LAST_ACK狀態,該狀態的作用是

  1. 表示被動端已經發送了FIN報文, 也要關閉連接了

  2. 等待對端的ACK應答報文, 即 需要知道 對端已經收到了被動端的FIN報文

    如果長時間沒有收到對端的ACK應答報文, 被動端需要重新發送FIN報文

    所以, 需要維護LAST_ACK狀態

直到收到對端的ACK應答, 才會關閉連接

在上面這5個狀態中, 有2個狀態很重要:?被動端的CLOSE_WAIT?狀態?和?主動端的TIME_WAIT 狀態?

并且, 這兩種狀態也是"四次揮手"過程中, 最容易觀察到的

三、通過實驗觀察CLOSE_WAIT?狀態TIME_WAIT 狀態?

下面我們使用一個最簡單的TCP服務器,并通過連接它來觀察現象


#include <iostream>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#define SOCKET_ERR  1
#define BIND_ERR    2
#define LISTEN_ERR  3
#define USE_ERR     4
#define CONNECT_ERR 5
#define FORK_ERR    6
#define WAIT_ERR    7#define BUFFER_SIZE 1024class tcpServer {
public:tcpServer(uint16_t port, const std::string& ip = ""): _port(port), _ip(ip), _listenSock(-1) {}void init() {_listenSock = socket(AF_INET, SOCK_STREAM, 0);if (_listenSock < 0) {// 套接字文件描述符創建失敗printf("socket() faild:: %s : %d\n", strerror(errno), _listenSock);exit(SOCKET_ERR); // 創建套接字失敗 以 SOCKET_ERR 退出}printf("socket create success: %d\n", _listenSock);// 套接字創建成功, 就需要將向 sockaddr_in 里填充網絡信息struct sockaddr_in local;std::memset(&local, 0, sizeof(local));// 填充網絡信息local.sin_family = AF_INET;local.sin_port = htons(_port);_ip.empty() ? (local.sin_addr.s_addr = htonl(INADDR_ANY)) : (inet_aton(_ip.c_str(), &local.sin_addr));// 綁定網絡信息到主機if (bind(_listenSock, (const struct sockaddr*)&local, sizeof(local)) == -1) {printf("bind() faild:: %s : %d\n", strerror(errno), _listenSock);exit(BIND_ERR);}printf("socket bind success : %d\n", _listenSock);if (listen(_listenSock, 2) == -1) {printf("listen() faild:: %s : %d\n", strerror(errno), _listenSock);exit(LISTEN_ERR);}printf("listen success : %d\n", _listenSock);// 開始監聽之后, 別的主機就可以發送連接請求了.}// 服務器初始化完成之后, 就可以啟動了void loop() {while (true) {/*  sleep(1);struct sockaddr_in peer;          // 輸出型參數 接受所連接主機客戶端網絡信息socklen_t peerLen = sizeof(peer); // 輸入輸出型參數int serviceSock = accept(_listenSock, (struct sockaddr*)&peer, &peerLen);if (serviceSock == -1) {printf("accept() faild:: %s : %d\n", strerror(errno), serviceSock);continue;}sleep(120);close(serviceSock); */sleep(1);}}private:uint16_t _port; // 端口號std::string _ip;int _listenSock; // 服務器套接字文件描述符
};void Usage(std::string proc) {std::cerr << "Usage:: \n\t" << proc << " port ip" << std::endl;std::cerr << "example:: \n\t" << proc << " 8080 127.0.0.1" << std::endl;
}int main(int argc, char* argv[]) {if (argc != 3 && argc != 2) {Usage(argv[0]);exit(USE_ERR);}uint16_t port = atoi(argv[1]);std::string ip;if (argc == 3) {ip = argv[2];}tcpServer svr(port, ip);svr.init();svr.loop();return 0;
}
  1. 需要注意的是上面的代碼所實現的服務器中accept() 接口并沒有被調用, close()接口也沒有被調用。?
  2. listen()接口的第二個參數設置為 2

1、CLOSE_WAIT?狀態

Ⅰ、先將服務器運行起來,在沒有建立連接的前提下使用netstat命令查看系統中處于監聽(LISTEN)狀態的TCP服務,如下

?Ⅱ 、接著使用telnet命令連接服務器,如下

?可以看到無論是客戶端到服務端還是服務端到客戶端,系統都有維護連接,狀態都處于ESTABLISHED狀態。可是我們在代碼中并沒有調用accept()接口,這是為什么呢?在我們潛意識中我們都認為accept()是來同意連接請求的,但是并不是這樣的,因為即使沒有調用accept()"三次握手"依然正常完成,雙方都進入了ESTABLISHED狀態。

這就說明“三次握手”的完成與否,實際上是和accept()無關的,accept()接口的作用只是將內核中已經與客戶端建立好連接的TCP連接數據信息,“拿”到進程中去執行。

可能很多人認為accept()接口是用來同意連接請求的, 但實際并不是的, 因為即使沒有調用accept(), "三次握手"依然是正常完成了, 雙端正常進入了ESTABLISHED狀態

所以, 這個現象說明 "三次握手"完成與否, 是與應用層是否調用accept()無關的

并且說明了, accept()接口的功能 只是將內核中已經與客戶端建立好的TCP連接數據、信息, "拿"到進程中使用

TCP協議中,三次握手的過程是由操作系統內核層面的TCP/IP協議棧處理的,而不是由應用程序直接控制的。這意味著即使服務器端的應用程序沒有調用 accept(),客戶端與服務器之間的三次握手仍然可以順利完成,并且連接狀態可以進入ESTABLISHED

accept() 函數的作用主要是用于從處于監聽狀態(LISTEN)的套接字上接收到來的連接請求。當有新的連接建立時(即三次握手中最后一個ACK已經收到并且連接已經建立),accept()會從已完成連接的隊列中取出一個連接,并為這個連接創建一個新的文件描述符,使得服務器可以通過這個新的文件描述符與客戶端進行數據交互。這樣做的好處之一是可以讓服務器管理多個并發的客戶端連接。

?Ⅲ 、接下來嘗試使用多個客戶端來連接此服務器

?可以看到建立的四條連接有三條都是建立成功的,只有一條的服務端的狀態為 SYN_RECYSYN_RECY 并不是標準的TCP狀態之一,而是一個特定于某些操作系統(尤其是Linux)中的狀態標識,用來描述類似的場景。在Linux內核中,當一個半開(half-open)連接存在時,即服務器已收到SYN包并發送了SYN-ACK但尚未收到最終的ACK確認,該連接可能會被標記為SYN_RECY。實際上,它指的是處于三次握手第二步和第三步之間的一個狀態,類似于標準TCP狀態轉換圖中的SYN_RCVD

這表示該條連接沒有建立成功,這是因為listen()接口的第二個參數設置為2,并且沒有調用accept()接口將建立好的連接拿走。

函數的第二個參數指定了處于待處理狀態的最大連接數,即那些已經完成了三次握手但還沒有被應用程序通過調用accept處理的連接數量。換句話說,它定義了等待隊列的最大長度,該隊列保存了那些等待被服務器接受(通過 accept方法)的客戶端連接。

  • 當一個客戶端嘗試連接到服務器時,如果此時服務器的待處理連接數小于設置的值,則該連接會被添加到等待隊列中。
  • 如果當連接請求到來時,等待隊列已滿(即當前未處理的連接數已經達到或超過了設置的數量),新的連接請求可能會被拒絕或者暫時懸置,具體行為取決于操作系統的實現。

IV、接下來我們使用客戶端對服務器進行連接,然后觀察客戶端斷開連接前后,服務端和客戶端所處的狀態。?

可以看到

  1. 客戶端發起連接之后,連接成功,服務端和客戶端相互維護連接,都是ESTABLISHED狀態。
  2. 客戶端主動關閉連接之后,服務端會進入COLSE_WAIT狀態。
  3. ?之后若服務端一直不停止運行,其會一直處于COLSE_WAIT狀態
  4. 只有服務端停止運行了,對應的連接狀態才會關閉
  • 客戶端主動關閉連接之后,可以看到先是進入了FIN_WAIT2狀態,在之后,客戶端就主動關閉了連接,這個狀態也就不見了,但是我們在上面的“四次揮手”中看到,如果客戶端沒有收到服務端的FIN報文的話,不是會一直處于FIN_WAIT2狀態嗎?

? ? ? ? ? ? ? ? 理論上是這樣的,但是Linux卻在實際中并沒有這么實現,Linux中 tcp_fin_timeout 指定了在強制關閉套接字前,等待最終FIN數據包的秒數。雖然它違反了TCP的規范,但是卻是防止服務器被攻擊所必需的,在Linux2.2中,該值默認為180

? ? ? ? ? ? ??也就是, 說Linux針對 主動端 等待 被動端的FIN報文 設定了一個時間限制. 只要 主動端等待的時間 超出了這個時間限制, 主動端就會強制關閉連接

這也就是為什么, 上面 客戶端進入FIN_WAIT2一段時間之后, 突然不見了

我們也可以發現,客戶端主動關閉連接之后,服務端會一直處于COLSE_WAIT狀態,盡管客戶端已經關閉了連接,所以這就會導致服務器一直無效地占用系統的資源。這是因為服務端沒有調用close()接口來關閉socket。?

下面我們將注釋的部分打開,然后做同樣的測試,如下

從觀察、分析的結果來看, 如果被動端 不調用close() 關閉此次連接創建的socket, 那么被動端就會一直處于CLOSE_WAIT狀態, 即使 已經不會再有任何通信

這就會導致, 被動端的系統資源得不到釋放, 一直被無效占用, 所以, 無論是客戶端還是服務端, 雙端在通信完成之后, 一定要調用close()關閉socket,釋放資源

2、TIME_WAIT 狀態

可以看到在 客戶端收到服務端的FIN 報文后依然處于TIME_WAIT 狀態一段時間后才關閉連接。

為什么需要維護一個TIME_WAIT狀態?

  • TIME_WAIT是"四次揮手"的主動發起方需要維持的一個狀態,我們知道, 主動端想要關閉連接, 被動端可能還存在數據未發送完畢 并不想要關閉連接,主動端是如何進入TIME_WAIT狀態的呢? 是被動端將數據發送完畢之后, 向主動端發送FIN報文, 主動端收到此報文并應答之后, 進入TIME_WAIT狀態,也就是說, 主動端向被動端 發送ACK應答報文之后, 才進入了TIME_WAIT,被動端是需要收到主動端的ACK應答報文才能正常關閉連接的, 所以主動端是需要保證 被動端確實收到了ACK應答報文的,如果, 被動端沒有收到主動端的ACK報文, 那么被動端是會重傳FIN報文的,因為, 被動端可能重傳FIN報文, 所以 主動端需要維持一段時間的TIME_WAIT狀態, 保證可能重傳的FIN報文不被漏掉
  • 其次, 如果在主動端應答了ACK報文之后, 不維護TIME_WAIT狀態 直接關閉連接, 被動端也收到了ACK報文正常的關閉了連接. 但是, 實際上網絡中還有報文在有效傳輸,如果, 此時 恰好 雙端使用了相同的四元組(源IP/目的IP:源Port/目的Port)建立了新的連接,那么 新的連接有沒有可能會收到 舊的有效報文呢? 舊報文會不會影響此次的連接呢?,答案當然是有可能的. 雖然因為序號和確認序號等標識 被影響的概率很低,所以, 需要維護一段時間的TIME_WAIT狀態, 保證舊報文傳輸完畢或失效。

TIME_WAIT狀態維持的時間是多少? 為什么?

????????

在簡單分析"四次揮手"雙端狀態時, 提到過 TIME_WAIT的持續時間不能太長, 不能太短, TCP協議標準 推薦值為2*MSL

MSL(Maximum Segment Lifetime), 表示 TCP報文在網絡中的最大生存時間. 不同系統 可能設置不同的MSL, 如果一個TCP報文在網絡中傳輸的時間超過了系統的MSL, 那么此報文到達目的地時會被丟棄

TCP協議標準 推薦TIME_WAIT的持續時長為2倍MSL, 為什么呢?

因為 如果TIME_WAIT持續2*MSL的時長, 那么基本可以保證此次連接 雙方發送的報文 都已經傳輸完畢或已經失效,如果, 主動端在TIME_WAIT期間 再次收到了被動端的FIN報文, 主動端會重新發送ACK報文并進入新的TIME_WAIT

那么, 主動端發送ACK應答報文之后, 此報文會有兩種結果:

  • 丟失, 即被動端一直沒收到ACK報文,此時, 被動端會一直重傳FIN, 直到達到重傳的上限,否則, 雙端的狀態一般是不會變化的,你可能會想, 如果被動端一直在重傳FIN, 但是每一個都沒有被主動端收到,然后重傳時間超出了2*MSL, 主動端都關閉連接了, 被動端還在重傳FIN,如果出現了這種情況, 那么在被動端達到重傳上限之前, 網絡中會一直存在有效的FIN報文,這怎么解決?只能說出現這種情況的概率, 非常非常低. 不過 在此種情況中, 由于被動端在達到重傳上線之前一直沒有關閉連接, 也就沒有釋放資源, 系統一直占用著四元組資源所以, 也不會出現 雙端使用相同四元組建立新連接的情況,當然, 還有最極限的一種情況: 被動端剛好達到重傳上限, 重傳了最后一個FIN報文, 剛好關閉連接釋放資源, 雙端就使用了相同的四元組建立了新的連接,此時, 網絡中還傳輸有有效的FIN報文, 新連接就又可能被影響,但是, 好像還是無法解決, 只能說出現這種情況的概率 更更更更低了,這都是非常非常非常極端的情況, 基本不需要考慮
  • 被動端收到了ACK報文,那么, 以此報文可以被接收為前提, 最長的傳輸時長就是不超過MSL,并且, 被動端有可能在收到ACK的前一瞬 剛好重傳了一份FIN報文, 那么 這一份FIN在網絡中傳輸失效需要的時間就是MSL,ACK最長的傳輸時間+FIN傳輸失效需要的時間<=2*MSL,所以, TCP協議標準 推薦TIME_WAIT持續時長為2*MSL

3、setsockopt()?

TIME_WAIT持續時間太久也會無效占用系統資源,除了占用系統資源還會造成一些其他的影響。比如我們平時斷開服務器連接時,如果想要接著短時間內再次開啟服務器,就會出現連接不成功的問題,如下

這就是為什么每次斷開服務器之后短時間連接不成功的問題。

由于服務端主動關閉連接, 維持在TIME_WAIT狀態, 導致端口資源無法釋放, 耽誤服務重啟

Linux系統TIME_WAIT維持時間在60s左右

服務器在實際運行中, 如果整個服務掛掉了, 服務器建立的每一個連接都會進入TIME_WAIT, 只要有一個連接沒有正式關閉, 服務就無法使用相同的端口重啟, 難道服務要等待60s再重啟嗎?

要解決這個問題, 除了直接從系統層面做優化之外. Linux還提供了一個系統調用setsockopt()?

#include <sys/socket.h>int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
/** sockfd, 創建的套接字* level, 協議層 可以指定協議, 這里使用 SOL_SOCKET* optname, 選項名* optval, 要設置的值/內容, 需要根據選項的實際類型進行定義和填充, 是一個輸入性參數* optlen, optval的長度/大小*/

?這個系統調用可以 對進程中的指定 socket 的行為 做出一些調整, 即 設置套接字的一些選項, 需要調用在創建套接字之后

有兩個選項, 可以使相同的服務直接使用相同的端口/IP創建套接字, 即使之前的連接還未正式關閉

這兩個選項看作布爾值, 可以直接以0/1設置

1、SO_REUSEADDR

可以在服務進入TIME_WAIT之后, 即使 沒有正式關閉連接, 讓服務使用相同的port和IP創建socket并bind. 不過前提是, 需要是同一個服務

// 創建套接字之后
int opt = 1;
setsockopt(_listenSock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

2、SO_REUSEPORT?

允許不同服務在任何狀態下, 使用相同的port和IP創建socket并bind, 之前的服務的連接不用在TIME_WAIT狀態

// 創建套接字之后
int opt = 1;
setsockopt(_listenSock, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));


感謝閱讀!?

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

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

相關文章

某碰瓷國賽美賽,號稱第三賽事的數模競賽

首先我非常不能理解的就是怎么好意思自稱第三賽事的呢&#xff1f;下面我們進行一個簡單討論&#xff0c;當然這里不對國賽和美賽進行討論。首先我們來明確一點&#xff0c;比賽的含金量由什么來定&#xff1f;這個可能大家的評價指標可能不唯一&#xff0c;我通過DeepSeek選取…

Redis 緩存問題:緩存雪崩、緩存擊穿、緩存穿透

文章目錄 緩存雪崩緩存擊穿緩存穿透在實際的業務場景中,Redis 通常作為緩存和其他數據庫(例如 MySQL)搭配使用,用來減輕數據庫的壓力。但是在使用 Redis 作為緩存數據庫的過程中,可能會遇到一些常見問題,例如緩存穿透、緩存擊穿和緩存雪崩等。 緩存雪崩 緩存雪崩是指緩存…

Qt 入門 4 之標準對話框

Qt 入門 4 之標準對話框 Qt提供了一些常用的對話框類型,它們全部繼承自QDialog類,并增加了自己的特色功能,比如獲取顏色、顯示特定信息等。下面簡單講解這些對話框,可以在幫助索引中查看Standard Dialogs關鍵字,也可以直接索引相關類的類名。 本文將以一個新的項目為主介紹不…

買不起了,iPhone 或漲價 40% ?

周知的原因&#xff0c;新關稅對 iPhone 的打擊&#xff0c;可以說非常嚴重。 根據 Rosenblatt Securities分析師的預測&#xff0c;若蘋果完全把成本轉移給消費者。 iPhone 16 標配版的價格&#xff0c;可能上漲43%。 iPhone 16 標配的價格是799美元&#xff0c;上漲43%&am…

軟件需求分析習題匯編

需求工程練習題 一、選擇題 1. 軟件需求規格說明書的內容不應包括對&#xff08; &#xff09;的描述。 A. 主要功能B. 算法的詳細過程C. 用戶界面及運行環境D. 軟件的性能 *正確答案:*B:算法的詳細過程; 2. 需求分析最終結果是產生&#xff08; &#xff09; A. 項目開發…

clickhouse注入手法總結

clickhouse 遇到一題clickhouse注入相關的&#xff0c;沒有見過&#xff0c;于是來學習clickhouse的使用&#xff0c;并總結相關注入手法。 環境搭建 直接在docker運行 docker pull clickhouse/clickhouse-server docker run -d --name some-clickhouse-server --ulimit n…

智能語音識別工具開發手記

智能語音識別工具開發手記 序言&#xff1a;聽見數字化的聲音 在縣級融媒體中心的日常工作中&#xff0c;我們每天需要處理大量音頻素材——從田間地頭的采訪錄音到演播室的節目原聲&#xff0c;從緊急會議記錄到專題報道素材。二十多年前&#xff0c;筆者剛入職時&#xff0…

TDengine 3.3.6.0 版本中非常實用的 Cols 函數

簡介 在剛剛發布的 TDengine 3.3.6.0 版本 中&#xff0c;新增了一個非常實用的 函數COLS &#xff0c;此函數用于獲取選擇函數所在行列信息&#xff0c;主要應用在生成報表數據&#xff0c;每行需要出現多個選擇函數結果&#xff0c;如統計每天最大及最小電壓&#xff0c;并報…

【AI學習】AI Agent(人工智能體)

1&#xff0c;AI agent 1&#xff09;定義 是一種能夠感知環境、基于所感知到的信息進行推理和決策&#xff0c;并通過執行相應動作來影響環境、進而實現特定目標的智能實體。 它整合了多種人工智能技術&#xff0c;具備自主學習、自主行動以及與外界交互的能力&#xff0c;旨…

【MCP】VSCode Cline配置MongoDB連接

VSCode MCP插件配置MongoDB連接教程 前言 本文將介紹如何在VSCode中配置Cline插件連接MongoDB。 環境準備 VSCodeNode.jsMongoDB服務器Cline插件 配置步驟 1. 安裝MCP插件 在VSCode擴展商店中搜索"Cline"并安裝。 安裝完之后需要配置API平臺以及設置API-KEY。…

this指針 和 類的繼承

一、this指針 Human類的屬性fishc與Human&#xff08;&#xff09;構造器的參數fishc同名&#xff0c;但卻是兩個東西。使用this指針讓構造器知道哪個是參數&#xff0c;哪個是屬性。 this指針&#xff1a;指向當前的類生成的對象 this -> fishc fishc當前對象&#xff08;…

使用PyTorch訓練VGG11模型:Fashion-MNIST圖像分類實戰

本文將通過代碼實戰&#xff0c;詳細講解如何使用 PyTorch 和 VGG11 模型在 Fashion-MNIST 數據集上進行圖像分類任務。代碼包含數據預處理、模型定義、訓練與評估全流程&#xff0c;并附上訓練結果的可視化圖表。所有代碼可直接復現&#xff0c;適合深度學習初學者和進階開發者…

汽車BMS技術分享及其HIL測試方案

一、BMS技術簡介 在全球碳中和目標的戰略驅動下&#xff0c;新能源汽車產業正以指數級速度重塑交通出行格局。動力電池作為電動汽車的"心臟"&#xff0c;其性能與安全性不僅直接決定了車輛的續航里程、使用壽命等關鍵指標&#xff0c;更深刻影響著消費者對電動汽車的…

打造船岸“5G+AI”智能慧眼 智驅力賦能客船數智管理

項目介紹 船舶在航行、作業過程中有著嚴格的規范要求&#xff0c;但在實際航行與作業中往往會因為人為的疏忽&#xff0c;發生事故&#xff0c;導致人員重大傷亡和財產損失&#xff1b; 為推動安全治理模式向事前預防轉型&#xff0c;實現不安全狀態和行為智能預警&#xff0c…

C#二叉樹

C#二叉樹 二叉樹是一種常見的數據結構&#xff0c;它是由節點組成的一種樹形結構&#xff0c;其中每個節點最多有兩個子節點。二叉樹的一個節點通常包含三部分&#xff1a;存儲數據的變量、指向左子節點的指針和指向右子節點的指針。二叉樹可以用于多種算法和操作&#xff0c;…

WinForm真入門(11)——ComboBox控件詳解

WinForm中 ComboBox 控件詳解? ComboBox 是 WinForms 中一個集文本框與下拉列表于一體的控件&#xff0c;支持用戶從預定義選項中選擇或直接輸入內容。以下從核心屬性、事件、使用場景到高級技巧的全面解析&#xff1a; 一、ComboBox 核心屬性? 屬性說明示例?Items?下拉…

超詳細解讀:數據庫MVCC機制

之前文章&#xff1a;Mysql鎖_exclusivelock for update寫鎖-CSDN博客 中有提到通過MVCC來實現快照讀&#xff0c;從而解決幻讀問題&#xff0c;這里詳細介紹下MVCC。 一、前言 表1&#xff1a;實例表t idk1122 表2&#xff1a;事務A、B、C的執行流程 事務A事務B事務Cstart …

【SpringCloud】從入門到精通【上】

今天主播我把黑馬新版微服務課程MQ高級之前的內容都看完了&#xff0c;雖然在看視頻的時候也記了筆記&#xff0c;但是看完之后還是忘得差不多了&#xff0c;所以打算寫一篇博客再溫習一下內容。 課程坐標:黑馬程序員SpringCloud微服務開發與實戰 微服務 認識單體架構 單體架…

力扣hot100_回溯(2)_python版本

一、39. 組合總和&#xff08;中等&#xff09; 代碼&#xff1a; class Solution:def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:ans []path []def dfs(i: int, left: int) -> None:if left 0:# 找到一個合法組合ans.append(pa…

AI平臺如何實現推理?數算島是一個開源的AI平臺(主要用于管理和調度分布式AI訓練和推理任務。)

數算島是一個開源的AI平臺&#xff0c;主要用于管理和調度分布式AI訓練和推理任務。它基于Kubernetes構建&#xff0c;支持多種深度學習框架&#xff08;如TensorFlow、PyTorch等&#xff09;。以下是數算島實現模型推理的核心原理、架構及具體實現步驟&#xff1a; 一、數算島…