HttpComponents: 領域對象的設計

1. HTTP協議

1.1 HTTP請求

HTTP請求由請求頭、請求體兩部分組成,請求頭又分為請求行(request line)和普通的請求頭組成。通過瀏覽器的開發者工具,我們能查看請求和響應的詳情。 下面是一個HTTP請求發送的完整內容。

POST https://track.abc.com/v4/track HTTP/1.1
Host: track.abc.com
Connection: keep-alive
Content-Length: 2048
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: "Google Chrome";v="87", " Not;A Brand";v="99", "Chromium";v="87"
Accept: application/json, text/javascript, */*;
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Origin: https://class.abc.com
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: https://class.abc.com/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: HJ_UID=1fafb8b2-2a34-9cbe-e6ba-b8b4aabab4a1; _SREF_45=d=2020&t=1609310840140

按照上面的理論,我們可以將這一個完整的請求拆分為3部分,請求行、請求頭、請求體。

1. 請求行
POST https://track.abc.com/v4/track HTTP/1.1

POST用于指定請求的方法,此外還可以有OPTOINS GET HEAD PUT DELETE TRACE CONNECT等,更多詳細解釋可以參見RFC 2616。后面跟的https://track.abc.com/v4/track是我們要訪問的資源URI。最后的HTTP/1.1指定了HTTP協議的版本,HTTP/1.1是目最常見的版本。

2. 請求頭
Host: track.abc.com
Connection: keep-alive
Content-Length: 2048
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/javascript, */*;
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Referer: https://class.abc.com/
Accept-Encoding: gzip, deflate, br
Cookie: HJ_UID=1fafb8b2-2a34-9cbe-e6ba-b8b4aabab4a1; _SREF_45=
Header說明
Host指定要訪問的域名。請求行的域名會在客戶端訪問時轉換為具體的IP。 Nginx是通過請求頭的Host來將請求轉發到不同的域名配置的。
Connection用于指定連接保持的策略,這里的keep-alive是期望服務器保持連接,在后續的請求中直接復用當前連接。
Pragma設置代理服務器(varnish)是否允許緩存,設置為no-cache時代理即使發現有緩存也會回源上層服務器。
Cache-Control類是Pragma,不同的是這個請求體是HTTP 1.0時代的規范,請這個頭同時支持做為響應頭,但Pragma不能。
Accept指定支持的MIME-TYPE
Content-Type指定請求內容類型以及編碼
Referer上一頁地址
Accept-Encoding支持的壓縮方式
Cookie符合當前請求的Cookie值

HTTP請求是無狀態的,Java服務端的Session都是通過Cookie保存會話標識來實現的。 Chrome新版本加了Cookie跨域的邏輯,SameSite設置會影響Cookie上報,對應邏輯查看SameSite對應的筆記。

3. 請求體
d=2020&t=1609310840140

請求體的格式可以通過Content-Type指定,日常我們常用的有兩種格式:

  1. Form表單提交,上面我們給定的就是Form表單提交的數據格式,通過&符合切割字段,通過=連接字段名和字段值。
  2. JSON請求體,一般我們在SpringBoot后端通過@ResponseBody接收
1.2 HTTP響應

類似于HTTP請求,HTTP響應同樣由響應頭、響應內容兩部分組成。 響應頭有分為兩類: 狀態行、 響應頭。 下面是一個HTTP響應的完整內容:

HTTP/1.1 200 OK
Date: Wed, 30 Dec 2020 06:47:20 GMT
Content-Length: 0
Connection: keep-alive
Server: nginx/1.14.0
Access-Control-Allow-Origin: https://class.abc.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400
Access-Control-Allow-Headers: x-requested-with,Authorization,Cookie
Access-Control-Allow-Credentials: true
Set-Cookie: HJ_SSID_45=hsrein-fe2d-455c-a765-ce1b76647d4c; Domain=.abc.com; Expires=Wed, 30-Dec-2020 07:17:20 GMT; Path=/
Set-Cookie: _SREF_45=""; Domain=.abc.com; Expires=Thu, 01-Jul-2021 06:47:20 GMT; Path=/
Set-Cookie: HJ_CSST_45=0; Domain=.abc.com; Expires=Wed, 30-Dec-2020 07:17:20 GMT; Path=/
X-Via: 1.1 PS-000-01AdS239:3 (Cdn Cache Server V2.0)
X-Ws-Request-Id: 5fec2278_PS-000-01yOO242_18720-46076{"hj_vt": 1
}
1. 狀態行
HTTP/1.1 200 OK

