【6】深入學習http模塊(萬字)-Nodejs開發入門

深入學習http模塊

  • 前言
  • http
    • 一個Web服務器
      • 項目創建
      • 代碼運行
      • 代碼解析
    • Server
      • 屬性:keepAlive
      • 屬性:keepAliveTimeout
      • 屬性:maxHeaderSize
      • 屬性:requestTimeout
      • 屬性:maxRequestsPerSocket
      • 方法:close()
      • 方法:closeAllConnections()
      • 方法:setTimeout()
      • 方法:listen()
      • 事件:connection
      • 事件:dropRequest
      • 事件:request
    • 案例
    • ClientRequest
      • 創建ClientRequest對象
      • 實例方法
      • 事件監聽
    • ServerResponse
      • 屬性
      • 方法
    • IncomingMessage
    • 其他屬性
      • http.METHODS
      • http.STATUS_CODES
  • 其他

前言

本章詳細介紹了http模塊的各部分概念和用途,包括創建一個簡單的 Web 服務器、配置服務器屬性、處理客戶端請求以及響應客戶端。

在學習本章之前,你最好具有一些前置的基礎知識,如:計算機網絡知識、Ajax或者Fetch請求、跨域相關知識等。

http

一個Web服務器

項目創建

const http = require('node:http');const hostname = '127.0.0.1';
const port = 3000;const server = http.createServer((req, res) => {res.statusCode = 200;res.setHeader('Content-Type', 'text/plain');res.end('Hello, World!\n');
});server.listen(port, hostname, () => {console.log(`Server running at http://${hostname}:${port}/`);
});

這是官方給出的一個案例,我們創建一個目錄為:node-server,并創建index.js文件,將上面內容寫入。

通過npm init創建一個package.json配置文件,根據提示填入相應的配置信息。

package.json中找到scripts配置,寫入:"start": "node ./index.js"

代碼運行

我們可以通過node index.js的方式運行上面的文件,但是為了方便操作,我們寫入了npm命令腳本,腳本的內容就是node index.js,這樣我們只需要運行start命令即可:

npm run start

運行之后,命令行會輸出:
在這里插入圖片描述
我們的Web服務就啟動成功了,在瀏覽器打開 http://127.0.0.1:3000/,就可以看到Hello, World!

代碼解析

我們來簡單解析一下上面的代碼,如果你有一定的Nodejs基礎,這段代碼是可以看得懂的:

  1. 導入http模塊;
  2. 定義主機名和端口號;
  3. 創建Web服務,并在回調中設置響應狀態、響應頭和響應體;
  4. 監聽設定的端口號,并在服務啟動成功之后給出提示信息。

代碼的核心在于http.createServerserver.listen它們分別用于創建服務和監聽端口號。

我們對Web服務器的要求也很簡單,能夠接收客戶端請求,并做出響應即可,這段代碼正好符合我們的要求。

不過這段代碼有點問題,比如,

  • 所有發送到localhost:3000的請求,無論路徑和參數是什么,它總會返回Hello,World!
  • 如果發生異常,如大并發、網絡緩慢、服務端出錯等,它無法做出“人性化”反饋。

第一點是我們必須要考慮的問題,我們希望不同的請求路徑響應不同的結果;

第二點對于普通項目來說需要考慮的場景并不是很多,但是我們也希望能夠做出適當配置,讓項目達到一個較好的運行狀態。

Server

Server類用于創建一個http實例。

官方給出了http.server相關的方法和屬性以及“事件”,我們只需要學會常用的配置即可。

你可以將配置寫在一個options對象中,并作為http.createServer的第一個參數傳入,也可以將配置作為http實例的屬性直接修改。

一般我們用第一種方式為服務設置通用配置信息,而第二種方式用于定制化項目配置,根據自己需要選擇。

const server = http.createServer({ ...options }, () => {});

屬性:keepAlive

我們知道,http的無狀態的,服務器無法知曉客戶端狀態,客戶端每發送一次請求,客戶端都會建立三次握手(SYN, SYN-ACK, ACK),這會消耗時間和資源。

假設你的官網加載了很多資源(圖片、文本、視頻…),服務器的握手會很占用時間,有沒有一種可能,我們讓服務器在某個設定的時間段內在客戶端和服務器之間保持持久連接,從而允許在同一個 TCP 連接上發送多個 HTTP 請求和接收響應,而不是為每個請求都打開和關閉一個新的連接。

