[Unity3d] 網絡開發基礎【個人復習筆記/有不足之處歡迎斧正/侵刪】

TCP/IP

TCP/IP協議是一 系列規則(協議)的統稱,他們定義了消息在網絡間進行傳輸的規則
是供已連接互聯網的設備進行通信的通信規則

OSI模型只是一個基本概念,而TCP/IP協議是基于這個概念的具體實現

TCP和UDP協議

TCP:傳輸控制協議,面向連接,更安全,效率較低,一對一
UDP:用戶數據報協議,無連接,不保證可靠性,效率較高,隨意組合

TCP

是面向連接的協議,也就是說,在收發數據前,必須和對方建立可靠的連接
并且在消息傳送過程中是有順序的,并且是不會丟包(丟棄消息)的
如果某一條消息在傳送過程中失敗了,會重新發送消息,直到成功

三次握手 四次揮手

三次握手建立連接
第一次握手(C->S)
TCP連接請求
第二次握手(S->C)
TCP授予連接
第三次握手(C->S)
TCP確認連接

四次揮手斷開連接
第一次揮手(C->S)
客戶端告訴服務器數據已經發完,如果服務器還有消息就請快發完
第二次揮手(S->C)
服務器告訴客戶端繼續等待服務器的消息
第三次揮手(S->C)
服務器告訴客戶端消息發送完成,可以正式斷開連接
第四次揮手(C- ->S)
客戶端告訴服務器等一會如果沒有收到服務器回復就斷開 了

提供可靠的服務,通過TCP連接傳送的數據,做到無差錯、不丟失、不重復、且按順序到達

UDP

是一種無需建立連接就可以發送封裝的IP數據包的方法,提供了面向事務的簡單不可靠信息傳送服務

具有資源消耗小,處理速度快的特點

UDP協議不像TCP協議需要建立連接有三次握手和四次揮手,當使用UDP協議發送信息時會直接把信息數據扔到網絡上,所以也就造成了UDP的不可靠性。信息在這個傳遞過程中是有可能丟失的雖然UDP是一個不靠譜的協議,但是由于它不需要建立連接。也不會像TCP協議那樣攜帶更多的信息,所以它具有更好的傳輸效率
?

網絡游戲通信方案

Socket\HTTP\FTP

Socket:網絡嵌套字

HTTP:超文本傳輸協議,主要完成短鏈接網絡游戲需求

FTP:主要用來完成資源的下載和上傳

byte [] ipAddress = new byte[]{118,102,111,11};
IPAddress ip1 =new IPAddress(ipAddress);
//用byte進行初始化//使用字符串進行初始化
IPAdress ip =IPAddress.Parse("118.102.111.11");//ipi27.0.0.1代表本機地址//命名空間System.NetIPEndPoint ipPoint =new IPEndPoint(0x79666F0B,8080);
IPEndPoint ipPoint2 =new IPEndPoint(IPAddress.Parse("118.102.111.11"),8080);

域名解析

也叫域名指向,服務器設置,域名配置

域名系統是互聯網上的一種服務,管理名字與IP的對應關系

//IPHostEntry類
//域名解析后的返回值,可以獲取該對象的IP地址主機名等信息//DNS類
print(Dns.GetHostName());
//獲取主機名字//獲取指定域名的ip地址
//可能會阻塞主線程
IPHostEntry entry = Dns.GetHostEntry("www.baidu.com");for(int i=0;i<entry.AddressList.Length;i++){print(entry.AddressList[i]);
}

Socket 套接字

c#用于提供網絡通信的一個類

類名:Socket? ????????命名空間:System.Net.Socket

Socket套接字時支持TCP/IP網絡通信的基本操作單位

一個套接字包括:本機地址IP和端口/對方主機的IP地址和端口/雙方通信的協議信息

一個Socket對象表示一個本地或者遠程嵌套字信息,可被視為一個數據通道,鏈接服務器和客戶端,可以接受數據的發送和接收

適合長連接的網絡游戲

AddressFamily 網絡尋址 枚舉類型,決定尋址方案
????????InterNetwork IPv4尋址
????????InterNetwork6 IPv6尋址