狀態行有3部分組成,HTTP/1.1標識HTTP協議的版本號,200是我們的HTTP響應的狀態碼,OK是HTTP響應的描述。

目前的狀態碼分為5類:

狀態碼描述
1xx請求已經接收,后臺內部處理中
2xx請求成功
3xx重定向,需要客戶端(瀏覽器)發起后續操作
4xx客戶端錯誤
5xx服務端錯誤
2. 響應頭
Date: Wed, 30 Dec 2020 06:47:20 GMT
Connection: keep-alive
Server: nginx/1.14.0
Access-Control-Allow-Origin: https://class.aaa.com
Set-Cookie: HJ_SSID_45=hsrein-fe2d-455c-a765-ce1b76647d4c; Domain=.bbb.com; Expires=Wed, 30-Dec-2020 07:17:20 GMT; Path=/
X-Via: 1.1 PS-000-01AdS239:3 (Cdn Cache Server V2.0)
X-Ws-Request-Id: 5fec2278_PS-000-01yOO242_18720-46076
Header說明
Date響應內容生成的時間
Connection連接復用策略
Access-Control-Allow-Origin允許跨域,https://class.aaa.com頁面發起到當前接口跨域請求
Set-Cookie向客戶端寫入Cookie

2. 領域對象設計

設計良好的系統有清晰劃分和邊界,層層遞進,領域對象設計很考驗架構師的全局觀。隨意的繼承、組合,很快就會變的不可維護,導致項目失敗。

所謂的架構就是定義劃分和邊界,讓系統的增長不受限于當初的定義的能力,具體的技術只是幫助實現這個定義的手段。

2.1 HttpMessage

HTTP請求和HTTP響應都繼承了HttpMessageHttpMessage提供HTTP頭各種操作(讀取、寫入、遍歷等)。

以下的代碼是對HttpMessage的基本操作:

HttpMessage ht = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
ht.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
ht.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");
Header h1 = ht.getFirstHeader("Set-Cookie");
System.out.println(h1);
Header h2 = ht.getLastHeader("Set-Cookie");
System.out.println(h2);
Header[] hs = ht.getHeaders("Set-Cookie");
System.out.println(hs.length);

遍歷所有HTTP頭:

HeaderIterator it = ht.headerIterator("Set-Cookie");
while (it.hasNext()) {System.out.println(it.next());
}

通過BasicHeaderElementIterator解析HTTP頭:

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
response.addHeader("Set-Cookie", "c1=a; path=/; domain=localhost");
response.addHeader("Set-Cookie", "c2=b; path=\"/\", c3=c; domain=\"localhost\"");HeaderElementIterator it = new BasicHeaderElementIterator(response.headerIterator("Set-Cookie"));while (it.hasNext()) {HeaderElement elem = it.nextElement();System.out.println(elem.getName() + " = " + elem.getValue());NameValuePair[] params = elem.getParameters();for (int i = 0; i < params.length; i++) {System.out.println(" " + params[i]);}
}
2.2 HTTP請求

HttpCore使用HttpRequest表示HTTP請求,我們可以通過下面這段代碼創建一個最簡單的HTTP請求:

HttpRequest request = new BasicHttpRequest("GET", "/index.html", HttpVersion.HTTP_1_1);
System.out.println(request.getRequestLine().getMethod());      // 輸出 GET
System.out.println(request.getRequestLine().getUri());         // 輸出 /index.html
System.out.println(request.getProtocolVersion());              // 輸出 HTTP/1.1
System.out.println(request.getRequestLine().toString());       // 輸出 GET /index.html HTTP/1.1
1. HttpRequest

