【Linux深入淺出】之全連接隊列及抓包介紹

【Linux深入淺出】之全連接隊列及抓包介紹

  • 理解listen系統調用函數的第二個參數
    • 簡單實驗
      • 實驗目的
      • 實驗設備
      • 實驗代碼
      • 實驗現象
    • 全連接隊列簡單理解
      • 什么是全連接隊列
      • 全連接隊列的大小
  • 從Linux內核的角度理解虛擬文件、sock、網絡三方的關系
    • 回顧虛擬文件部分的知識
    • struct socket結構體介紹
    • struct tcp_sock與struct udp_sock介紹
      • struct tcp_sock
        • struct inet_connection_sock結構體
        • struct inet_sock結構體
        • 總結
      • struct udp_sock
      • Tcp接收緩沖區與發送緩沖區
    • 分層介紹
  • tcp抓包介紹
    • Linux中使用tcp dump進行抓包并分析tcp過程
      • tcp dump的安裝
      • tcp dump的簡單使用
      • 實驗
    • windows中使用wireshark進行抓包
      • wireshark的安裝
      • 使用telnet作為客戶端訪問云服務器上的服務器程序
      • 設置wireshark過濾規則
      • 使用wireshark進行抓包

理解listen系統調用函數的第二個參數

listen函數是在進行TCP socket編程時的系統調用函數,它的功能是將普通套接字設置為監聽狀態,也就是將普通的套接字變成監聽套接字,以便它能收到來自客戶端的連接請求。

image-20250218101233322

第一個參數是我們之前創建的socket描述符,那么第二個參數應該如何理解呢?直接輸出結論:backlog規定了全連接隊列的最大長度,全連接隊列是用于維護三次握手成功但是系統來不及接收的連接,backlog+1是這個隊列的長度。

簡單實驗

實驗目的

下面我們將做一個小實驗,這個實驗主要會驗證如下幾個點:

  • 三次握手成功建立連接,并不需要accept的參與,因為它是系統自動完成的,accept只是負責從全連接隊列中取走已經建立好的連接。
  • backlog+1 = 全連接隊列的長度。

因為accept函數會取走全連接隊列中的連接,而且我們的實驗就是模擬系統非常忙的情況,所以 TCP server端是不需要調用accept函數的。

實驗設備

虛擬機一臺,云服務器一臺。

在同一臺設備上會影響實驗效果,因為TCP連接是雙向的,從服務器->客戶端,客戶端->服務器都會維護一個連接,所以如果在一臺設備上做實驗,會有干擾。