SocketType 嵌套字枚舉類型 決定使用的套接字類型
????????Dgram 支持數據報,最大長度固定,無連接,不可靠的消息(主要用于UDP通信)
????????Stream 支持可靠、雙向、基于連接的字節流(主要用于TCP通信)

ProtocolType 協議類型枚舉,決定套接字使用的通信協議

? ? ? ? TCP

? ? ? ? UDP

流套接字

主要實現TCP通信

數據報套接字

主要實現UDP通信

    //TCP流套接字Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//UDPSocket socketUdp = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

實現服務端基本邏輯

 //創建套接字Socket socketTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//用bind方法將套接字與本地地址綁定//端口號要大于1024,且不能被占用IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);try{socketTcp.Bind(ipPoint);}catch (Exception e){return;}//用LISTEN監聽socketTcp.Listen(1024);//accept等待客戶端連入//建立連接,返回套接字Socket socketClient =socketTcp.Accept();//收發數據socketClient.Send(Encoding.UTF8.GetBytes("HFUT"));byte[] result=new byte[1024];int receiveNum=socketClient.Receive(result);//返回值為接收到的數量socketClient.Shutdown(SocketShutdown.Both);socketClient.Close();

實現客戶端基本邏輯

//創建套接字Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//與服務端相連IPEndPoint ipPoint =new IPEndPoint(IPAddress.Parse("127.0.0.0"), 8080);//上述應填寫服務端的IP地址try{socket.Connect(ipPoint);}catch(SocketException e) {if(e.ErrorCode ==10061){}else{}}//收發數據byte[] data = new byte[1024];int num = socket.Receive(data);//發送數據socket.Send(Encoding.UTF8.GetBytes("HFUTER"));socket.Shutdown(SocketShutdown.Both);socket.Close();

區分消息類型

發送自定義類的二進制信息(需要繼承BaseData類)

區分不同消息:給發送的消息添加標識,比如添加消息ID

例如選擇int做消息ID,那么熱前四個字節為消息ID,后面為數據類的內容

實現:

????????1.創建一個消息基類,基類繼承BaseData,基類添加獲取消息的ID的方法或者屬性

? ? ? ? 2.讓想要被發送的消息繼承該類,實現序列化反序列化的方法

? ? ? ? 3.寫客戶端和服務端收發處理消息的邏輯

分包與黏包

網絡通信中由于各種因素造成的消息與消息之間出現的兩種狀態

分包:一個消息分成了多個消息進行發送

黏包:一個消息和另一個消息黏在一起

兩者可能同時發生

解決辦法:

可以通過消息的長度來判斷是否出現分包或者黏包

為消息添加長度,消息長度記錄消息的長度

心跳信息

在長連接中,客戶端和服務端之間定期發送的一種特殊數據包,通知對方自己還在線,以保證長連接的有效性

其發送時間間隔是固定的,且持續,因此稱之為心跳消息

心跳消息可以避免非正常關閉客戶端時,服務器無法正常收到關閉連接消息,同時避免客戶端長期不發消息,防火墻或者路由器會斷開連接

當客戶端主動斷開時,服務端無法得知客戶端已經主動斷開

在客戶端中可以使用Disconect方法,看是否因為之前直接Close()從而沒有調用Disconect造成服務端無法及時獲取狀態(仍然無法準確地讓服務端得知客戶端已經斷開連接)

可以考慮自定義退出消息

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class QuitMsg : BaseMsg
{//繼承自消息基類//主動發送一條斷開連接的消息給服務端//重寫函數public override int GetBytesNum(){return 8;}public override int Reading(byte[] bytes, int beginIndex = 0){return 0;}public override byte[] Writing(){int index = 0;byte[] bytes = new byte[GetBytesNum()];WriteInt(bytes, GetID(), ref index);WriteInt(bytes, 0, ref index);return bytes;}public override int GetID(){return 1003;}
}

實現心跳消息

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class HeartMsg : BaseMsg
{//繼承消息基類public override int GetBytesNum(){return 8;}public override int Reading(byte[] bytes, int beginIndex = 0){//不需要反序列化任何變量return 0;}public override byte[] Writing(){int index = 0;byte[] bytes = new byte[GetBytesNum()];WriteInt(bytes, GetID(), ref index);WriteInt(bytes, 0, ref index);return bytes;}public override int GetID(){return 999;}
}