這種做法既可以保證連接的安全性,也極大提升了“高并發”下的加載速度。

HTTP/1.1中,keep-alive是默認行為,不需要顯式設置,你可以在一些請求中看到相關的信息:
在這里插入圖片描述
單個請求的KeepAlive并沒有什么意義,我們會在學習完其他屬性之后進行測試。

屬性:keepAliveTimeout

為了“高并發”下的請求效率,瀏覽器默認啟用了KeepAlive,我們不考慮其他非瀏覽器情況。

這里又會產生一個問題,開啟KeepAlive之后,服務器需要分配一部分內存來管理連接,如果瞬時用戶量很大,可能會造成服務器崩潰。

為了解決這個問題,我們約定,給KeepAlive一個時長,在這個時長范圍內保持鏈接,超過時長則斷開,這個時長默認為5000ms,當然你也可以通過設置keepAliveTimeout來自定義時長,它的單位為毫秒,當客戶端發送請求在這個范圍內時,會一直使用同一個連接。

連接會自動延長,當下一次請求觸發之后,會自動順延時長。

屬性:maxHeaderSize

請求頭的最大長度,沒什么特殊的地方,默認長度為16384,也就是16k。

屬性:requestTimeout

請求超時時長,默認是300000ms,也就是300秒,可以應付大部分需求,如果你需要上傳大問題,可以在指定的請求內修改并覆蓋。

屬性:maxRequestsPerSocket

每次連接的最大請求數量,默認為0表示不限制,這個屬性需要放在server實例上配置:

server.maxRequestsPerSocket = 3;

方法:close()

關閉服務器本身,使其停止監聽新的連接請求。

server.close(() => {console.log('server on port 8000 closed successfully');
});

方法:closeAllConnections()

關閉所有連接到此服務器的連接,包括那些正在處理請求或等待響應的連接,以及空閑連接。

我們一般先調用server.close再調用server.closeAllConnections(),這樣既能夠保證清晰的邏輯順序也能確保服務被正確關閉。

server.closeAllConnections();

方法:setTimeout()

除了在創建服務時配置的timeout之外,還可以使用server.setTimeout方法進行設置。

// 設置連接超時時間為 5000 毫秒(5 秒)
server.setTimeout(5000, () => {console.log('A connection was closed due to inactivity.');
});

方法:listen()

開始監聽特定端口或路徑,你可以監聽多個端口來實現不同的項目。

server.listen(3344, () => {console.log(`Server running at http://localhost:${3344}/`);
});server.listen(1122, () => {console.log(`Server running at http://localhost:${1122}/`);
});

事件:connection

當有新的 TCP 連接時,‘connection’ 事件被觸發,我們可以用connection時間來查看相關信息。

server.on('connection', (socket) => {console.log('Connection connected!', socket.localAddress, socket.localPort, socket.remoteAddress, socket.remotePort);
});

事件:dropRequest

當連接的請求超過maxRequestsPerSocket的閾值時,連接會刪除新的請求,并觸發dropRequest,不過在瀏覽器端并不會發生該事件,而是會將請求分成多批次進行響應。

事件:request

每當有一個請求都會觸發該事件,一個連接中的多個請求都會觸發一次。

server.on('request', (request) => {console.log('Received request', request.url);
});

案例

我們來寫一個案例測試上面的配置:

const http = require('node:http');const hostname = '127.0.0.1';
const port = 3000;const server = http.createServer({keepAlive: true,keepAliveTimeout: 1000
},(req, res) => {res.setHeader('Access-Control-Allow-Origin', '*');res.setHeader('Access-Control-Allow-Methods', '*');res.setHeader('access-control-allow-headers', '*');res.statusCode = 200;res.setHeader('Content-Type', 'text/plain');res.end('Hello, World!\n');
});
server.maxRequestsPerSocket = 3;
server.on('connection', (socket) => {console.log('Connection connected!', socket.localAddress, socket.localPort, socket.remoteAddress, socket.remotePort);
});
server.on('request', (request) => {console.log('Received request', request.url);
});
server.listen(port, hostname, () => {console.log(`Server running at http://${hostname}:${port}/`);
});