實驗代碼

  1. TcpServer.cc:

    #include <iostream>
    #include <string>
    #include <cerrno>
    #include <cstring>
    #include <cstdlib>
    #include <memory>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/wait.h>
    #include <unistd.h>const static int default_backlog = 1;enum
    {Usage_Err = 1,Socket_Err,Bind_Err,Listen_Err
    };#define CONV(addr_ptr) ((struct sockaddr *)addr_ptr)class TcpServer
    {
    public:TcpServer(uint16_t port) : _port(port), _isrunning(false){}// 都是固定套路void Init(){// 1. 創建socket, file fd, 本質是文件_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){exit(0);}int opt = 1;setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));// 2. 填充本地網絡信息并bindstruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = htonl(INADDR_ANY);// 2.1 bindif (bind(_listensock, CONV(&local), sizeof(local)) != 0){exit(Bind_Err);}// 3. 設置socket為監聽狀態,tcp特有的if (listen(_listensock, default_backlog) != 0){exit(Listen_Err);}}void ProcessConnection(int sockfd, struct sockaddr_in &peer){uint16_t clientport = ntohs(peer.sin_port);std::string clientip = inet_ntoa(peer.sin_addr);std::string prefix = clientip + ":" + std::to_string(clientport);std::cout << "get a new connection, info is : " << prefix << std::endl;while (true){char inbuffer[1024];ssize_t s = ::read(sockfd, inbuffer, sizeof(inbuffer)-1);if(s > 0){inbuffer[s] = 0;std::cout << prefix << "# " << inbuffer << std::endl;std::string echo = inbuffer;echo += "[tcp server echo message]";write(sockfd, echo.c_str(), echo.size());}else{std::cout << prefix << " client quit" << std::endl;break;}}}void Start(){_isrunning = true;while (_isrunning){sleep(1);}}~TcpServer(){}private:uint16_t _port;int _listensock; // TODObool _isrunning;
    };using namespace std;void Usage(std::string proc)
    {std::cout << "Usage : \n\t" << proc << " local_port\n"<< std::endl;
    }
    // ./tcp_server 8888
    int main(int argc, char *argv[])
    {if (argc != 2){Usage(argv[0]);return Usage_Err;}uint16_t port = stoi(argv[1]);std::unique_ptr<TcpServer> tsvr = make_unique<TcpServer>(port);tsvr->Init();tsvr->Start();return 0;
    }
    
  2. TcpClient.cc

    #include <iostream>
    #include <string>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <arpa/inet.h>
    #include <netinet/in.h>int main(int argc, char **argv)
    {if (argc != 3){std::cerr << "\nUsage: " << argv[0] << " serverip serverport\n"<< std::endl;return 1;}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);int clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (clientSocket < 0){std::cerr << "socket failed" << std::endl;return 1;}sockaddr_in serverAddr;serverAddr.sin_family = AF_INET;serverAddr.sin_port = htons(serverport);                  // 替換為服務器端口serverAddr.sin_addr.s_addr = inet_addr(serverip.c_str()); // 替換為服務器IP地址int result = connect(clientSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr));if (result < 0){std::cerr << "connect failed" << std::endl;::close(clientSocket);return 1;}while (true){std::string message;std::cout << "Please Enter@ ";std::getline(std::cin, message);if (message.empty())continue;send(clientSocket, message.c_str(), message.size(), 0);char buffer[1024] = {0};int bytesReceived = recv(clientSocket, buffer, sizeof(buffer) - 1, 0);if (bytesReceived > 0){buffer[bytesReceived] = '\0'; // 確保字符串以 null 結尾std::cout << "Received from server: " << buffer << std::endl;}else{std::cerr << "recv failed" << std::endl;}}::close(clientSocket);return 0;
    }
    

實驗現象

  1. 驗證:即使服務器端沒有調用accpet函數,三次握手也能建立連接成功:

    image-20250218104617555

    image-20250218111030075

    • 結論:accept系統調用函數并不參與三次握手,它只負責從下層取走連接(socket文件描述符)。
  2. 驗證Tcp全連接最多維護backlog+1個連接:

    image-20250218110201620

  • 可以看到,我們在虛擬機中同時運行了多個TcpClient客戶端,但最終只有兩個成功建立連接,這是因為全連接的大小不夠了所以不會接收來自客戶端的連接,只有上層調用accept拿走在全連接隊列中已經建立的連接,全連接的空間才會騰出來。

全連接隊列簡單理解

什么是全連接隊列

全連接隊列就是我們內核(傳輸層)中某個結構體中維護的一個隊列,每一個listen套接字都有一個全連接隊列:

image-20250218111735714

在Linux內核中所謂的連接和全連接隊列都是struct結構體,后面我們會結合Linux內核重點介紹。

  • 注意:全連接隊列的大小并不是代表TcpServer服務器只能同時處理這么多,而是表示它來不及處理(來不及調用accept)的連接的最大數量。

全連接隊列的大小

全連接隊列的本質其實是生產者消費者模型,全連接隊列作為生產者一直生產連接,而上層的accept作為消費者一直從全連接隊列中取走連接。

全連接隊列的大小不能太大,也不能太小

  • 如果backlog為0:會增加服務器的閑置率,如果有全連接隊列,那么可能只需要等待一會,服務器就有空閑可以把連接取走處理了,如果backlog為0,直接就是三次握手建立不成功,用戶就以為你的服務已經崩潰,短時間內就不會訪問了。
  • 如果backlog過大:那么處于全連接隊列結尾的用戶就可能需要等待很久來能享受到服務,這樣用戶體驗不好,還不如直接連接失敗,而且維護連接也會占用內存,用多余的內存去給服務器處理數據可能效率更高。

