TinyWebserver學習(9)-HTTP

一、相關知識

1、有限狀態機:

有限狀態機(Finite State Machine, FSM)是一種用于描述對象在其生命周期內可能經歷的不同狀態及其狀態轉換規則的模型。它廣泛應用于游戲開發、網絡協議、詞法解析、UI邏輯控制等領域。以下是C++中有限狀態機的簡介:
有限狀態機的核心概念

  1. 狀態(State)
    對象可能處于的某種特定情況(如游戲中的角色“空閑”、“移動”、“攻擊”)。
  2. 事件(Event)
    觸發狀態轉換的外部或內部條件(如用戶輸入“跳躍”、定時器超時)。
  3. 轉換(Transition)
    定義在特定事件發生時,從當前狀態轉移到目標狀態的規則(如“空閑 → 移動”)。
  4. 動作(Action)
    狀態轉換時執行的操作(如播放動畫、發送網絡包)。

2、http報文

HTTP報文分為請求報文(瀏覽器端向服務器發送)和響應報文(服務器處理后返回給瀏覽器端)兩種,每種報文必須按照特有格式生成,才能被瀏覽器端識別。
(1)請求報文:由請求行、請求頭部、空行、請求數據四部分組成

  • 請求行:用來說明請求類型(方法)、要訪問的資源以及使用的http的版本
  • 請求頭部:用來說明服務器要使用的附加信息,由“名/值”對組成,每對一行,中間用冒號隔開
  • 空行:請求頭后面的空行是必須的,即使第四行請求數據為空行,第三行也必須是空行
  • 請求數據:也叫主體,可以添加任意類型的數據

以下是通過抓包得到的http請求報文:

GET http://jsuacm.cn/ HTTP/1.1 //Get為請求方法,URL為請求資源,1.1為http版本
Host: jsuacm.cn
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3877.400 QQBrowser/10.8.4506.400
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9//”請求數據”(GET方式的請求一般不包含)

在這里插入圖片描述
常用的頭部信息匯總:

頭部名稱類型作用說明示例值
Host請求頭指定請求的目標主機名和端口號(HTTP/1.1 必須包含)Host: www.example.com
User-Agent請求頭標識客戶端(瀏覽器、操作系統等)信息Mozilla/5.0 (Windows NT 10.0)...
Accept請求頭指定客戶端可接受的響應內容類型(MIME 類型)text/html,application/xhtml+xml
Accept-Language請求頭指定客戶端可接受的語言en-US,en;q=0.9,zh-CN;q=0.8
Accept-Encoding請求頭指定客戶端可接受的編碼方式(如壓縮格式)gzip, deflate
Authorization請求頭提供身份驗證憑證(如 Bearer Token、Basic Auth)Bearer <token>
Referer請求頭標明當前請求的來源頁面 URLhttps://www.google.com/
If-Match請求頭條件請求頭,用于驗證資源 ETag 是否匹配"67ab43"
If-None-Match請求頭條件請求頭,驗證資源 ETag 是否不匹配(用于緩存更新)"67ab43"
If-Modified-Since請求頭條件請求頭,驗證資源是否在指定時間后被修改Wed, 21 Oct 2023 07:28:00 GMT
Cookie請求頭客戶端隨請求發送的 Cookie 數據sessionid=abc123