我們啟用了KeepAlive,并設置1000毫秒的持續時長,同時設置maxRequestsPerSocket為3表示一個連接最多傳遞3個請求。

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title>
</head>
<body><button onclick="button()">點擊</button><script>function button() {Promise.all([request(),request(),request(),request(),request()])}function request(url = 'http://127.0.0.1:3000'){fetch(url, {method: 'get',keepalive: true,})}</script>
</body>
</html>

我們在html中同時發送五個請求,我們來嘗試運行,注意由于index.html并沒有通過Nodejs的服務訪問,因此存在跨域,我們在代碼中做了簡單處理。

當我們點擊按鈕,瀏覽器會發出五次請求,由于限定了maxRequestsPerSocket的大小,connection事件會被觸發兩次。
在這里插入圖片描述
我們修改maxRequestsPerSocket為0,然后快速點擊按鈕:

server.maxRequestsPerSocket = 0;

設置為0表示不限制單個連接的請求數量,由于點擊速度在1秒內,因此除了第一次,后面每次點擊無需再次建立連接:
在這里插入圖片描述
在你最后一次點擊之后,停留1秒之后再次點擊,則會再次觸發connection事件,我們設置的超時時間為1秒,超過一秒原來的連接會中斷并建立新鏈接。

ClientRequest

ClientRequest是一個非常重要的類,用于表示一個正在進行中的 HTTP 客戶端請求。

通俗一點就是,Nodejs向其它服務器發出請求時所創建的請求對象。

我們學習它是為了更好地理解后面的請求對象。

創建ClientRequest對象

我們通常使用http.request()方法創建ClientRequest對象,下面的是一個案例告訴你如何創建:

const http = require('http');const options = {hostname: 'www.example.com',port: 80,path: '/',method: 'GET'
};const req = http.request(options, (res) => {console.log(`STATUS: ${res.statusCode}`);console.log(`HEADERS: ${JSON.stringify(res.headers)}`);res.on('data', (chunk) => {console.log(`BODY: ${chunk}`);});res.on('end', () => {console.log('No more data in response.');});
});req.on('error', (e) => {console.error(`problem with request: ${e.message}`);
});req.end();

我們通過http.requesthttp://www.example.com發出了一個get請求,并監聽其回調。

回調函數接受一個參數,這個參數稱為響應對象,類型為“IncomingMessage”,在后面也會再次遇到。

實例方法

ClientRequest提供了多種方法來配置和發送請求,我們可以用這些方法來定制化請求。

  • setHeader(name, value):設置請求頭的單個字段。
req.setHeader('Content-Type', 'application/json');
  • getHeader(name):獲取請求頭的單個字段的值。
const contentType = req.getHeader('Content-Type');
  • removeHeader(name):移除請求頭的某個字段。
req.removeHeader('Content-Type');
  • write(chunk[, encoding]):將數據寫入請求體,get請求無效。
req.write('{"name": "John Doe"}');
  • end([data][, encoding][, callback]):結束請求并發送數據,如果你用過write則無需再添加參數。
req.end('{"email": "john.doe@example.com"}');
  • abort():終止請求。
req.abort(); // 立即終止請求
  • setTimeout(timeout[, callback]):設置請求的超時時間。
req.setTimeout(5000, () => {console.log('Request timed out');req.abort();
});

事件監聽

我們可以通過監聽事件在不同階段做出一些操作。

  • ‘response’:當接收到響應頭時觸發,傳遞一個 http.IncomingMessage 對象作為參數。
req.on('response', (res) => {console.log(`STATUS: ${res.statusCode}`);
});
  • ‘socket’:當為請求分配了一個 net.Socket 時觸發。
req.on('socket', (socket) => {console.log('Socket assigned.');
});
  • ‘error’:當請求過程中發生錯誤時觸發。
req.on('error', (e) => {console.error(`problem with request: ${e.message}`);
});
  • ‘abort’:當請求被終止時觸發。
req.on('abort', () => {console.log('Request aborted.');
});
  • ‘timeout’:當請求超時時觸發。
req.on('timeout', () => {console.log('Request timed out.');req.abort();
});

在實際開發中,我們一般會用第三方工具輔助開發,但是最基本的原理也要學會,這樣才能應付出現的問題。

ServerResponse

ServerResponse用于表示 HTTP 服務器的響應對象。每當 HTTP 服務器接收到請求時,都會創建一個http.ServerResponse對象,因此我們無需手動創建該對象。