過去,常用的默認值可能是5或者50左右,但是現代Linux系統的默認值通常要高得多。

通常這個值有一個上限,我的Linux系統中為4096:

image-20250218113737436

  • /proc/sys/net/core/somaxconn:這個內核參數定義了系統范圍內每個端口的最大監聽隊列長度。它設置了listen()函數中backlog參數的上限值。
  • tcp_max_syn_backlog:這個文件中規定的是未完成連接請求的最大數量(半連接隊列)。

從Linux內核的角度理解虛擬文件、sock、網絡三方的關系

回顧虛擬文件部分的知識

我們都知道在運行服務器程序后,系統會給這個進程創建一個task_struct結構體,它是用來描述進程的,這個結構體中又會有一個file_struct*的指針,它指向了一個file_struct的對象,這個file_struct結構體是用來管理打開的文件的,它里面有一個文件描述符表,這個文件描述符表中的每一個下標都指向struct file*對象。

但是我們的網絡socket是怎么和struct file這個結構體掛上聯系的呢?這是我們今天要解決的問題之一,因為文件描述表中的文件描述符不僅僅有普通文件的還有網絡套接字文件。

image-20250218114925683

struct socket結構體介紹

struct socket {socket_state		state;unsigned long		flags;struct proto_ops	*ops;struct fasync_struct	*fasync_list;struct file		*file;struct sock		*sk;wait_queue_head_t	wait;short			type;unsigned char		passcred;
};

struct socket結構體是我們網絡socket的入口,它是一個通用的套接字類型。

  • short type:表示套接字的類型,是流式套接字還是數據報式的套接字:

    image-20250218120610357

  • struct proto_ops *ops:它是一個保存各種函數方法的類型,可以通過type字段讓其指向不同的方法。

    image-20250218120414776

  • struct file*:指向虛擬文件層的struct file對象,但是我們不是需要通過socket文件描述符找到struct socket嘛,怎么順序反過來,別擔心,其實struct file對象中也有一個開放字段是可以指向struct socket對象的,它就是void*類型的private_data字段。

所以經過對struct socket結構體的學習,我們上面的圖可以繼續完善:

image-20250218121338553

并且調用socket系統調用的同時就創建了struct socketstruct file并在文件描述表中申請了空間,然后還讓struct socketstruct file互相指向。

Tcp SocketUdp Socket豈不是沒有區別了,既然調用socket都會創建struct socket的話,別急,我們繼續往下學習。

struct tcp_sock與struct udp_sock介紹

struct tcp_sock

image-20250218133948690

tcp_sock結構體中有很多關于tcp的字段,譬如:

  • int tcp_header_len:即將要發送的TCP報文的頭部的長度,以字節為單位。
  • rcv_nxt, snd_nxt: 分別表示期望接收的下一個序列號和發送方即將發送的下一個序列號。
  • snd_ssthresh, snd_cwnd: 慢啟動閾值和擁塞窗口大小,是擁塞控制的重要參數。

但我們更想知道,它的第一個字段struct inet_connection_sock是什么:

  • struct inet_connection_sock inet_conn;:光看其名稱,這個結構體肯定與連接有關。
struct inet_connection_sock結構體

這個結構體是描述的TCP與連接相關的屬性,里面包括了全連接隊列。全連接隊列中不僅維護三次握手已經建立好的連接,也會維護只進行了二次或者一次的半連接,但是半連接的生命周期一般很短

image-20250218134723123

  • struct request_sock_queue icsk_accept_queue;:這個字段就是我們之前一直在談的全連接隊列,它由listensock維護,用于管理監聽套接字上的半連接(SYN_RCVD狀態)和全連接(ESTABLISHED狀態但未被accept()接受)隊列。

    image-20250218134936143

但我們最好奇的是它的第一個屬性字段:

  • struct inet_sock inet:這是一個 struct inet_sock 類型的成員,包含了通用的因特網套接字信息。tcp_sock 以此為基礎,添加TCP特定的信息。