HttpCore提供了大量實現類,下面的圖片是httpcore:4.4.13版本下的類繼承結構

  • HttpMessage提供了HTTP頭和HTTP協議版本號相關的操作。
  • HttpRequestHttpMessage的基礎上額外提供了請求行。
  • AbstractExecutionAwareRequest實現HttpMessageHttpRequest,額外實現HttpExecutionAware查看是否取消、接收Cancellable對象取消請求。
  • HttpUriRequest繼承自HttpRequest,額外提供了獲取HTTP方法、URI,運行取消執行(abort方法),以及查詢是否已經取消(isAborted方法)
  • HttpEntityEnclosingRequest繼承自HttpRequest,額外提供攜帶請求體的能力(setEntity(HttpEntity entity))
  • BasicHttpRequest提供了HttpRequest最基本的實現,對于只需要請求行、HTTP頭的請求,可以直接使用。
    在這里插入圖片描述
2. HttpGet、HttpPost、HttpPut、HttpDelete等

HttpRequest的4個主要實現類中AbstractExecutionAwareRequestHttpUriRequest共同做為HttpRequestBase的基類,提供不包含請求體的HTTP請求實現,我們常用的有HttpGetHttpDeleteHttpOptionsHttpHeadHttpTrace

HttpRequestBase組合HttpEntityEnclosingRequest提供實現類HttpEntityEnclosingRequestBase,它是所有包含請求體的HTTP請求實現,包括HttpPutHttpPostHttpDeleteHttpPatch
在這里插入圖片描述

2.3 HTTP響應

HttpCore使用HttpResponse表示HTTP響應,同樣繼承自HttpMessage,額外提供了狀態行、響應體(HttpEntity)。通過下面的代碼可以創建一個最簡單的HTTP響應:

HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK");
System.out.println(response.getProtocolVersion());              // 輸出 HTTP/1.1
System.out.println(response.getStatusLine().getStatusCode());   // 輸出 200
System.out.println(response.getStatusLine().getReasonPhrase()); // 輸出 OK
System.out.println(response.getStatusLine().toString());        // 輸出 HTTP/1.1 200 OK
1. HttpResponse

HttpResponse提供了獲取狀態行、狀態碼、狀態描述,以及響應內容的方法。
在這里插入圖片描述

2. BasicHttpResponse

HttpResponse只提供了兩個實現類,常用的BasicHttpResponse封裝普通HTTP響應,HttpResponseProxy供代理服務器使用。
在這里插入圖片描述

2.4 HttpEntity

HttpCore抽象了HttpEntity表示請求體/響應體,回想一下,前面我們學習的HttpRequest有部分是實現了HttpEntityEnclosingRequest的可以攜帶HttpEntity向服務器提交數據。 HttpResponse都包含一個setEntitygetEntity方法,當然RFC文檔定義,部分響應如302跳轉不應該包含HttpEntity

HttpCore官方將HttpEntity分為3類:

類型說明
streamed請求體內容來自于InputStream或者程序生成,因為流無法重復讀取,導致HttpEntity內容只能被消費一次
self-contained請求體內容存儲在內存中,可以反復讀取
wrapping裝飾器模式,請求體內容來自其他HttpEntity,額外包裝處理后對外提供
1. HttpEntity定義

HttpEntity的核心作用就是表示請求體,請求體會被用在輸入和輸出,基本上這也就確定了HttpEntity的接口定義。

  • 我們要從請求體讀取數據,于是定義了InputStream HttpEntity#getContent()
  • 我們要將請求體發送到服務端(寫到輸出流),于是定義了void HttpEntity#writeTo(OutputStream)
  • 服務端需要知道我們發送的是圖片還是文本,于是定義了Header HttpEntity#getContentType()
  • 服務端需要知道我們發送的文本用什么編碼,于是定義了Header HttpEntity#getContentEncoding()

在這里插入圖片描述