客戶端:定時發送消息

服務端:收心跳消息 記錄收到消息的時間

異步通信

方法中的邏輯還沒執行完,便繼續執行后面的內容

UNITY中的協同程序中的某系異步方法,有的使用的是多線程有的使用的是迭代器分步執行

//線程回調
public void CountDownAsync(int second, UnityAction callBack){Thread t = new Thread(() =>{while (true){print(second);Thread.Sleep(1000);--second;if (second == 0)break;}
//?的目的是,如果是空的,則不會執行callBack?.Invoke();});t.Start();print("開始倒計時");}//讓函數分布執行public async void CountDownAsync(int second){print("倒計時開始");//await->Task通過線程池開啟一個線程
//本質上將函數分布執行await Task.Run(() =>{while (true){print(second);Thread.Sleep(1000);--second;if (second == 0)break;}});print("倒計時結束");}

UCP通信

udp不會對數據包進行合并發送,不會出現黏包問題

UDP是不可靠連接,消息傳遞過程中可能出現無序、丟包等情況

為了避免分包,建議控制消息的大小在MTU(最大傳輸單元)范圍內

MTU:

最大傳輸單元,用來通知對方所能接受數據服務單元的最大尺寸

局域網:1472字節以內? ? ?互聯網:548字節以內

如果想要發送的消息過大,可以進行手動分包,但是手動分包的前提是解決UDP的無序和丟包的問題

UDP流程更簡單,效率高,但是不可靠

FTP

是支持因特網文件傳輸的各種規則的集合,使得文件可以被從一臺主機拷貝到另一臺主機上。此外,FTP還提供登錄、文件查詢以及其他繪畫控制等功能

FTP本質上是TCP通信通過FTP,雙發至少需要簡歷兩個TCP連接,一個稱為控制連接,一個成為數據連接

FTP的數據連接和控制連接方向一般是相反的

兩種傳輸方式:1.ASCII傳輸? ? 2.二進制傳輸

FTP關鍵類

1.NetworkCredential 通信憑證類?

命名空間:System.Net

用于Ftp文件傳輸,設置賬號密碼

NetworkCredrntial n =new NetworkCredrntial("HFUTER","hfuter");


2.FtpWebRequest Ftp文件傳輸客戶端操作類

命名空間:System.Net

用于上傳,下載,刪除服務器上的文件
3.FtpWebResponse類

FtpWebRequest req = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/Test.txt")) as FtpWebRequest;//創建req.Abort();//停止Stream s=  req.GetRequestStream();//獲取流對象FtpWebResponse res= req.GetResponse() as FtpWebResponse;//返回FTP服務器響應

HTTP

HTTP超文本傳輸協議就是一個在網絡中上傳下載文件的一套規則

其本質也是TCP通信,因此不會丟包、不會亂序

1.以TCP方式工作:

HTTP/1.1支持持久連接(目前使用版本)

2.HTTP是無狀態的:

HTTP不會記錄客戶端請求過的狀態

3.元信息作為標頭

主要數據前添加一段額外信息

請求類型和相應狀態碼

HTTP/1.1:GET\POST\HEAD\PUT\......

響應狀態碼:1xx\2xx\3xx\4xx\5xx

GET:請求獲取特定的資源

POST:請求提交數據進行處理

HTTP常用狀態碼:

200 OK;404 NOT FOUND;405 不支持請求的方法;501 服務器不能識別請求揮著沒有實現指定的請求

關鍵類

HttpWebRequest類:

命名空間:System.Net
HttpWebRequest是主要用于發送客戶端請求的類
主要用于:發送HTTP客戶端請求給服務器,可以進行消息通信、上傳、下載等等操作
?

//創建新的WebRequest,用于進行HTTP相關操作HttpWebRequest req = HttpWebRequest.Create(new Uri("http://192.168.50.109:8000/Http_Server/")) as HttpWebRequest;//終止傳輸 req.Abort();//獲取用于上傳的流Stream s = req.GetRequestStream();//返回HTTP服務器響應HttpWebResponse res = req.GetResponse() as HttpWebResponse;//異步獲取用于上傳的流req.BeginGetRequestStream()異步獲取返回的HTTP服務器響應//req.BeginGetResponse()

HttpWebResponse類:


命名空間:System.Net
主要用于獲取服務器反饋信息的類,可以通過HttpWebRequest對象中的GetResponse()方法獲取。當使用完畢時,要使用Close釋放

POST

上傳文件到HTTP資源服務器需要遵守的規則:

1:ContentType = "multipart/form-data; boundary=邊界字符串";

2:上傳的數據必須按照格式寫入流中

3:保證服務器允許上傳

4:寫入流前需要先設置ContentLength內容長度

WWW類

WWW是Unity提供給程序員簡單的訪問網頁的類,可以通過該類下載和上傳一些數據,在使用http協議時,默認的請求類型是Get,如果想要Post上傳,需要配合WWWFrom類使用(該類在較新Unity版本中會提示過時,但是仍可以使用,新版本將其功能整合進了UnityWebRequest類)

主要支持的協議:

1.http://和https:// 超文本傳輸協議
2.ftp:// 文件傳輸協議(但僅限于匿名下載)
3.file:// 本地文件傳輸協議,可以使用該協議異步加載本地文件(PC、IOS、Android都支持)

//創建
WWW www = new WWW("");//從下載數據返回一個音效切片AudioClip對象
www.GetAudioClip();//用下載數據中的圖像來替換現有的一個Texture2D對象
Texture2D tex = new Texture2D(100, 100);//從緩存加載AB包對象,如果該包不在緩存則自動下載存儲到緩存中,以便以后直接從本地緩存中加載
WWW.LoadFromCacheOrDownload("", 1);

UnityWebRequest

是一個Unity提供的一個模塊化的系統類,用于構成HTTP請求和處理HTTP響應,主要目標是讓Unity游戲和Web服務端進行交互,將之前WWW的相關功能都集成在了其中(新版本中都建議使用UnityWebRequest類來代替WWW類)

注意:
1.UnityWebRequest和WWW一樣,需要配合協同程序使用
2.UnityWebRequest和WWW一樣,支持http、ftp、file協議下載或加載資源
3.UnityWebRequest能夠上傳文件到HTTP資源服務器

UnityWebRequest類的常用操作:

        //1.獲取文本或2進制StartCoroutine(LoadText());//2.獲取紋理StartCoroutine(LoadTexture());//3.獲取AB包StartCoroutine(LoadAB());IEnumerator LoadText(){UnityWebRequest req = UnityWebRequest.Get("http://192.168.50.109:8000/Http_Server/test.txt");//就會等待 服務器端響應后 斷開連接后 再繼續執行后面的內容yield return req.SendWebRequest();//如果處理成功 結果就是成功枚舉if(req.result == UnityWebRequest.Result.Success){//獨立的處理對象print(req.downloadHandler.text);byte[] bytes = req.downloadHandler.data;print("字節數組長度" + bytes.Length);}else{print("獲取失敗:" + req.result + req.error + req.responseCode);}}IEnumerator LoadTexture(){//UnityWebRequest req = UnityWebRequestTexture.GetTexture("http://192.168.50.109:8000/Http_Server/HFUTER.jpg");//UnityWebRequest req = UnityWebRequestTexture.GetTexture("ftp://127.0.0.1/HFUTER.jpg");UnityWebRequest req = UnityWebRequestTexture.GetTexture("file://" + Application.streamingAssetsPath + "/test.png");yield return req.SendWebRequest();if (req.result == UnityWebRequest.Result.Success){//(req.downloadHandler as DownloadHandlerTexture).texture//DownloadHandlerTexture.GetContent(req)//image.texture = (req.downloadHandler as DownloadHandlerTexture).texture;image.texture = DownloadHandlerTexture.GetContent(req);}elseprint("獲取失敗" + req.error + req.result + req.responseCode);}IEnumerator LoadAB(){UnityWebRequest req = UnityWebRequestAssetBundle.GetAssetBundle("http://192.168.50.109:8000/Http_Server/lua");req.SendWebRequest();while (!req.isDone){print(req.downloadProgress);print(req.downloadedBytes);
//每幀執行yield return null;}//yield return req.SendWebRequest();print(req.downloadProgress);print(req.downloadedBytes);if (req.result == UnityWebRequest.Result.Success){//AssetBundle ab = (req.downloadHandler as DownloadHandlerAssetBundle).assetBundle;AssetBundle ab = DownloadHandlerAssetBundle.GetContent(req);print(ab.name);}elseprint("獲取失敗" + req.error + req.result + req.responseCode);}

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

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

相關文章

VsCode配置PCL、Open3D自動補全

寫在前面 本文內容 在VsCode上開發PCL、Open3D相關代碼&#xff0c;代碼自動補全 Open3D、PCL的安裝使用見各個版本的Open3D、PCL的編譯、使用教程 平臺/環境 windows11(windows10): visual studio 2022&#xff1b;cmake 3.22; VsCode 通過cmake構建項目&#xff1b; 轉載請…

Excel MATCH函數 兩張順序不同表格,統一排序

目錄 一. 背景二. 添加輔助列,使用MATCH函數生成排序條件三. 效果 一. 背景 有如下圖所示的兩張表格&#xff0c;分別記錄著同一批人的1月份和2月份的工資。表格A和表格B中的姓名列相同&#xff0c;工資列數據不同現在要求參考表格A中的姓名列對表格B中的數據進行排序&#xf…

C語言:預處理

C語言&#xff1a;預處理 預定義符號#define定義常量定義宏宏與函數對比 #操作符##操作符條件編譯頭文件包含庫文件包含本地文件包含嵌套文件包含 預定義符號 C語?設置了?些預定義符號&#xff0c;可以直接使?&#xff0c;預定義符號也是在預處理期間處理的。 __FILE__ //…

多智能體強化學習簡介

基礎概念 什么是多智能體系統 多智能體系統&#xff08;Multi-Agent System&#xff0c;MAS&#xff09;是由多個自主智能體組成的系統。這些智能體可以協同工作&#xff0c;也可以獨立行動&#xff0c;以實現各自的目標。在多智能體系統中&#xff0c;每個智能體都有自己的決…

在你的 Vue + Electron 項目里,引入 ESLint

因為我的項目是基于 Electron 平臺的 Web 應用&#xff0c;使用 Vue 3 實現&#xff0c;而且用了 TypeScript&#xff0c;所以&#xff0c;在引入 ESLint 的時候&#xff0c;要考慮好幾種規范的問題。 文章目錄 零、簡介1. 規則2. 配置文件3. 共享配置4. 插件5. 解析器6. 自定義…

Vue開發實例(九)動態路由實現左側菜單導航

之前在【Vue開發實例&#xff08;六&#xff09;實現左側菜單導航】文中實現了菜單的導航&#xff0c;本篇是在那個基礎上改造的。 動態路由實現左側菜單導航 一、動態菜單創建二、根據菜單數據來創建路由三、添加路由已加載標記&#xff0c;省的每次點擊菜單都要加載 一、動態…

2021 年 3 月青少年軟編等考 C 語言一級真題解析

目錄 T1. 字符菱形思路分析 T2. 與圓相關的計算思路分析 T3. 蘋果和蟲子 2思路分析 T4. 奇數求和思路分析 T5. 藥房管理思路分析 T1. 字符菱形 給定一個字符&#xff0c;用它構造一個對角線長 5 5 5 個字符&#xff0c;傾斜放置的菱形。 時間限制&#xff1a;1 s 內存限制&a…

3、云原生安全之falco的部署

文章目錄 1、helm安裝2、拉去鏡像失敗與解決3、安裝faclo4、安裝nfs服務器,配置k8s的持久卷4.1、創建nfs服務器,4.2、部署master節點(nsf服務的客戶端)4.3、pv與pvc4.4、假設pv和pvc的配置文件出錯了5、安裝falcosidekick可視化(建議跳過,直接使用6)6、安裝faclo與falco…

【設計模式 01】單例模式

單例模式&#xff0c;是一種創建型設計模式&#xff0c;他的核心思想是保證一個類只有一個實例&#xff08;即&#xff0c;在整個應用程序中&#xff0c;只存在該類的一個實例對象&#xff0c;而不是創建多個相同類型的對象&#xff09;&#xff0c;并提供一個全局訪問點來訪問…

java012 - Java集合基礎

1、集合基礎 1.1 集合概述 引用數據類型包括&#xff1a;類、接口、數組[] 1.2 ArrayList構造和添加方法 代碼&#xff1a; 空集合對象&#xff1a;[] add() add(int index,E element): 1.3 ArrayList集合常用方法

計算機體系結構安全:對體系結構如何支持安全機制進行調研

一、體系結構支持信任建立和主動防御的技術&#xff1a; 可信3.0 二、體系結構怎么更好的支持信任建立和主動防御 2.1 支持信任建立 一、以手機芯片舉例&#xff0c;用智能手機的芯片作為信任根&#xff0c;確保應用程序和敏感數據受到保護。 二、啟動時驗證操作系統和應用…

Stable Diffusion 模型分享:Henmix_Real(人像、真實、寫真、亞洲面孔)

本文收錄于《AI繪畫從入門到精通》專欄,專欄總目錄:點這里。 文章目錄 模型介紹生成案例案例一案例二案例三案例四案例五案例六案例七案例八下載地址模型介紹 作者述:這個模型試圖改

深入理解算法的空間復雜度

算法一&#xff1a;逐步遞增型 void Loveyou(int n)//n為問題規模 {int i1;while(i<n){i;printf("I love you %d\n",i);}printf("I love you more than %d\n",n);//5 } int main() {Loveyou(3000);return 0; } 無論問題規模怎么變&#xff0c;算法運行…

Springboot教程(五)——單元測試

idea中一般使用JUnit進行單元測試 基本使用 我們可以在idea的test文件夾下的XXXXApplicationTests內進行單元測試&#xff1a; 可以在Test標注的方法上寫測試代碼&#xff1a; SpringBootTest class C0101ApplicationTests {Testfun contextLoads() {println("Hello …

基礎二分學習筆記

模板 : 個人傾向第一種 ; 整數二分 : 最大化查找 : 可行區域在左側 : 查找最后一個<q的數的下標 : int find(int q){// 查找最后一個 < q 的下標 int l 0 , r n 1 ;while(l 1 < r){int mid l r >> 1 ;if(a[mid]<q) l mid ;else r mid ;}return…

django settings.py STATICFILES_FINDERS 設置

STATICFILES_FINDERS 定義查找器后端以確保Django能夠正確地定位和提供靜態文件是很重要的. Django中的STATICFILES FINDERS設置是一個inder后端列表&#xff0c;它知道如何在不同的位置定位靜態文件。 它被Django的靜態文件處理系統用來在開發和部署過程中查找和收集靜態文件…

js json轉換成字符串

js中JSON數據轉換成字符串&#xff0c;可以使用JSON.stringify()方法。 var obj {name: "張三", age: 18, gender: "男"}; var jsonString JSON.stringify(obj); console.log(jsonString); // 輸出 {"name":"張三","age"…

土壤類型數據

國家地球系統科學數據中心

AGM CPLD (AGRV2K )的時鐘(外部時鐘和片上內部振蕩器)

AGM CPLD &#xff08;AGRV2K &#xff09;的時鐘(外部時鐘和片上內部振蕩器) 外部晶振 與 內部振蕩器&#xff1a; mcu 和 cpld 聯合編程時&#xff0c; 整顆芯片需要一顆外部晶振。 &#xff08;芯片有內部振蕩器&#xff0c; 但誤差較大&#xff0c; 校準后 5%以內誤差&…

216. 組合總和 III(力扣LeetCode)

文章目錄 216. 組合總和 III回溯算法 216. 組合總和 III 找出所有相加之和為 n 的 k 個數的組合&#xff0c;且滿足下列條件&#xff1a; 只使用數字1到9每個數字 最多使用一次 返回 所有可能的有效組合的列表 。該列表不能包含相同的組合兩次&#xff0c;組合可以以任何順序…