你可能會有疑問

在上面的案例中:

const req = http.request(options, (res) => {console.log(`STATUS: ${res.statusCode}`);console.log(`HEADERS: ${JSON.stringify(res.headers)}`);res.on('data', (chunk) => {console.log(`BODY: ${chunk}`);});res.on('end', () => {console.log('No more data in response.');});
});

通過http.request創建請求的第二個參數表示回調,我們上面說了,這個回調函數的參數類型是IncomingMessage,按照我們正常的邏輯來說,它應該是響應對象,那為什么會是IncomingMessage呢?

如果你問出了這樣的疑問,說明有思考,我們需要區分一下場景。

上面說到了Server,這是個服務器,服務器接受請求,發送響應;
上面說到了ClientRequest,這是個客戶端工具,用于請求服務器;
現在說的ServerResponse,這是個中介,它在服務器叫ServerResponse,當它被發送到客戶端之后稱為IncomingMessage,因此很容易被迷惑。

如果再網上,在Server的案例中,有這樣一段代碼:

server.on('request', (request) => {console.log('Received request', request.url);
});

注意,這里的request可不是ClientRequest,它只是被我們碰巧命名為request,為了區分,可以將它改為req

我們思考一下,IncomingMessage叫“即將到來的消息”,對于服務器來說,發送過來的請求是“即將到來的消息”,對于客戶端來說發送過來的響應是“即將到來的消息”。我們要理清一下關系:

  1. 客戶端通過ClientRequest創建一個請求;
  2. 請求發送到服務器,這個請求對象被包裝變成Serverrequest事件的回調函數的參數,稱為IncomingMessage,它用于獲取請求信息;
  3. Serverrequest事件的回調函數還接受第二個參數,作為響應對象,也就是上面所說的ServerResponse,我們給ServerResponse進行配置之后,返回給客戶端;
  4. 客戶端通過ClientRequest的回調響應請求,拿到服務端發送回來的ServerResponse,此時它也被稱為IncomingMessage

記住這個關系,后面還會遇到。

屬性

作為一個響應對象,它會有下列常用屬性:

  • statusCode,狀態碼;
  • statusMessage:狀態消息
  • header:響應頭(使用方法代替)
  • 響應體
response.statusCode = 200;
response.setHeader('Content-Type', 'text/plain');
response.end('Goodbye!');

方法

  • setHeader(name, value),設置響應頭的單個字段。
  • getHeader(name),獲取響應頭的單個字段的值。
  • removeHeader(name),移除響應頭的某個字段。
  • write(chunk[, encoding][, callback]),向響應體寫入數據塊。
  • writeHead(statusCode[, statusMessage][, headers]),同時設置響應狀態碼、狀態消息和響應頭,然后準備發送響應體。
  • end([data][, encoding][, callback]),結束響應過程,并可選地發送數據塊。

IncomingMessage

IncomingMessage用于表示 HTTP 請求(在服務器端)或 HTTP 響應(在客戶端)。它提供了訪問請求/響應頭和讀取請求/響應體的接口。

  • 在服務器端:
    • http.Server對象在接收到請求時自動創建,并通過回調函數的第一個參數傳遞給開發者。
  • 在客戶端:
    • http.ClientRequest對象在接收到響應時自動創建,并通過response事件傳遞給開發者。

這個對象的相關屬性和方法其實在上面基本上都講過了,我們需要做的就是理清楚它在客戶端與服務器之間具體的用途。

其他屬性

http.METHODS

一個字符串數組,列出來所有能被解析的請求方法。

http.STATUS_CODES

包含了所有標準 HTTP 狀態碼及其對應的描述性文本。

console.log(http.STATUS_CODES[200]); // 輸出: 'OK'
console.log(http.STATUS_CODES[404]); // 輸出: 'Not Found'
console.log(http.STATUS_CODES[500]); // 輸出: 'Internal Server Error'

其他

通過上面的內容學習,我們能夠實現一個簡單的http服務器,它能夠接收請求并做出響應,同時能夠根據請求的url和method做出簡單的路由,但是我們還有一些部分沒有提到,如參數的解析,nodejs本身沒有直接從body或者查詢字符串提取數據的能力,需要我們自己進行提取,因此我們要在學完BufferStream之后再來解析參數。

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

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