GET/ POST的區別:
GET最常見的一種請求方式,當客戶端要從服務器中讀取文檔時,當點擊網頁上的鏈接或者通過在瀏覽器的地址欄輸入網址來瀏覽網頁的,使用的都是GET方式。GET方法要求服務器將URL定位的資源放在響應報文的數據部分,回送給客戶端。使用GET方法時,請求參數和對應的值附加在URL后面,利用一個問號(“?”)代表URL的結尾與請求參數的開始,傳遞參數長度受限制。
GET方式的請求一般不包含”請求數據”部分,請求數據以地址的形式表現在請求行。顯然,這種方式不適合傳送私密數據。另外,由于不同的瀏覽器對地址的字符限制也有所不同,一般最多只能識別1024個字符,所以如果需要傳送大量數據的時候,也不適合使用GET方式。
和get一樣很常見,對于上面提到的不適合使用GET方式的情況,可以考慮使用POST方式,因為使用POST方法可以允許客戶端給服務器提供信息較多。POST方法將請求參數封裝在HTTP請求數據中,以名稱/值的形式出現,可以傳輸大量數據,這樣POST方式對傳送的數據大小沒有限制,而且也不會顯示在URL中
(簡單來講,就是GET一般用于我們點擊網頁其他鏈接時使用,POST一般就是我們從網頁上下載資源的時候使用)

(2) 響應報文: 由狀態行+消息報頭+空行+響應正文四個部分組成

  • 狀態行:由HTTP協議版本號,狀態碼,狀態消息 三部分組成;
  • 消息報頭:用來說明客戶端要使用的一些附加信息;
  • 空行
  • 響應正文:主要就是服務端向客戶端發送的數據,比如一個頁面、照片、視頻等

以下是抓取的一段響應報文

HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Wed, 20 Oct 2021 06:46:15 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Length: 737265<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="description" content=""><meta name="author" content=""><link rel="icon" href="../../favicon.ico"><title>吉首大學	</title>

在這里插入圖片描述
HTTP狀態碼與請求方法

HTTP有5種類型的狀態碼,具體的:

  • 1xx:指示信息—表示請求已接收,繼續處理。

  • 2xx:成功—表示請求正常處理完畢。
    200 OK:客戶端請求被正常處理。
    206 Partial content:客戶端進行了范圍請求。

  • 3xx:重定向—要完成請求必須進行更進一步的操作。
    301 Moved Permanently:永久重定向,該資源已被永久移動到新位置,將來對該資源訪問都要使用本響應返回的若干個URI之一。
    302 Found:臨時重定向,請求的資源臨時從不同的URI中獲得。

  • 4xx:客戶端錯誤—請求有語法錯誤,服務器無法處理請求。
    400 Bad Request:請求報文存在語法錯誤。
    403 Forbidden:請求被服務器拒絕。
    404 Not Found:請求不存在,服務器上找不到請求的資源。

  • 5xx:服務器端錯誤—服務器處理請求出錯。
    500 Internal Server Error:服務器在執行請求時出現錯誤。

執行邏輯:

  1. 首先主線程接收到一個客戶端發來的事件,如讀/寫等,并將該事件按照事件類型標識,并加入任務隊列

eventLoop()

			......//處理客戶連接上接收到的數據else if (events[i].events & EPOLLIN){dealwithread(sockfd);}else if (events[i].events & EPOLLOUT){dealwithwrite(sockfd);......}