struct inet_sock結構體

image-20250218123449300

struct inet_sock結構體中存儲的是與網絡通信相關的信息,例如:

  • _u32 daddr:外部IPv4地址。
  • _u32 rcv_saddr:本地IPv4地址。
  • _u32 dport:目的端口號。

我們進行Tcp網絡通信,調用bind系統調用函數bindIP地址和端口號,不就是在往這個struct inet_sock結構體中寫數據嗎?

我們驚奇的發現這個inet_sock結構體的第一個字段的類型居然是struct sock,我們之前不是在struct socket里面見過這個字段嗎,讓struct socket指向它,不就可以通過通用套接字訪問到Tcpsock了嗎?

所以我們預測udp_sock中一定也存在struct sock字段,而且一定是在最前面。

image-20250218135232593

總結

看了這么多結構體,我們可以畫圖總結一下它們的關系了:

后續只需要通過socket中的struct sock*字段,通過強制類型轉化,我們就可以訪問到struct sockstruct inet_sockstruct inet_connection_sockstruct tcp_sock結構體的內容,因為它們的初始地址都是相同的,這樣通過結構體嵌套,我們就實現了C風格的多態。

那么當我們客戶端和服務器經過三次握手后,建立了一個新的連接,內核會幫助我們做哪些事情呢?

  1. 最最重要的是創建struct inet_connection_sock,這表示一個新的連接,里面的inet_sock字段存儲著這個連接相關的屬性字段(IP地址、端口號)。

  2. 然后就是struct tcp_sock對象,三次握手完成,內核實際上已經為這個新建立的連接創建了完整的struct tcp_sock結構體。它不僅包含了inet_connection_sock中的所有字段,還添加了許多TCP特有的屬性和方法,例如序列號管理、窗口縮放、重傳機制等。每當一個新的TCP連接被接受(即完成了三次握手),就會創建一個tcp_sock實例來管理這個連接的狀態和行為。

  3. 除此之外,內核還會將這個連接(隊尾的nextstruct sock*指向新連接的struct sock)加入listen套接字的全連接隊列中(做類似鏈表的操作),然后需要將隊列的元素個數加1。

    image-20250220112433080
    內核中會有實現上述功能的方法:

    image-20250218160530154

如果全連接隊列中沒有空間了,三次握手根本就不會完成,也就不會創建上述的結構體。

當調用accept函數時,它會做如下事情:

  1. 創建struct socket對象(三次握手完成時并沒有創建這個通用的套接字類型),然后從全連接隊列中取出隊頭連接的struct sock*,然后賦值給struct socketstruct sock*變量,就相當與讓struct socket指向了struct tcp_sock,因為struct tcp_sock的最開始的字段是struct sock*

  2. 創建struct file,并在文件描述符表中開辟一個新的空間指向這個struct file對象。

  3. 最后,讓struct filestruct socket互相引用。

  4. 返回文件描述符給上層。

    內核中的方法sock_map_fd就是實現類似功能的。

    image-20250220113021007

自此之后,我們就可以通過socket fd找到struct file,然后通過struct file中的private_data字段找到struct socket對象,而通用套接字的sk又指向struct tcp_sock的首地址空間的struct sock對象。然后通過強制類型轉換,可以訪問tcp這個連接相關的任何信息,包括報文、擁塞控制屬性、滑動窗口屬性、確認應答相關屬性(序號、確認序號)。

struct udp_sock

image-20250218194218690

由于udp協議比tcp協議要簡單,所以udp_sock結構體的字段也要少一些。

而且由于udp是無連接的協議,所以它沒有連接相關的字段,它的第一個結構體對象就直接是struct inet_sock結構體。這和tcpinet_sock是一樣的,因為網絡套接字部分兩者有很多相同的部分,所以可以復用。

對于udp_sock就是這樣:

Tcp接收緩沖區與發送緩沖區

我們前面不是一直談到TCP存在接收緩沖區和發送緩沖區嗎?它們在內核中是否有體現呢?

當然有,在struct sock結構體中,存在著這兩個字段:

image-20250218200122058