相關文章

buuctf sql注入類練習

BUU SQL COURSE 1 1 實例無法訪問 / Instance cant be reached at that time | BUUCTF但是這個地方很迷惑就是這個 一個 # 我們不抓包就不知道這個是sql注入類的判斷是 get 類型的sql注入直接使用sqlmap我們放入到1.txt中 目的是 優先檢測 ?id1>python3 sqlmap.py -r 1.t…

(即插即用模塊-特征處理部分) 三十二、(TGRS 2024) MDAF 多尺度雙表示對齊過濾器

文章目錄 1、Multiscale Dual-Representation Alignment Filter2、代碼實現 paper&#xff1a;SFFNet: A Wavelet-Based Spatial and Frequency Domain Fusion Network for Remote Sensing Segmentation Code&#xff1a;https://github.com/yysdck/SFFNet 1、Multiscale Dual-…

Python 中為什么 hash(-1) == hash(-2)?

推薦超級課程: 本地離線DeepSeek AI方案部署實戰教程【完全版】Docker快速入門到精通Kubernetes入門到大師通關課AWS云服務快速入門實戰目錄 讓我們從哪里開始?獲取源代碼!讓我們瀏覽一下這是正確/完整的答案嗎?結論前幾天在瀏覽 Reddit 時,我在 r/Python 上看到了這樣一個…

基于PySide6與pycatia的CATIA繪圖比例智能調節工具開發全解析

引言&#xff1a;工程圖紙自動化處理的技術革新 在機械設計領域&#xff0c;CATIA圖紙的比例調整是高頻且重復性極強的操作。傳統手動調整方式效率低下且易出錯。本文基于PySide6pycatia技術棧&#xff0c;提出一種支持智能比例匹配、實時視圖控制、異常自處理的圖紙批處理方案…

macos下 ragflow二次開發環境搭建

參考官網鏈接 https://ragflow.io/docs/dev/launch_ragflow_from_source虛擬環境 git clone https://github.com/infiniflow/ragflow.git cd ragflow/ # if not pipx, please install it at first pip3 install pipxpipx install uv uv sync --python 3.10 --all-extras 安裝 …

libva之ffavdemo分析

ffavdemo 代碼庫實現了一個基于FFmpeg和VAAPI的硬件加速視頻解碼與渲染框架&#xff0c;主要用于演示視頻解碼與渲染的完整硬件加速流程。支持多種渲染后端&#xff08;X11、DRM、EGL&#xff09;&#xff0c;適應不同顯示環境。包含視頻處理過濾器&#xff0c;可進行格式轉換和…

JavaWeb 課堂筆記 —— 09 MySQL 概述 + DDL

本系列為筆者學習JavaWeb的課堂筆記&#xff0c;視頻資源為B站黑馬程序員出品的《黑馬程序員JavaWeb開發教程&#xff0c;實現javaweb企業開發全流程&#xff08;涵蓋SpringMyBatisSpringMVCSpringBoot等&#xff09;》&#xff0c;章節分布參考視頻教程&#xff0c;為同樣學習…

精品推薦 | 湖倉一體電商數據分析平臺實踐教程合集(視頻教程+設計文檔+完整項目代碼)

精品推薦&#xff0c;湖倉一體電商數據分析平臺實踐教程合集&#xff0c;包含視頻教程、設計文檔及完整項目代碼等資料&#xff0c;供大家學習。 1、項目背景介紹及項目架構 2、項目使用技術版本及組件搭建 3、項目數據種類與采集 4、實時業務統計指標分析一——ODS分層設計與數…

【人工智能】大語言模型多義詞解析技術揭秘——以“項目“歧義消解為例

今天田辛老師和小伙伴探討了一個有趣的多義詞問題&#xff0c; 在人工智能技術日新月異的今天&#xff0c;大語言模型&#xff08;LLM&#xff09;對自然語言的理解能力已經達到令人驚嘆的水平。大模型到底是如何去區分多義詞的&#xff1f; 比如&#xff1a;當用戶提到"…

【maxENT】最大熵模型(Maximum Entropy Model)R語言實現