void WebServer::dealwithread(int sockfd)
{util_timer *timer = users_timer[sockfd].timer;//reactor(反應堆),就是IO多路復用,收到事件后,根據事件類型分配給某個線程if (1 == m_actormodel){if (timer){adjust_timer(timer);}//若監測到讀事件,將該事件放入請求隊列m_pool->append(users + sockfd, 0); //users是一個數組指針,sockfd是索引,因此這個表示的就是當前處理的客戶端的對象//stat:0表示read事件,1表示write事件while (true){if (1 == users[sockfd].improv){if (1 == users[sockfd].timer_flag){deal_timer(timer, sockfd);users[sockfd].timer_flag = 0;}users[sockfd].improv = 0;break;}}}
  1. 如果有事件添加進入任務隊列,則會通知線程池,有空閑線程則會取出任務來進行執行,執行線程執行函數run()(內核是run函數,但是run不是靜態函數,所以不能作為線程執行函數,而是在外層套了一個殼的worker()函數)
void threadpool<T>::run()
{while (true){m_queuestat.wait();//等待信號m_queuelocker.lock();if (m_workqueue.empty()){m_queuelocker.unlock();continue;}T *request = m_workqueue.front();m_workqueue.pop_front();m_queuelocker.unlock();if (!request)continue;if (1 == m_actor_model) //reactor{if (0 == request->m_state){if (request->read_once()){request->improv = 1;connectionRAII mysqlcon(&request->mysql, m_connPool);request->process();}else{request->improv = 1;request->timer_flag = 1;}}else{if (request->write()){request->improv = 1;}else{request->improv = 1;request->timer_flag = 1;}}}else{connectionRAII mysqlcon(&request->mysql, m_connPool);request->process();}}
}
  1. 然后根據事件的類型(request->state),來選擇執行相應的函數,這里我們以讀事件為例。首先,客戶端發來請求響應,服務端需要先將客戶端發來的請求響應的內容保存下來然后再進行解析,保存請求響應的函數即是read_once()函數
    該函數的邏輯也比較簡單,主要就是將套接字發送的內容儲存到m_read_buf這個緩存區中
//循環讀取客戶數據,直到無數據可讀或對方關閉連接
//非阻塞ET工作模式下,需要一次性將數據讀完
bool http_conn::read_once()
{if (m_read_idx >= READ_BUFFER_SIZE){return false;}int bytes_read = 0;//LT讀取數據if (0 == m_TRIGMode){bytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0);m_read_idx += bytes_read;if (bytes_read <= 0){return false;}return true;}//ET讀數據else{while (true){bytes_read = recv(m_sockfd, m_read_buf + m_read_idx, READ_BUFFER_SIZE - m_read_idx, 0);if (bytes_read == -1){if (errno == EAGAIN || errno == EWOULDBLOCK)break;return false;}else if (bytes_read == 0){return false;}m_read_idx += bytes_read;}return true;}
}
  1. 我們得到緩沖區中的請求響應后,就需要對其進行解析,解析函數為process()函數,這個函數會先使用process_read()函數對請求響應進行解析,然后使用process_write()輸出回應報文
void http_conn::process()
{HTTP_CODE read_ret = process_read(); // 請求報文處理,限定結果在枚舉范圍之內if (read_ret == NO_REQUEST) //如果請求不完整,需要繼續接收請求數據{modfd(m_epollfd, m_sockfd, EPOLLIN, m_TRIGMode);return;}bool write_ret = process_write(read_ret);//相應報文處理if (!write_ret){close_conn();}modfd(m_epollfd, m_sockfd, EPOLLOUT, m_TRIGMode);
}
  1. process_read()函數是請求報文解析函數,是http的核心函數之一,我們可以結合下面的圖來看
/* 該函數為請求報文處理函數,通過while循環來實現對主從狀態機的封裝,其中主狀態機為process_read()函數,從狀態機為parse_line()函數*/
http_conn::HTTP_CODE http_conn::process_read() 
{LINE_STATUS line_status = LINE_OK;HTTP_CODE ret = NO_REQUEST;char *text = 0;//m_checked_state 主狀態機狀態為CHECK_STATE_REQUESTLINE時,該條件涉及解析消息體//line_status 從狀態機狀態轉移為LINE_OK時,該條件涉及解析請求行while ((m_check_state == CHECK_STATE_CONTENT && line_status == LINE_OK) || ((line_status = parse_line()) == LINE_OK)){text = get_line(); //得到改行的具體內容m_start_line = m_checked_idx;LOG_INFO("%s", text);switch (m_check_state){case CHECK_STATE_REQUESTLINE: //請求行,初始化的時候定義了{ret = parse_request_line(text);if (ret == BAD_REQUEST)return BAD_REQUEST;break;}case CHECK_STATE_HEADER: //頭信息{ret = parse_headers(text);if (ret == BAD_REQUEST)return BAD_REQUEST;else if (ret == GET_REQUEST){return do_request();}break;}case CHECK_STATE_CONTENT: //消息體{ret = parse_content(text);if (ret == GET_REQUEST)return do_request();line_status = LINE_OPEN;break;}default:return INTERNAL_ERROR;}}return NO_REQUEST;
}

在這里插入圖片描述
6. 其中,主狀態機則為process_read()函數,從狀態機為parse_line()函數,parse_line主要作用就是從讀緩沖區中讀取一行內容,并將每一行結尾的\r\n改為\0\0。

//從狀態機,用于分析出一行內容
//返回值為行的讀取狀態,有LINE_OK,LINE_BAD,LINE_OPEN
http_conn::LINE_STATUS http_conn::parse_line()
{char temp;for (; m_checked_idx < m_read_idx; ++m_checked_idx){temp = m_read_buf[m_checked_idx];if (temp == '\r'){if ((m_checked_idx + 1) == m_read_idx)return LINE_OPEN;else if (m_read_buf[m_checked_idx + 1] == '\n'){m_read_buf[m_checked_idx++] = '\0';m_read_buf[m_checked_idx++] = '\0';return LINE_OK;}return LINE_BAD;}else if (temp == '\n'){if (m_checked_idx > 1 && m_read_buf[m_checked_idx - 1] == '\r'){m_read_buf[m_checked_idx - 1] = '\0';m_read_buf[m_checked_idx++] = '\0';return LINE_OK;}return LINE_BAD;}}return LINE_OPEN;
}
  1. 如果返回LINE_OK,表示改行已經讀完,并且該行的地址m_check_idx也已經更新,接著通過get_line()這個函數就返回改行具體的內容了。
    然后進入switch循環,首先my_check_state的初始狀態為CHECK_STATE_REQUESTLINE,即解析請求行,然后執行parse_request_line函數來解析,函數如下:
    主要作用就是解析出m_method、m_url這兩個屬性,然后將my_check_state的狀態修改為CHECK_STATE_HEADER。
//解析http請求行,獲得請求方法,目標url及http版本號
http_conn::HTTP_CODE http_conn::parse_request_line(char *text)
{m_url = strpbrk(text, " \t");//用于查找第一個出現指定字符串的位置,如果找到,則返回指向該字符的指針,否則返回NULLif (!m_url){return BAD_REQUEST;}*m_url++ = '\0';char *method = text;if (strcasecmp(method, "GET") == 0)m_method = GET;else if (strcasecmp(method, "POST") == 0){m_method = POST;cgi = 1; //是否啟用POST}elsereturn BAD_REQUEST;m_url += strspn(m_url, " \t"); //從m_url開始,跳過空白字符,返回第一個非空白字符的指針,該作用是確保指針指向有效的字符m_version = strpbrk(m_url, " \t");if (!m_version)return BAD_REQUEST;*m_version++ = '\0';m_version += strspn(m_version, " \t");if (strcasecmp(m_version, "HTTP/1.1") != 0) //檢查版本是否為1.1return BAD_REQUEST;if (strncasecmp(m_url, "http://", 7) == 0) //如果前綴包括http://,則去掉{m_url += 7;m_url = strchr(m_url, '/');}if (strncasecmp(m_url, "https://", 8) == 0) //同上{m_url += 8;m_url = strchr(m_url, '/');}if (!m_url || m_url[0] != '/')return BAD_REQUEST;//當url為/時,顯示判斷界面if (strlen(m_url) == 1)strcat(m_url, "judge.html");m_check_state = CHECK_STATE_HEADER;return NO_REQUEST;
}
  1. 接下來是解析頭部信息HEADER,函數如下:
    其邏輯為如果檢測到該行頭部為“Connection:”、“keep-alive”等,則賦予相應的屬性值,但是m_check_state不變;
    如果檢測到“Content-length”,這代表接下來是客戶端發送過來的內容了,這樣的話就轉變m_check_state的屬性為CHECK_STATE_CONTENT,代表下一次循環就要解析內容了
//解析http請求的一個頭部信息
http_conn::HTTP_CODE http_conn::parse_headers(char *text)
{if (text[0] == '\0'){if (m_content_length != 0){m_check_state = CHECK_STATE_CONTENT;return NO_REQUEST;}return GET_REQUEST;}else if (strncasecmp(text, "Connection:", 11) == 0){text += 11;text += strspn(text, " \t");if (strcasecmp(text, "keep-alive") == 0){m_linger = true;}}else if (strncasecmp(text, "Content-length:", 15) == 0){text += 15;text += strspn(text, " \t");m_content_length = atol(text);}else if (strncasecmp(text, "Host:", 5) == 0){text += 5;text += strspn(text, " \t");m_host = text;}else{LOG_INFO("oop!unknow header: %s", text);}return NO_REQUEST;
}
  1. 以下是解析內容體的函數,因為在該項目中,客戶端主要傳輸的對象很簡單,就只有輸入的用戶名和密碼。
    如果,你希望在這個項目的基礎上繼續改進,一個主要的改進方向就是這個,你可以上傳文件、圖片等等。
    那么既然客戶端有上次內容,那么服務器肯定需要對內容做一個回應或者處理,那么就引出了接下來的do_request() 函數
//判斷http請求是否被完整讀入
http_conn::HTTP_CODE http_conn::parse_content(char *text)
{if (m_read_idx >= (m_content_length + m_checked_idx)){text[m_content_length] = '\0';//POST請求中最后為輸入的用戶名和密碼m_string = text;return GET_REQUEST;}return NO_REQUEST;
}
  1. do_request() 函數因為比較長,所以在這里就不直接粘貼出來了,大家對照著源碼來學習吧。

  2. 有請求報文就會有響應報文,響應報文主要根據請求報文返回的狀態來定義,在process()函數中,響應報文函數為process_write()函數
    該函數的作用主要返回幾種狀態碼,如404、200等,這些狀態碼的含義之前也已經敘述了,大家可以翻到上面去看。

bool http_conn::process_write(HTTP_CODE ret)
{switch (ret){case INTERNAL_ERROR:{add_status_line(500, error_500_title);add_headers(strlen(error_500_form));if (!add_content(error_500_form))return false;break;}case BAD_REQUEST:{add_status_line(404, error_404_title);add_headers(strlen(error_404_form));if (!add_content(error_404_form))return false;break;}case FORBIDDEN_REQUEST:{add_status_line(403, error_403_title);add_headers(strlen(error_403_form));if (!add_content(error_403_form))return false;break;}case FILE_REQUEST:{add_status_line(200, ok_200_title);if (m_file_stat.st_size != 0){add_headers(m_file_stat.st_size);m_iv[0].iov_base = m_write_buf;m_iv[0].iov_len = m_write_idx;m_iv[1].iov_base = m_file_address;m_iv[1].iov_len = m_file_stat.st_size;m_iv_count = 2;bytes_to_send = m_write_idx + m_file_stat.st_size;return true;}else{const char *ok_string = "<html><body></body></html>";add_headers(strlen(ok_string));if (!add_content(ok_string))return false;}}default:return false;}m_iv[0].iov_base = m_write_buf;m_iv[0].iov_len = m_write_idx;m_iv_count = 1;bytes_to_send = m_write_idx;return true;
}

在這里插入圖片描述

這些基本上就是http的整個運行的邏輯框架了,大家有什么不懂的,評論區見~

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

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

相關文章

2025最新軟件測試面試八股文

&#x1f345; 點擊文末小卡片&#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快 Part1 1、你的測試職業發展是什么&#xff1f; 測試經驗越多&#xff0c;測試能力越高。所以我的職業發展是需要時間積累的&#xff0c;一步步向著高級測試工程師…

React 強大的表單驗證庫formik之集成Yup、React Hook Form庫

簡介 Formik 是為 React 開發的開源表單庫&#xff0c;提供狀態管理、驗證和提交處理功能&#xff0c;可簡化復雜表單的開發。 核心優勢 ?- 狀態管理 ?&#xff1a;自動跟蹤輸入值、驗證狀態和提交進度&#xff0c;無需手動編寫狀態邏輯。 ? ?- 驗證功能 ?&#xff1a;…

破解風電運維“百模大戰”困局,機械版ChatGPT誕生?

面對風機87%的非計劃停機&#xff0c;30多個專用模型為何束手無策&#xff1f;一套通用大模型如何實現軸承、齒輪、轉子“一站式”健康管理&#xff1f;一、行業痛點&#xff1a;風機運維深陷“碎片化泥潭”1.1 87%停機故障由多部件引發齒輪斷裂、軸承磨損、電機短路……風電故…

Spring Bean 控制銷毀順序的方法總結

控制Spring Bean銷毀順序的方法 在Spring框架中&#xff0c;有幾種方法可以控制Bean的銷毀順序。以下是主要的幾種方式及其示例代碼&#xff1a; 1. 實現DisposableBean接口 實現DisposableBean接口并重寫destroy()方法&#xff0c;Spring會按照依賴關系的相反順序調用這些方…

Gemini CLI MCP 教程:設置、命令與實際使用

如果你想要為任何 MCP 服務器設置 Gemini CLI,這個快速教程將為你提供指導。 Gemini CLI 結合 MCP(模型上下文協議)服務器,改變了你通過自然語言命令進行編碼的方式。 你可以要求 Gemini 分析你的代碼庫、管理文件、查詢數據庫,或通過對話提示與 API 交互。 MCP 服務器連…

LangChain 構建向量數據庫和檢索器

LangChain 構建向量數據庫和檢索器實戰 隨著大語言模型&#xff08;LLM&#xff09;在各類 AI 應用中不斷普及&#xff0c;RAG&#xff08;Retrieval-Augmented Generation&#xff0c;檢索增強生成&#xff09;逐漸成為提升回答準確率與上下文關聯性的重要技術路徑。 一、什…

基于 LangChain 實現通義千問 + Tavily 搜索 Agent 的簡單實踐

文章目錄 一、相關背景1.1 LangChain 簡介1.2 通義千問&#xff08;Tongyi Qianwen&#xff09;1.3 Tavily 實時搜索引擎1.4 架構總覽 二、環境配置設置 API 密鑰 三、 Tavily 搜索四、智能 Agent 示例&#xff1a;自動判斷是否調用 Tavily4.1 初始化 Agent4.2 提問兩個問題&am…

SegGPT: 在上下文中分割一切

摘要 我們提出了 SegGPT&#xff0c;一個在上下文中執行“分割一切”的通用模型。我們將各種分割任務統一為一個通用的上下文學習框架&#xff0c;通過將不同類型的分割數據轉換為相同的圖像格式來兼容這些差異。SegGPT 的訓練被建模為一個帶有隨機顏色映射的上下文著色問題。…

【網絡】Linux 內核優化實戰 - net.core.busy_read

目錄 核心功能工作原理與優勢配置方式1. 臨時配置&#xff08;重啟失效&#xff09;2. 永久配置&#xff08;重啟生效&#xff09; 與 net.core.busy_poll 的協同作用適用場景與注意事項適用場景&#xff1a;注意事項&#xff1a; 總結 net.core.busy_read 是 Linux 內核中與網…

alpine安裝及配置nodejs開發測試環境

在Alpine Linux上安裝和使用Node.js&#xff0c;打造開發和測試的環境。 apk倉庫打開社區的源。 先在命令行中使用命令apk search nodejs npm yarn對倉庫源進行搜索&#xff0c;&#xff0c;看看nodejs、yarn、npm的版本情況。 localhost:~# apk search nodejs npm yarn nod…

Apache Commons Pool中的GenericObjectPool詳解

GenericObjectPool 是 Apache Commons Pool 庫中的核心類&#xff0c;用于實現對象的池化管理&#xff0c;適用于數據庫連接、HTTP 客戶端、線程等昂貴資源的復用。以下從核心概念、工作原理、參數配置、使用場景及最佳實踐等方面詳細解析&#xff1a; ?? 一、核心概念與組成…

攻防世界CTF題目解析系列————(1)

題目來源:攻防世界wife_wife 打開題目之后&#xff0c;發現登錄界面&#xff0c;然后嘗試弱口令&#xff0c;sql二次注入&#xff0c;xss發現都沒有&#xff0c;然后看見下面go register&#xff08;去注冊&#xff09;按鈕 成功注冊&#xff08;username和password隨便搞&…

楚存科技SD NAND貼片式T卡—高性能存儲解決方案、賦能AI智能硬件

楚存科技SD NAND貼片式T卡—高性能存儲解決方案、賦能AI智能硬件應用 在 AIoT 技術重構產業生態的時代浪潮中&#xff0c;智能硬件正從單一功能終端向數據樞紐演進 —— 智能家居設備日均產生 TB 級交互數據&#xff0c;工業物聯網傳感器需實時存儲生產參數&#xff0c;車載智…

Python[數據結構及算法 --- 查找]

一.順序查找&#xff08;無序表&#xff09;&#xff1a; def sequentialSearch(alist, item):pos 0found Falsewhile pos < len(alist) and not found:if alist[pos] item:found Trueelse:pos pos 1return foundtestlist [1, 2, 32, 8, 17, 19, 42, 13, 0] print(s…

Seata Saga模式實戰:Java微服務中的分布式事務管理

在分布式系統中&#xff0c;Saga模式是一種用于管理跨多個服務的事務的柔性事務解決方案。它通過將長事務拆分為多個本地事務&#xff08;每個事務對應一個服務的操作&#xff09;&#xff0c;并通過補償機制保證最終一致性。以下是Java中Saga模式的詳細介紹&#xff0c;包括實…

若依學習筆記1-validated

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 保證 Spring AOP 相關的依賴包 --><dependency><groupId>org.springframework.boot<…

Excel 如何處理更復雜的嵌套邏輯判斷?

處理復雜的嵌套邏輯判斷&#xff0c;是Excel進階路上必然會遇到的一道坎。當簡單的IF函數“套娃”變得冗長、難以閱讀和維護時&#xff0c;我們就需要更高級、更清晰的工具。 這里介紹三種從基礎到高級的處理方法&#xff1a; 傳統的 IF 函數嵌套 (經典&#xff0c;但容易混亂)…

使用Claude和MCP增強Selenium

1.配置MCP服務器打開Claude Desktop—>Settings—>Developer—>Edit Config{"mcpServers": {"selenium": {"command": "npx","args": ["-y", "angiejones/mcp-selenium"]}} }配置完成后重啟Cl…

數據倉庫錨點建模方法的前世今生

數據倉庫錨點建模方法&#xff08;Anchor Modeling&#xff09;作為一種面向復雜數據環境的創新方法論&#xff0c;其發展歷程與技術演進深刻反映了數據管理從結構化到動態化的轉型需求。以下從起源、發展、核心思想、技術演進及未來趨勢五個維度&#xff0c;系統梳理錨點建模的…

<三>Sping-AI alibaba 文生圖

環境和配置請看&#xff1c;二&#xff1e;Sping-AI alibaba 入門-記憶聊天及持久化 源代碼&#xff1a;https://github.com/springaialibaba/spring-ai-alibaba-examples/blob/main/spring-ai-alibaba-image-example/dashscope-image/src/main/java/com/alibaba/cloud/ai/exam…