它們就是接收緩沖區與發送緩沖區,每個連接都有單獨的struct sock,也就意味著有單獨的接收緩沖區與發送緩沖區。

sk_buff_head是這個緩沖區的類型,它是一個類似隊列的結構體:

image-20250218200326278

struct sk_buff是描述報文的,也就是解析出來或者即將發送的應用層的報文:

image-20250218200618750

分層介紹

自此之后,虛擬文件、socket、網絡三者的關系我們就清楚了,我們也清楚了如何通過文件描述符找到關于套接字的各種信息。

它們自上而下是有層次的,可以分為虛擬文件層、通用套接字層和網絡套接字層。其中通用套接字就像是一個基類,它提供了一種通用的方式來創建各種類型的套接字,但是當網絡真的建立起來,又會有其它細微的不同。

tcp抓包介紹

Linux中使用tcp dump進行抓包并分析tcp過程

tcp dump的安裝

ubuntu下

sudo apt update
sudp apt install -y tcpdump

通過檢查版本號驗證是否安裝成功

tcpdump --version

image-20250220121020961

tcp dump的簡單使用

  1. 捕獲所有網絡接口中的報文:

    sudo tcpdump -i any tcp
    
    • -i:interface是接口的意思,any代表任何,-i any的意思就是捕獲所以網絡接口中的報文。
    • tcp:只捕獲tcp報文。

    image-20250220121732786

  2. 捕獲指定網絡接口的報文:

    sudo tcpdump -i [本機某網絡接口名稱] tcp
    

    我們可以通過命令ifconfig查看本主機的所有網絡接口:

    image-20250220122051444

    image-20250220122247575

  3. 捕獲指定源IP的報文:

    sudo tcpdump src host 192.168.0.1 and tcp
    

    上述命令的含義是:捕獲源IP地址為192.168.0.1的到達本主機的tcp報文:

    image-20250220122953630

    • 現在的一般后端服務器都會使用反向代理來實現負載均衡技術,在大型應用或服務中,通常會部署多個反向代理服務器以提高性能、增加可用性和提供冗余。所以可能就會出現多次ping qq.com這個相同的域名,得到來自不同公網IP服務器的回復,這也不用驚訝,所以上面的實驗存在一定的運氣的成分。

      image-20250220123330174

      • 上面的顯示的公網IP可能是反向代理服務器的IP地址,而不是后端服務真正的公網IP。
  4. 捕獲指定目的IP地址的報文:

    sudo tcpdump dst host 192.168.0.1 and tcp
    

    上面命令的含義是捕獲目的IP地址為192.168.0.1tcp報文:

    image-20250220125705218

    • 注意這個目的IP為什么是iZt8qyfqyfs47mZ呢?云服務提供商使用類似的隨機字符串作為實例ID或設備標識:

      image-20250220134359259

      你也可以去云服務網站的控制臺修改這個實例名稱。

    • 但是如果我們希望它顯示IP地址,而不是顯示云服務器的實例名稱該怎么辦呢?加上選項-n即可:

      image-20250220134659966

  5. 捕獲特定端口號的TCP報文:

    • 使用port關鍵字可以捕獲特定端口號的報文,例如捕獲80端口的TCP報文(通常是http請求):

      sudo tcpdump port 80 and tcp
      

      image-20250220135146901

實驗