文章目錄 一、相關package介紹1.1 dismo 包1.2 raster包1.3 常見問題與解決 二、代碼示例 &#x1f7e2;&#x1f7e0;先看&#xff1a;【maxENT】最大熵模型&#xff08;Maximum Entropy Model&#xff09;介紹與使用&#xff08;maxENT軟件&#xff09; ASCII文件太大&#…

哈希表-算法小結

哈希表 map set 數組 在C中&#xff0c;set 和 map 分別提供以下三種數據結構&#xff0c;其底層實現以及優劣如下表所示&#xff1a; 集合底層實現是否有序數值是否可以重復能否更改數值查詢效率增刪效率std::set紅黑樹有序否否O(log n)O(log n)std::multiset紅黑樹有序是否…

OpenCompass模型評估

OpenCompass面向大模型的開源方和使用者&#xff0c; 提供開源、高效、全面的大模型評測開放平臺。 一、OpenCompass文檔 1.基礎安裝 使用Conda準備 OpenCompass 運行環境&#xff1a; conda create --name opencompass python3.10 -y conda activate opencompass2. 安裝 Op…

博途 TIA Portal之1200做主站與有意思的板子做MODBUS_RTU通訊

做為博途的硬件,1200和1500本體都不具有串口通訊功能,只能使用擴展板或是通訊模塊完成。 其中1200使用CB1241或CM1241進行串口通訊,本文將使用CM1241進行演示。 1、硬件介紹 1200的PLC一臺,有意思的板子(以下簡單4D板)一臺。 其中1200帶擴展模塊CM1241 RS232;4D板使…

【深度學習與實戰】3.1 邏輯回歸模型

?1. 定義與核心思想? 邏輯回歸&#xff08;Logistic Regression&#xff09;是一種用于?二分類問題?的統計學習方法&#xff0c;通過?sigmoid函數?將線性回歸的輸出映射到[0,1]區間&#xff0c;表示樣本屬于某一類別的概率?。 ?本質?&#xff1a;廣義線性模型&#x…

AI三萬字論文生成效果——隨機森林在信用卡欺詐分析

以下內容全文由AI制作&#xff0c;有gemini和gpt模型配合一次性生成&#xff08;即未來我們會發布的功能&#xff09;&#xff0c;一次性生成的三萬多字論文效果。 標題&#xff1a;隨機森林在信用卡欺詐分析中的應用研究 摘要 信用卡欺詐已成為全球金融領域面臨的嚴峻挑戰…

質檢LIMS系統在半導體制造行業的應用 半導體質量革命的現狀

在半導體這個“工業皇冠上的明珠”領域&#xff0c;納米級的精度要求與質量管控如同硬幣的兩面。隨著芯片制程向3nm、2nm演進&#xff0c;傳統質檢模式已難以滿足海量數據、復雜工藝的質量追溯需求。質檢LIMS實驗室系統作為質量管理的中樞神經&#xff0c;正在重構半導體制造的…

idea手動創建resources文件夾

有時maven沒有構建成功可能造成&#xff0c;resources文件夾不創建的現象 此時我們可以手動創建 手動創建

利用Ruby的Typhoeus編寫爬蟲程序

Typhoeus是一個基于libcurl的HTTP客戶端&#xff0c;支持并行請求&#xff0c;適合高效爬取數據。用戶可能想要一個簡單的例子&#xff0c;或者需要處理更復雜的情況&#xff0c;比如分頁、并發請求或者數據解析。 首先&#xff0c;我應該檢查用戶是否已經安裝了Typhoeus。通常…

【mllm】——x64模擬htp的后端無法編譯debug

mllm, qnn, x64 code:https://github.com/UbiquitousLearning/mllm 1. 問題 通過自定義qualcomm graph使用高通的htp后端進行llm推理&#xff0c;網絡暫時只有mllm&#xff0c;和https://github.com/chraac/llama.cpp。qualcomm是支持x64模擬htp推理的&#xff0c;這樣比較好d…

JDK(Java Development Kit)從發布至今所有主要版本 的詳細差異、新增特性及關鍵更新的總結,按時間順序排列

以下是 JDK&#xff08;Java Development Kit&#xff09;從發布至今所有主要版本 的詳細差異、新增特性及關鍵更新的總結&#xff0c;按時間順序排列&#xff1a; 1. JDK 1.0 (1996) 發布年份&#xff1a;1996年1月23日關鍵特性&#xff1a; Java首次正式發布。核心語言特性…