創建HttpEntity要提供ContentType對象,用于定于HttpEntity包含的內容及編碼,后面的HTTP協議攔截器會協助我們處理HTTP頭和HttpEntity的關系。

  • 發送HttpEntity的時候HTTP協議攔截器會自動從HttpEntity讀取ContentType,并在HttpRequest下添加HTTP頭Content-Type
  • 接收HttpResponse初始化HttpEntity時,通過HTTP協議攔截器自動從Content-Type頭讀取并設置HttpEntityContentType

HttpEntity有大量的實現類,我們來看一個最簡單的HttpEntity初始化:

StringEntity myEntity = new StringEntity("important message", Consts.UTF_8);  // 默認ContentType是text/plainSystem.out.println(myEntity.getContentType());                                // 輸出 Content-Type: text/plain; charset=UTF-8
System.out.println(myEntity.getContentLength());                              // 將字符串轉為字節數組后的長度
System.out.println(EntityUtils.toString(myEntity));                           // 構造函數傳入的字符串
System.out.println(EntityUtils.toByteArray(myEntity).length);                 // 將字符串轉為字節數組后的長度

在這里插入圖片描述

HttpEntity有4組實現類:

  • RequestEntityProy,用于實現代理服務器

  • StreamingHttpEntity,提供Body對象,將Body.writeTo方法寫OutputStream,適用于生成InputStream開銷大的場景

  • HttpEntityWrapper,裝飾器模式,主要用于實現壓縮、解壓
    在這里插入圖片描述

  • AbstractHttpEntity,最實用的實現,它是我們常用的StringEntity、InputStreamEntity、BasicHttpEntity、FileEntity、ByteArrayEntity等的父類
    在這里插入圖片描述

1. BasicHttpEntity

BasicHttpEntity提供無參構造函數,默認表示空的HttpEntity。 通過BasicHttpEntity#setContent傳入InputStream,BaiscHttpEntity#setContentLength設置HttpEntity長度,能構造出有實際能容的HttpEntity

我們看一個簡單的示例:

BasicHttpEntity e = new BasicHttpEntity();
e.setContent(new ByteArrayInputStream("helloworld".getBytes()));
e.setContentLength(-1);
2. ByteArrayEntity

ByteArrayEntity屬于self-containedHttpEntity,只需要提供byte數組即可構建。

我們看一個簡單的示例:

ByteArrayEntity myEntity = new ByteArrayEntity(new byte[] {1,2,3}, ContentType.APPLICATION_OCTET_STREAM);
3. StringEntity

StringEntity也是self-containedHttpEntity, 有3種構造方式:

HttpEntity myEntity1 = new StringEntity(sb.toString());  // 默認MIME-TYPE為text/plain,默認編碼 ISO-8859-1
HttpEntity myEntity2 = new StringEntity(sb.toString(), Consts.UTF_8);  // 默認MIME-TYPE為text/plain,自己指定編碼
HttpEntity myEntity3 = new StringEntity(sb.toString(), ContentType.create("text/plain", Consts.UTF_8)); // 自己字段MIME-TYPE和編碼
4. InputStreamEntity

通過InputStream構建,允許傳入要讀取的字節數(-1表示不限)。比較使用的場景是前端提交一個文件后我們拿到一個輸入流,通過這個流我們再上傳到文件到分布式文件系統。

我們看一個簡單的示例:

InputStream instream = getSomeInputStream();
InputStreamEntity myEntity = new InputStreamEntity(instream, 16);
5. FileEntity

通過提供一個File對象構建,是self-contained類型的HttpEntity

我們看一個簡單的示例:

HttpEntity entity = new FileEntity(staticFile,ContentType.create("application/java-archive"));
6. BufferedHttpEntity

BufferedHttpEntity繼承自HttpEntityWrapper,使用裝飾器模式,將其他類型的HttpEntity轉為self-contained類型,內部實現是將其他類型的HttpEntity內容讀取并緩存在內存中。

我們看一個簡單的示例:

myNonRepeatableEntity.setContent(someInputStream);
BufferedHttpEntity myBufferedEntity = new BufferedHttpEntity(myNonRepeatableEntity);
2. EntityUtils