使用tcpdump工具,一般以捕獲特定端口的形式居多,代碼和上述的驗證listen系統調用函數的第二個參數的代碼一樣,簡單的tcp echo服務器:

  1. 服務器不給客戶端發送數據,也不accept接受連接:

    image-20250220135716488

    • 將客戶端在虛擬機上運行,觀察抓包現象:

      1. 三次握手:

        image-20250220161715672

        • 因為三次握手是沒有發送數據的,所以length為0。
      2. 當我們虛擬機客戶端給服務器發送數據報文,但是服務器收到該報文,發送的ack報文數據為0,沒有發送數據報文,我們有理由相信,服務器根本沒有將這個連接拿上來給用戶,但是三次握手肯定成功了,并且ack報文是OS自動發送的,不需要用戶參與:

        image-20250220162304996

        image-20250220162559707

        • 看了一下代碼果然沒有將連接拿上來。
  2. accept函數注釋取消后繼續實驗:

    • 三次握手部分(依舊正常):Flags中的S代表SYN標志位,win是窗口大小(用于滑動窗口中確定窗口大小),可以看到雙方還協商了mss的大小。

      外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

    • 服務器接收數據,發送數據:

      image-20250220163401186

      現在收發數據都正常了。

      image-20250220163505417

    • 四次揮手部分,客戶端主動退出:

      外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳

      • 就只有客戶端給服務器發送了FIN報文,服務器OS自動給它回復了一個ACK報文,服務器并沒有斷開連接,我們有理由相信,服務器端忘記close關閉socket描述符了。

        image-20250220163807463

  3. 服務器端在客戶端關閉連接后也要正常關閉連接,修改代碼后繼續測試四次揮手的過程:

    image-20250220165028109

    • 不是說四次揮手嗎,為什么只有三次呢,我們有理由相信,在客戶端給服務器發送FIN報文后,服務器立馬就給客戶端發送了FIN報文,并且這個時間和系統自動發送ACK報文的時間幾乎是同時,所以觸發了捎帶應答,如果我們讓服務器sleep上1s再關閉socketfd,就可以看到四次揮手:

      image-20250220170319069

    • sleep后的結果:

      image-20250220170355643

windows中使用wireshark進行抓包

wireshark的安裝

wireshark-4.4.3-x64.exe

下載好之后,直接安裝即可,沒有太多要注意的地方。

使用telnet作為客戶端訪問云服務器上的服務器程序

默認windows上telnet服務是沒有打開的,我們可以手動打開,打開telnet教程

設置wireshark過濾規則

  1. 首先選擇你想捕獲哪個網卡的流量(上行和下行):

    image-20250220174448817

  2. 選擇好之后,頂部工具欄點捕獲,點開始,就可以開始捕獲該網卡的流量:

    image-20250220174611588

  3. 默認是捕獲經過該網絡接口的流量:

    image-20250220174732358

  4. 在頂部可以設置過濾規則,我們設置ip為服務器ip,只關心服務器所在的端口號8888

    ip.addr == 121.40.68.117 && tcp.port == 8888
    

    頂部過濾欄是綠色說明語法沒有問題:

    image-20250220175032517

使用wireshark進行抓包

  1. 啟動服務器程序:

    image-20250220175112957

  2. 啟動windows上的telnet服務:

    telnet [服務器公網ip] [端口號]
    

    image-20250220175243523

    • 進入這個界面就代表啟動成功了。
  3. 觀察報文:

    1. 三次握手:

      image-20250220175444404

    2. telnet發送1字節的數據:

      image-20250220180035091

      點擊某一個包,下面可以看到更詳細的信息:

      image-20250220180214708

  4. 四次揮手,telnet輸入ctrl ]進入命令行模式,然后點quit就可退出:

    image-20250220180532792

紅色的報文為超時重傳。

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

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

相關文章

DB-GPT V0.7.1 版本更新:支持多模態模型、支持 Qwen3 系列,GLM4 系列模型 、支持Oracle數據庫等

V0.7.1版本主要新增、增強了以下核心特性 &#x1f340;DB-GPT支持多模態模型。 &#x1f340;DB-GPT支持 Qwen3 系列&#xff0c;GLM4 系列模型。 &#x1f340; MCP支持 SSE 權限認證和 SSL/TLS 安全通信。 &#x1f340; 支持Oracle數據庫。 &#x1f340; 支持 Infini…

2025五一數學建模競賽A題完整分析論文(共45頁)(含模型、可運行代碼、數據)

2025年五一數學建模競賽A題完整分析論文 摘 要 一、問題分析 二、問題重述 三、模型假設 四、符號定義 五、 模型建立與求解 5.1問題1 5.1.1問題1思路分析 5.1.2問題1模型建立 5.1.3問題1參考代碼 5.1.4問題1求解結果 5.2問題2 5.2.1問題2思路分析 …

[學習]RTKLib詳解:pntpos.c與postpos.c

文章目錄 RTKLib詳解&#xff1a;pntpos.c與postpos.cPart A: pntpos.c一、概述二、整體工作流程三、主要函數說明1. pntpos()2. satposs()3. estpos()4. rescode()5. prange()6. ionocorr()7. tropcorr()8. valsol()9. raim_fde()10. estvel() 四、函數調用關系圖&#xff08;…

【科研繪圖系列】R語言繪制世界地圖(map plot)

禁止商業或二改轉載,僅供自學使用,侵權必究,如需截取部分內容請后臺聯系作者! 文章目錄 介紹加載R包數據下載導入數據數據預處理畫圖輸出圖片系統信息介紹 【科研繪圖系列】R語言繪制世界地圖(map plot) 加載R包 library(ggmap) library(RColorBrewer) library(pals) …

在pycharm profession 2020.3上安裝使用xlwings

之前寫了一篇文章在win7和python3.8上安裝xlwings-CSDN博客 今天安裝了pycharm profession 2020.3&#xff0c;自帶Terminal&#xff0c;所以試一下安裝xlwings。 一、新建一個python項目 二、安裝xlwings 三、輸入安裝命令 pip3.exe install -i https://pypi.tuna.tsinghu…

【PostgreSQL數據分析實戰:從數據清洗到可視化全流程】4.3 數據脫敏與安全(模糊處理/掩碼技術)

&#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 &#x1f449; 點擊關注不迷路 文章大綱 PostgreSQL數據脫敏實戰&#xff1a;從模糊處理到動態掩碼的全流程解析4.3 數據脫敏與安全&#xff1a;模糊處理與掩碼技術深度實踐4.3.1 數據脫敏的核心技術體系4.3.1.1 技…

堅鵬:平安保險集團《保險行業發展趨勢與AI應用方法及案例》培訓

堅鵬&#xff1a;平安保險集團《保險行業發展趨勢與AI應用方法及案例》培訓圓滿成功 中國平安保險&#xff08;集團&#xff09;股份有限公司是全球領先的綜合金融生活服務集團&#xff0c;2024年位列《財富》世界500強第16位&#xff0c;連續多年蟬聯全球保險品牌價值榜首。截…

NetSuite 2025.1 學習筆記

目錄 領域、新功能統計表 值得注意功能摘要 最有價值功能詳解 1. 領域、新功能統計表 2. 值得注意功能 3. 最有價值功能 3.1 Customer 360 目前的Customer 360在加入了幾個新的控件后&#xff0c;變得完整了&#xff0c;相比較過去&#xff0c;真正有了實用感。 3.2 CSV Im…

Messenger.Default.Send 所有重載參數說明

Messenger.Default.Send 是 MVVM 框架中實現消息傳遞的核心方法,其重載參數主要用于控制消息的發送范圍和接收條件。以下是其所有重載形式及參數說明: ?1. 基本消息發送? Send<TMessage>(TMessage message) ?參數說明?: TMessage:消息類型(泛型參數),可以是任…

代碼異味(Code Smell)識別與重構指南

1、引言:什么是“代碼異味”? 在軟件開發中,“代碼異味(Code Smell)”是指那些雖然不會導致程序編譯失敗或運行錯誤,但暗示著潛在設計缺陷或可維護性問題的代碼結構。它們是代碼演進過程中的“信號燈”,提示我們某段代碼可能需要優化。 1.1 ? 為什么關注代碼異味? 預…

K8S有狀態服務部署(MySQL、Redis、ES、RabbitMQ、Nacos、ZipKin、Sentinel)

K8S部署MySQL ①、創建配置 ②、創建存儲卷 ③、創建服務 指定配置文件 指定存儲卷 ④、同樣的方式創建mysql-slaver服務&#xff08;配置文件和mysql-master不同&#xff09; ⑤、進行主從同步關聯 進入master服務中 進入從庫的終端 K8S部署Redis…

正則表達式與文本三劍客grep、sed、awk