通過InputStream HttpEntity#getContent()太過底層,使用麻煩。 HttpCore提供了EntityUtils幫助我們消費HttpEntityEntityUtils主要提供3類方法:

  • consumeconsumerQuietly用于關閉HttpEntity的輸入流,以便是否資源,在我們不需要HttpEntity的內容的時候使用。
  • toByteArrayHttpEntity內容轉換為字節數字,適用于傳輸非文本內容的場景,比如圖片。
  • toString系列,用于將HttpEntity的內容轉換為字符串,可以使用HttpEntity字段的編碼信息,或者自己指定編碼
2.5 HTTP協議處理器

通常復雜而多變的邏輯都會采用責任鏈模式或者攔截器模式分而治之,HttpCore中的HTTP協議就是通過攔截器鏈完成的。

HttpCore中將攔截器劃分為兩類:

  • HttpRequestInterceptor,請求攔截器
  • HttpResponseInterceptor,響應攔截器
    在這里插入圖片描述

HttpCore定義了大量的攔截器來出來HTTP協議的細節,下面的表格我們列出一些常見的攔截器:

攔截器說明
RequestContent計算請求體長度,添加Content-Length、Transfer-Content頭,添加HTTP版本號
RequestConnControl負責在請求中添加Connection頭
RequestDate負責在請求添加Date頭
RequestExpectContinue負責添加請求的Expect頭
RequestTargetHost負責添加請求的Host頭
RequestUserAgent負責添加請求的User-Agent頭
ResponseContent計算響應體長度,添加Content-Length、Transfer-Content頭,添加HTTP版本號
ResponseConnControl負責在響應中添加Connection頭
ResponseDate負責在響應添加Date頭
ResponseServer負責添加響應的Server頭

HTTP攔截器需要配合HttpProcessorBudiler使用,這個Builder會創建一個ImmutableHttpProcessorImmutableHttpProcessor會在process方法內循環調用其他HttpProcessor

HttpProcessor httpproc = HttpProcessorBuilder.create().add(new RequestContent()).add(new RequestTargetHost()).add(new RequestConnControl()).add(new RequestUserAgent("MyAgent-HTTP/1.1")).add(new RequestExpectContinue(true)).build();HttpCoreContext context = HttpCoreContext.create();
context.setTargetHost(HttpHost.create("www.a.com"));HttpRequest request = new BasicHttpRequest("GET", "/index.html");
request.addHeader("Host","www.a.com");httpproc.process(request, context);HeaderIterator it = request.headerIterator();
while (it.hasNext()) {Header h = it.nextHeader();System.out.println(h.getName() + ":" + h.getValue());
}System.out.println(request);

服務端處理邏輯

HttpResponse = <...>
httpproc.process(response, context);
2.6 HttpCoreContext

HTTP請求本身是無狀態的,很多場景下我們希望保留狀態,比如Java服務端會話需要的JSESSIONID。 HttpCoreContext的目的就是為了解決這個問題。

下面是一個最簡單的使用示例:

HttpProcessor httpproc = HttpProcessorBuilder.create().add(new HttpRequestInterceptor() {public void process(HttpRequest request,HttpContext context) throws HttpException, IOException {String id = (String) context.getAttribute("session-id");if (id != null) {request.addHeader("Session-ID", id);}}}).build();HttpCoreContext context = HttpCoreContext.create();
HttpRequest request = new BasicHttpRequest("GET", "/");
httpproc.process(request, context);

3. 總結

在這里插入圖片描述

4. 參考資料

  1. HTTP協議文檔 RFC2616 https://tools.ietf.org/html/rfc2616
  2. HttpComponents文檔 https://hc.apache.org/httpcomponents-core-ga/tutorial/html/fundamentals.html

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

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

相關文章

根據對數器找規律、根據數據量猜題目解法