目錄 一、正則表達式 1.1、字符匹配 1.2、次數匹配 1.3、位置錨定 1.4、分組或其他 二、擴展正則表達式 三、grep 四、awk 4.1、常用命令選項 4.2、工作原理 4.3、基礎用法 4.4、內置變量 4.5、模式 4.6、條件判斷 4.7、awk中的循環語句 4.8、數組 4.9、腳本 …

Matlab/Simulink的一些功能用法筆記(4)

水一篇帖子 01--MATLAB工作區的保護眼睛顏色設置 默認的工作區顏色為白色 在網上可以搜索一些保護眼睛的RGB顏色參數設置 在MATLAB中按如下設置&#xff1a; ①點擊預設 ②點擊顏色&#xff0c;點擊背景色的三角標符號 ③點擊更多顏色&#xff0c;找到RGB選項 ④填寫顏色參數…

Qt國際化實戰--精通Qt Linguist工具鏈

概述 在全球化的今天,軟件產品需要支持多種語言和地區,以滿足來自世界各地用戶的需求。Qt框架提供了一套完整的工具集來幫助開發者實現應用程序的國際化(i18n)和本地化(l10n),其中最核心的就是Qt Linguist工具鏈 關于國際化與本地化 國際化(i18n): 指的是設計和開發…

0基礎 | STM32 | STM32F103C8T6開發板 | 項目開發

注&#xff1a;本專題系列基于該開發板進行&#xff0c;會分享源代碼 F103C8T6核心板鏈接&#xff1a; https://pan.baidu.com/s/1EJOlrTcProNQQhdTT_ayUQ 提取碼&#xff1a;8c1w 圖 STM32F103C8T6開發板 1、黑色制版工藝、漂亮、高品質 2、入門級配置STM32芯片(SEM32F103…

【SF順豐】順豐開放平臺API對接(注冊、API測試篇)

1.注冊開發者賬號 注冊地址&#xff1a;順豐企業賬戶中心 2.登錄開發平臺 登錄地址&#xff1a;順豐開放平臺 3.開發者對接 點擊開發者對接 4.創建開發對接應用 開發者應用中“新建應用”創建應用&#xff0c;最多創建應用限制數量5個 注意&#xff1a;需要先復制保存生產校驗…

AI Agent開發第48課-DIFY中利用AI動態判斷下一步流程-DIFY調用API、REDIS、LLM

開篇 之前我們在《AI Agent開發第47課-DIFY處理多步流程慢?你確認用對了?》中講述了DIFY的設計中在整合多步LLM時如避免過多調用LLM的良好設計,并給出了AI工作流的相應設計手法。今天我們要在上一篇的基礎上把“上門維修預約”這個流程進一步按照實際業務需求加入用戶在整個…

剝開 MP4 的 千層 “數字洋蔥”:從外到內拆解通用媒體容器的核心

在當今數字化時代&#xff0c;MP4 格式隨處可見&#xff0c;無論是在線視頻、手機拍攝的短片&#xff0c;還是從各種渠道獲取的音頻視頻文件&#xff0c;MP4 都占據著主流地位。它就像一個萬能的 “數字媒體集裝箱”&#xff0c;高效地整合和傳輸著各種視聽內容。接下來&#x…

JavaScript性能優化實戰:深入探討性能瓶頸與優化技巧

JavaScript性能優化實戰:深入探討性能瓶頸與優化技巧 引言 在當今快速發展的Web世界中,性能已經成為衡量應用質量的關鍵指標。隨著Web應用復雜度的不斷提升,JavaScript作為前端開發的核心語言,其性能優化變得尤為重要。本文旨在全面深入地探討JavaScript性能優化的各個方…

無憂AI綜合插件,可實現圖色識別、機器視覺、圖像編輯等多種功能

說明: 無憂AI綜合插件(vu.dll)是一款功能強大的AI腳本插件&#xff0c;主要用于按鍵精靈、易語言、Python、C/C等輔助制作工具&#xff0c;具有圖像識別、文本識別、鍵盤鼠標、內存操作、AI視覺等多種功能。 其官網地址 無憂 - AI圖色綜合插件 http:www.voouer.com/Plugin 功…