題目一 小虎去買蘋果&#xff0c;商店只提供兩種類型的塑料袋&#xff0c;每種類型都有任意數量。1&#xff09;能裝下6個蘋果的袋子2&#xff09;能裝下8個蘋果的袋子小虎可以自由使用兩種袋子來裝蘋果&#xff0c;但是小虎有強迫癥&#xff0c;他要求自己使用的袋子數量必須…

python門戶網站文件爬取并顯示

廣西南寧政府門面網站 import requests import os import io import numpy as np from concurrent.futures import ThreadPoolExecutor from bs4 import BeautifulSoup import time import pdfplumber import pandas as pd from docx import Document import docx import win32…

WordCount 源碼解析 Mapper,Reducer,Driver

創建包 com.nefu.mapreduce.wordcount &#xff0c;開始編寫 Mapper &#xff0c; Reducer &#xff0c; Driver 用戶編寫的程序分成三個部分&#xff1a; Mapper 、 Reducer 和 Driver 。 &#xff08; 1 &#xff09; Mapper 階段 ? 用戶自定義的 Mapper 要繼承自己的父…

文件服務器搭建

文件服務器搭建 文件服務器有四個選擇&#xff1a; httpd&#xff08;apache&#xff09; 穩定&#xff0c;使用廣泛&#xff0c;服務器一般自帶&#xff0c;對于開發人員來說強烈推薦。 nginx 穩定高效&#xff0c;使用廣泛&#xff0c;linux命令可直接下載&#xff0c;對…

STM32CubeIDE串口空閑中斷實現不定長數據接收

STM32F051空閑中斷實現串口不定長數據接收 目的編程軟件配置串口開中斷中斷程序運行結果目的 在串口輸入不定長數據時,通過串口空閑中斷來斷幀接收數據。 編程軟件 STM32CubeIDE STM32CubeMX配置MCU。通過對端口配置,自動生成程序,減少編程量。 配置串口開中斷 配置串口…

redis中序列化問題,value包含全路徑類名解析

1. 問題 redis中保存的key-value格式 value直接存入的是實體對象&#xff0c;值中包含全路徑類名&#xff0c;在使用Jackson2JsonRedisSerializer和GenericJackson2JsonRedisSerializer解析器時報錯 報錯內容&#xff1a; com.fasterxml.jackson.databind.exc.InvalidTypeI…

《師兄啊師兄》第二季確認定檔!海神揚名,穩健回歸!

近日&#xff0c;《師兄啊師兄》第二季的定檔海報和PV終于發布&#xff0c;確認將于12月14日上午10點強勢回歸&#xff01;這部備受矚目的國漫作品自第一季播出以來&#xff0c;便以其獨特的劇情設定和唯美的畫風&#xff0c;贏得了廣大觀眾的喜愛。如今&#xff0c;動畫第二季…

第一課【習題】給應用添加通知和提醒

構造進度條模板通知&#xff0c;name字段當前需要固定配置為downloadTemplate。 給通知設置分發時間&#xff0c;需要設置showDeliveryTime為false。 OpenHarmony提供后臺代理提醒功能&#xff0c;在應用退居后臺或退出后&#xff0c;計時和提醒通知功能被系統后臺代理接管…

Qt 5.15.2 三維顯示功能

Qt 5.15.2 三維顯示功能 三維顯示效果&#xff1a; .pro項目文件 QT core gui opengl 3dcore 3drender 3dinput 3dextrasgreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c17# You can make your code fail to compile if it uses deprecated APIs. # In ord…

2023年法國經銷商Solu-Watt來訪安科瑞-安科瑞 蔣靜

2023年4月10日上午9點&#xff0c;法國Solu-Watt公司Matthieu先生一行到安科瑞考察參觀工廠的智能化出入庫工作站、柔性化儀表生產車間及實驗室。自1992年以來&#xff0c;Solu-Watt在電氣設備市場中不斷發展。能夠提供量身定制的安裝有線電氣解決方案&#xff08;電氣柜、接線…

如何用Qt配置git項目并上傳Gitee

1.進入到Qt項目文件夾內&#xff0c;打開 “Git Bash Here” 2.初始化&#xff0c;在“Git Bash Here”中輸入 git init 3.加入所有文件&#xff0c;在“Git Bash Here”中輸入 git add . (需要注意&#xff0c;git add 后面還有一個點) 4.添加備注&#xff0c;git com…

STL源碼剖析筆記——哈希表、unordered_set、unordered_map、unordered_mutiset、unordered_mutimap

系列文章目錄 STL源碼剖析筆記——迭代器 STL源碼剖析筆記——vector STL源碼剖析筆記——list STL源碼剖析筆記——deque、stack&#xff0c;queue STL源碼剖析筆記——Binary Heap、priority_queue STL源碼剖析筆記——AVL-tree、RB-tree、set、map、mutiset、mutimap STL源…

一套rk3588 rtsp服務器推流的 github 方案及記錄 -01

我不生產代碼&#xff0c;我只是代碼的搬運工&#xff0c;相信我&#xff0c;看完這個文章你的圖片一定能變成流媒體推出去。 訴求&#xff1a;使用opencv拉流&#xff0c;轉成bgr數據&#xff0c;需要把處理后的數據&#xff08;BGR&#xff09;編碼成264&#xff0c;然后推流…

字符串函數strtok

1.調用格式&#xff1a; 2.調用形式&#xff1a;char*strtok(char*p1,const char*p2),其中第二個是由分隔符組成的字符串&#xff0c;第一個為需要分隔的字符串 3.調用目的&#xff1a;將分隔符之間的字符串取出 4.調用時一般將源字符串拷貝后調用&#xff0c;因為此函數會將…

基于Unity3D 低多邊形地形模型紋理貼圖

在線工具推薦&#xff1a; 3D數字孿生場景編輯器 - GLTF/GLB材質紋理編輯器 - 3D模型在線轉換 - Three.js AI自動紋理開發包 - YOLO 虛幻合成數據生成器 - 三維模型預覽圖生成器 - 3D模型語義搜索引擎 當談到游戲角色的3D模型風格時&#xff0c;有幾種不同的風格&#xf…

【工程實踐】使用modelscope下載大模型文件

前言 Modelscope&#xff08;魔搭社區&#xff09;是阿里達摩院的一款開源模型平臺&#xff0c;里面提供了很多的熱門模型供使用體驗&#xff0c;其中的模型文件可以通過git clone 快速下載。并且為模型提供了Notebook的快速開發體驗&#xff0c;使用阿里云服務&#xff0c;不需…

【優選算法系列】【專題二滑動窗口】第三節.904. 水果成籃和438. 找到字符串中所有字母異位詞

文章目錄 前言一、水果成籃 1.1 題目描述 1.2 題目解析 1.2.1 算法原理 1.2.2 代碼編寫 1.2.3 題目總結二、找到字符串中所有字母異位詞 2.1 題目描述 2.2 題目解析 2.2.1 算法原理 2.2.2 代碼編寫 …

SAP UI5 walkthrough step9 Component Configuration

在之前的章節中&#xff0c;我們已經介紹完了MVC的架構和實現&#xff0c;現在我們來講一下&#xff0c;SAPUI5的結構 這一步&#xff0c;我們將所有的UI資產從index.html里面獨立封裝在一個組件里面 這樣組件就變得獨立&#xff0c;可復用了。這樣&#xff0c;無所什么時候我…

隊列的實現

學習就像一段長跑&#xff0c;比的不是誰跑得快&#xff0c;而是誰更能堅持&#xff01;&#xff01; 1 隊列的概念及結構 隊列&#xff1a;只允許在一端進行插入數據操作&#xff0c;在另一端進行刪除數據操作的特殊線性表&#xff0c;隊列具有先進先出 FIFO(First In First O…

外網訪問內網服務器使用教程

如何在任何地方都能訪問自己家里的筆記本上的應用&#xff1f;如何讓局域網的服務器可以被任何地方訪問到&#xff1f;有很多類似的需求&#xff0c;我們可以統一用一個解決方案&#xff1a;內網穿透。內網穿透的工具及方式有很多&#xff0c;如Ngrok、Ssh、autossh、Natapp、F…