linux下用c++11寫一個UDP回顯程序

需求:

1)從2個UDP端口接收數據,并在同樣的端口回顯。echo

2)多個處理線程,多個發送線程;

3)使用條件變量喚醒;

#include <stack>
#include <mutex>
#include <atomic>#include <iostream>
#include <thread>
#include <mutex>
#include <queue>
#include <vector>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <arpa/inet.h>   // for inet_pton
#include <cstring>       // for memset
#include <fcntl.h>       // for non-block
#include <signal.h>
#include <condition_variable>// #include "concurrentqueue.h" // moodycamel concurrentqueue 頭文件using namespace std;
/*
編譯方式(Linux)
g++ -std=c++11           -pthread cssff.cpp -o udp_server -lstdc++
g++ -std=c++11 -Wall -O2 -pthread cssff.cpp -o udp_server -lstdc++你可以用 netcat 發送測試數據:
echo "hello port1" | nc -u 127.0.0.1 4000
echo "hello port2" | nc -u 127.0.0.1 5000
*//*
你幾乎不會收到大于 1472~1500 的數據
推薦設為 1536 字節:正好是大多數操作系統分配 UDP buffer 的默認對齊粒度,還是 64 的倍數(好對齊
*/#define NUM_PROC_THREADS 1
#define NUM_SEND_THREADS 1#define BUF_SIZE_DEF 1536
constexpr int PORT1 = 4000;
constexpr int PORT2 = 5000;
constexpr int MAX_EVENTS = 10;
constexpr int BUF_SIZE = BUF_SIZE_DEF;// 一次批量處理至多32個,防止抖動和阻塞
const size_t MAX_BATCH = 32;// 退出標志
std::atomic<bool> g_running(true);// 數據包的簡單封裝
struct Packet {char data[BUF_SIZE];sockaddr_in addr_from;socklen_t addr_from_len;int port_from;    // 記錄是哪個端口接收的size_t data_len;sockaddr_in addr_to;socklen_t addr_to_len;int port_to;// 構造函數,默認初始化所有成員Packet() {reset();}// reset 函數,供復用時調用,清空所有數據和結構void reset() {memset(data, 0, sizeof(data));memset(&addr_from, 0, sizeof(addr_from));addr_from_len = 0;port_from = 0;data_len = 0;memset(&addr_to, 0, sizeof(addr_to));addr_to_len = 0;port_to = 0;}
};// 這個無鎖內存池,需要使用外部類支持
// 手動下載源碼(沒有官方包管理器直接安裝)
// git clone https://github.com/cameron314/concurrentqueue.git
// 
// 編譯指定頭文件目錄
// g++ -std=c++11 -pthread -I/path/to/concurrentqueue cssff.cpp -o udp_server
// 
// #define LOCK_IN_POOL 1
// class PacketPool {
//     moodycamel::ConcurrentQueue<Packet*> pool;
//     const size_t MAX_POOL_SIZE;
//     std::atomic<size_t> total_allocated;// public:
//     PacketPool(size_t max_pool = 10000) 
//         : MAX_POOL_SIZE(max_pool), total_allocated(0) {}//     ~PacketPool() {
//         Packet* pkt;
//         while (pool.try_dequeue(pkt)) {
//             delete pkt;
//         }
//     }//     Packet* acquire() {
//         Packet* pkt = nullptr;
//         if (pool.try_dequeue(pkt)) {
//             return pkt;
//         }
//         total_allocated.fetch_add(1, std::memory_order_relaxed);
//         return new Packet();
//     }//     void release(Packet* pkt) {
//         if (!pkt) return;//         size_t cached = pool.size_approx(); // 估算當前緩存大小
//         if (cached < MAX_POOL_SIZE) {
//             pool.enqueue(pkt);
//         } else {
//             delete pkt;
//             total_allocated.fetch_sub(1, std::memory_order_relaxed);
//         }
//     }//     size_t allocatedCount() const {
//         return total_allocated.load(std::memory_order_relaxed);
//     }//     // 估算緩存數(不是嚴格準確)
//     size_t cachedCount() const {
//         return pool.size_approx();
//     }
// };class PacketPool {std::vector<Packet*> pool;mutable std::mutex mtx;const size_t MAX_POOL_SIZE = 10000;public:~PacketPool() {for (auto pkt : pool) delete pkt;}Packet* acquire() {std::lock_guard<std::mutex> lock(mtx);if (!pool.empty()) {Packet* pkt = pool.back();pool.pop_back();pkt->reset();return pkt;}return new Packet();}void release(Packet* pkt) {if (pkt == nullptr) return;std::lock_guard<std::mutex> lock(mtx);if (pool.size() < MAX_POOL_SIZE) {pool.push_back(pkt);} else {delete pkt;}}size_t cachedCount() const {std::lock_guard<std::mutex> lock(mtx);return pool.size();}
};/////////////////////////////////////////////////////////////////////
// TODO: std::condition_variable 優化等待,暫時可能性能問題不大// 線程安全隊列
std::queue<Packet*> recv_queue_1;
std::queue<Packet*> send_queue_1;
// 枷鎖使用,或者配合那個條件變量使用
std::mutex recv_mutex_1;
std::mutex send_mutex_1;
// 使用條件變量來同步
std::condition_variable recv_cv_1;
std::condition_variable send_cv_1;
/*
notify_one 基本規則:
每調用一次 notify_one(),只會喚醒 一個正在等待在該 condition_variable 上的線程。
如果沒有線程在等待,這次調用 不會保存或累積信號,即后來的線程 wait() 依然會阻塞,直到下一次 notify_one() 或 notify_all()。
?? 注意:條件變量不是信號量,不會記住之前的“通知次數”。
?? 所以:謂詞一定要加上隊列不為空,否則,如果干活時候錯過了喚醒會造成一直等待信號的問題。
其中wait原理偽代碼:
function wait(unique_lock& lock, predicate pred):while not pred():                  // 先檢查謂詞// 1. 把 mutex 解鎖,讓其他線程可以修改共享狀態lock.unlock()// 2. 阻塞等待條件變量通知wait_for_notify()// 3. 被喚醒后,重新加鎖lock.lock()end while// 當 predicate() 返回 true 時,函數返回return*/// 全局的內存池子
PacketPool pool;const int MAX_READ_PER_FD = 10;// 1)這個線程負責從這2個端口接收數據,放到對應的隊列中;
void recv_thread(int sock1, int sock2) {int epfd = epoll_create1(0);if (epfd == -1) {perror("epoll_create1");return;}epoll_event ev1 = {0}, ev2 = {0};ev1.events = EPOLLIN;ev1.data.fd = sock1;epoll_ctl(epfd, EPOLL_CTL_ADD, sock1, &ev1);ev2.events = EPOLLIN;ev2.data.fd = sock2;epoll_ctl(epfd, EPOLL_CTL_ADD, sock2, &ev2);epoll_event events[MAX_EVENTS];while (true) {if (false == g_running){std::cout << "recv thread end here by signal " << std::endl;return;}int nfds = epoll_wait(epfd, events, MAX_EVENTS, 1000);if (nfds == -1) {if (errno == EINTR) continue; // 被信號中斷,可忽略std::cout << "recv thread end, epoll_wait err " << std::endl;perror("epoll_wait");break;  }for (int i = 0; i < nfds; ++i) {int sockfd = events[i].data.fd;// 嘗試讀多次,直到沒數據,提高效率; 但是也要防止一個fd上數據太多,造成其他的句柄餓死int read_count = 0;while (read_count < MAX_READ_PER_FD) {read_count ++;Packet* packet = pool.acquire();socklen_t addr_len = sizeof(packet->addr_from);ssize_t len = recvfrom(sockfd, packet->data, BUF_SIZE, 0,(sockaddr*)&packet->addr_from, &addr_len);if (len > 0) {packet->data_len = len;packet->addr_from_len = addr_len;packet->port_from = (sockfd == sock1) ? PORT1 : PORT2;// 轉換IP地址為字符串char ip_str[INET_ADDRSTRLEN];  // 存儲IPv4地址的緩沖區const char* ip_addr;// 判斷地址族(IPv4)if (packet->addr_from.sin_family == AF_INET) {// 轉換IPv4地址struct sockaddr_in* ipv4_addr = (struct sockaddr_in*)&packet->addr_from;ip_addr = inet_ntop(AF_INET, &(ipv4_addr->sin_addr), ip_str, INET_ADDRSTRLEN);}// 如需支持IPv6可添加以下代碼// else if (packet->addr_from.sa_family == AF_INET6) {//     struct sockaddr_in6* ipv6_addr = (struct sockaddr_in6*)&packet->addr_from;//     ip_addr = inet_ntop(AF_INET6, &(ipv6_addr->sin6_addr), ip_str, INET6_ADDRSTRLEN);// }else {ip_addr = "未知地址族";}// 放到同一個隊列中{std::lock_guard<std::mutex> lock(recv_mutex_1);recv_queue_1.push(packet);}recv_cv_1.notify_one();std::cout << "read data from port " << packet->port_from << " IP="<< ip_addr << std::endl;} // end if len > 0else {pool.release(packet);if (errno == EAGAIN || errno == EWOULDBLOCK) {// 緩沖區讀空了,退出循環break;} else {std::cerr << "read data error: " << strerror(errno) << std::endl;break;}}  // end of else  }// end 針對一個socket的循環讀取while } // end foreach(events)}// end of while(true)return;
}// 處理PORT1端口數據,echo 模擬
// 將要發送的數據放到隊列中
void process_port1(Packet * pkt){pkt->addr_to = pkt->addr_from;pkt->addr_to_len = pkt->addr_from_len;{std::lock_guard<std::mutex> send_lock(send_mutex_1);send_queue_1.push(pkt);}send_cv_1.notify_one();
}// 處理PORT1端口數據echo 模擬
void process_port2(Packet * pkt){pkt->addr_to = pkt->addr_from;pkt->addr_to_len = pkt->addr_from_len;{std::lock_guard<std::mutex> send_lock(send_mutex_1);send_queue_1.push(pkt);}send_cv_1.notify_one();
}// 處理收到數據的線程,這里只是簡單的拷貝到發送隊列中,做一個echo的邏輯
void process_thread() {while (true) {if (false == g_running){std::cout << "process thread end here by signal " << std::endl;return;}// 提高鎖的效率,一次性處理多個包,std::vector<Packet*> pkts1;// 使用條件變量等待{std::unique_lock<std::mutex> lock(recv_mutex_1);recv_cv_1.wait(lock, [] { return !recv_queue_1.empty() || !g_running; });// 喚醒后檢查一下是否退出if (!g_running.load() && recv_queue_1.empty()) return;size_t cnt = 0;while (!recv_queue_1.empty() && cnt < MAX_BATCH) { pkts1.push_back(recv_queue_1.front());recv_queue_1.pop();cnt++; }}// 處理這最多32個數據包,for (auto pkt : pkts1) {if (pkt->port_from == PORT1){process_port1(pkt);}else{process_port2(pkt);}}}// end of while 
}// 處理需要發送的數據
void send_thread(int sock1, int sock2) {while (true) {if (!g_running.load()) {std::cout << "send thread end here by signal " << std::endl;return;}std::vector<Packet*> pkts;// 批量取出發送隊列的數據{std::unique_lock<std::mutex> lock(send_mutex_1);// 這里的謂詞很重要,謂詞中一定要包含隊列不為空send_cv_1.wait(lock, []{return !send_queue_1.empty() || !g_running.load();});// 喚醒后檢查退出條件if (!g_running.load() && send_queue_1.empty())return;size_t cnt = 0;while (!send_queue_1.empty() && cnt < MAX_BATCH) {pkts.push_back(send_queue_1.front());send_queue_1.pop();cnt++;}}// 循環發送for (auto pkt : pkts) {if (pkt->port_from == PORT1) {sendto(sock1, pkt->data, pkt->data_len, 0, (sockaddr *)&pkt->addr_to, pkt->addr_to_len);} else {sendto(sock2, pkt->data, pkt->data_len, 0, (sockaddr *)&pkt->addr_to, pkt->addr_to_len);}pool.release(pkt);}}  // end of while
}// 設置為非阻塞模式端口
int set_nonblocking(int sockfd) {int flags = fcntl(sockfd, F_GETFL, 0);if (flags == -1) return -1;return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);
}// 打開2個端口進行監聽
int create_udp_socket(int port) {int sock = socket(AF_INET, SOCK_DGRAM, 0);if (sock < 0) {perror("socket create error");exit(1);}sockaddr_in addr{};addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY;addr.sin_port = htons(port);if (bind(sock, (sockaddr *)&addr, sizeof(addr)) < 0) {std::cout << "binding on UDP port " << port << " error " << std::endl;perror("bind error");exit(1);}return sock;
}// 讓線程檢測這個標志退出
void signal_handler(int signum) {if (signum == SIGINT) {std::cout << "\nSIGINT received. Exiting here!" << std::endl;g_running.store(false);send_cv_1.notify_all();recv_cv_1.notify_all();}
}sockaddr_in buildSockaddr(const std::string& ip, uint16_t port) {sockaddr_in addr;memset(&addr, 0, sizeof(addr));         // 清零結構體addr.sin_family = AF_INET;              // IPv4 協議addr.sin_port = htons(port);            // 端口轉網絡字節序inet_pton(AF_INET, ip.c_str(), &addr.sin_addr); // 將IP字符串轉為二進制return addr;
}
/// @brief 入口函數
/// @return 0
int main() {// 注冊 Ctrl+C 信號signal(SIGINT, signal_handler);int sock1 = create_udp_socket(PORT1);int sock2 = create_udp_socket(PORT2);// 在 create_udp_socket 后調用:set_nonblocking(sock1);set_nonblocking(sock2);std::cout << "Listening on UDP ports " << PORT1 << " and " << PORT2 << std::endl;std::thread t_recv(recv_thread, sock1, sock2);std::vector<std::thread> proc_threads, send_threads;for (int i = 0; i < NUM_PROC_THREADS; ++i)proc_threads.emplace_back(process_thread);for (int i = 0; i < NUM_SEND_THREADS; ++i)send_threads.emplace_back(send_thread, sock1, sock2);// 主線程等信號while (g_running) {std::this_thread::sleep_for(std::chrono::milliseconds(100));}// 等待線程退出if (t_recv.joinable())t_recv.join();// 等待處理線程退出for (auto& t : proc_threads) {if (t.joinable())t.join();}// 等待發送線程退出for (auto& t : send_threads) {if (t.joinable())t.join();}// 關閉資源close(sock1);close(sock2);std::cout << "ssff app  Clean shutdown complete." << std::endl;return 0;
}

編譯以后,

可以使用nc測試一下;

echo "hello port1" | nc -u 127.0.0.1 4000
echo "hello port2" | nc -u 127.0.0.1 5000

或者寫一個python程序發送代碼,可以跨主機試試:

import socket
import time
import argparse
import sys
import threadingdef udp_handler(local_ip, local_port, target_ip, target_port, count, interval, data):"""單個套接字處理發送和接收:- 綁定本地4000端口- 向目標4000端口發送數據- 在同一個端口接收回顯數據"""# 創建UDP套接字并綁定本地4001端口(發送和接收共用)sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)try:# 綁定本地端口(關鍵:發送和接收都用這個端口)sock.bind((local_ip, local_port))print(f"? 已綁定本地端口 {local_ip}:{local_port},開始發送和接收數據...")# 啟動接收線程(用同一個套接字)def receive_loop():while True:try:# 接收數據(阻塞等待)data_recv, addr = sock.recvfrom(1024)# 顯示接收內容try:message = data_recv.decode('utf-8')print(f"\n📥 收到來自 {addr} 的數據: {message}")except UnicodeDecodeError:print(f"\n📥 收到來自 {addr} 的數據(無法解碼): {data_recv.hex()}")except Exception as e:print(f"\n? 接收出錯: {str(e)}")break# 啟動接收線程receive_thread = threading.Thread(target=receive_loop, daemon=True)receive_thread.start()# 發送數據print(f"\n📤 開始向 {target_ip}:{target_port} 發送數據(共 {count} 個)...")for i in range(count):# 用已綁定的套接字發送(源端口固定為4001)sock.sendto(data.encode('utf-8'), (target_ip, target_port))# 顯示發送進度sys.stdout.write(f"\r已發送 {i+1}/{count} 個數據包")sys.stdout.flush()# 最后一個包不等待if i < count - 1:time.sleep(interval)print("\n\n? 發送完成!繼續等待接收數據(按Ctrl+C退出)...")# 保持程序運行,等待接收while True:time.sleep(1)except KeyboardInterrupt:print("\n\n?? 用戶中斷程序")except Exception as e:print(f"\n? 程序出錯: {str(e)}")finally:sock.close()print("\n🔌 套接字已關閉")if __name__ == "__main__":port = 5000parser = argparse.ArgumentParser(description='UDP雙向通信工具(固定本地4000端口)')parser.add_argument('--local-ip', type=str, default='0.0.0.0',help='本地綁定IP,默認0.0.0.0(所有網卡)')parser.add_argument('--local-port', type=int, default=port,help='本地綁定端口,默認4000')parser.add_argument('--target-ip', type=str, default='192.168.228.129',help='目標IP地址,默認虛擬機IP')parser.add_argument('--target-port', type=int, default=port,help='目標端口,默認4001')parser.add_argument('--count', type=int, default=10,help='發送數據包數量,默認10個')parser.add_argument('--interval', type=float, default=1.0,help='發送間隔(秒),默認1秒')parser.add_argument('--data', type=str, default='Hello UDP',help='發送的數據內容,默認"Hello UDP"')args = parser.parse_args()# 啟動UDP處理邏輯udp_handler(local_ip=args.local_ip,local_port=args.local_port,target_ip=args.target_ip,target_port=args.target_port,count=args.count,interval=args.interval,data=args.data)

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

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

相關文章

MySQL 深分頁優化與條件分頁:把 OFFSET 換成“游標”,再用覆蓋索引抄近路

MySQL 深分頁優化與條件分頁:把 OFFSET 換成“游標”,再用覆蓋索引抄近路 這不是“玄學調優”,而是可復制的方案。本文用可復現的 DDL/造數腳本,演示為什么 OFFSET 越大越慢,如何用 條件游標(Keyset Pagination) 替換它,并配上 覆蓋索引。還會教你看 EXPLAIN/EXPLAIN A…

Unity 繩子插件 ObjRope 使用簡記

Unity 繩子插件&#xff0c;是一個基于物理的、高度逼真且可交互的繩索模擬解決方案。 其性能良好&#xff0c;能夠運行在小游戲平臺。 一、插件基本 插件資源商店地址&#xff1a; Obi Rope | Physics | Unity Asset Store 官方文檔&#xff08;手冊&#xff09;&#xff…

demo 通訊錄 + 城市選擇器 (字母索引左右聯動 ListItemGroup+AlphabetIndexer)筆記

一、城市選擇器實現筆記1. 雙層 for 循環渲染數據結構interface BKCityContent {initial: string; // 字母索引cityNameList: string[]; // 城市列表 }核心實現// 外層循環&#xff1a;字母分組 - 遍歷城市數據&#xff0c;按字母分組顯示 ForEach(this.cityContentList, (item…

【總結型】c語言中的位運算

位運算包括 & | ^ ~ << >>按位與 將某些變量中的某些位清0同時保持其他位不變。也可以用來獲取變量中的某一位。 例如&#xff1a;將int型變量n低8位全置為0&#xff0c;其余位保持不變。 n n & 0xffffff00 如何判斷一個int型變量n的第七位。 n & 0x8…

如何在FastAPI中玩轉APScheduler,實現動態定時任務的魔法?

url: /posts/4fb9e30bb20956319c783e21897a667a/ title: 如何在FastAPI中玩轉APScheduler,實現動態定時任務的魔法? date: 2025-08-16T01:14:26+08:00 lastmod: 2025-08-16T01:14:26+08:00 author: cmdragon summary: APScheduler是Python中強大的任務調度庫,支持任務持久化…

GitHub的簡單使用方法----(5)

最后一篇簡單講講git管理遠程倉庫 1.目的 備份&#xff0c;實現代碼共享集中化管理 &#xff08;將本地倉庫同步到git遠程倉庫中&#xff09; git clone 倉庫地址 以下圖為示例&#xff0c;我打開了一個別人的項目倉庫&#xff0c;點擊code能看到倉庫地址 等待完成即可 如…

C++ STL-string類底層實現

摘要&#xff1a; 本文實現了一個簡易的string類&#xff0c;主要包含以下功能&#xff1a; 1. 默認成員函數&#xff1a;構造函數&#xff08;默認/參數化&#xff09;、拷貝構造、賦值重載和析構函數&#xff0c;采用深拷貝避免內存問題&#xff1b; 2. 迭代器支持&#xff1…

【LeetCode每日一題】

每日一題3. 無重復字符的最長子串題目總體思路代碼1.兩數之和題目總體思路代碼15. 三數之和題目總體思路代碼2025.8.153. 無重復字符的最長子串 題目 給定一個字符串 s &#xff0c;請你找出其中不含有重復字符的 最長 子串 的長度。 示例 1: 輸入: s “abcabcbb” 輸出: 3…

sharding-jdbc讀寫分離配置

一主兩從&#xff0c;爆紅是正常的&#xff0c;不知為啥 spring:shardingsphere:datasource:names: ds_master,ds_s1,ds_s2ds_master:type: com.zaxxer.hikari.HikariDataSourcedriverClassName: com.mysql.jdbc.DriverjdbcUrl: jdbc:mysql://192.168.135.100:3306/gmall_produ…

【大模型核心技術】Dify 入門教程

文章目錄一、Dify 是什么二、安裝與部署2.1 云端 SaaS 版&#xff08;快速入門&#xff09;2.2 私有化部署&#xff08;企業級方案&#xff09;三、界面導航與核心模塊3.1 控制臺概覽3.2 核心功能模塊詳解3.2.1 知識庫&#xff08;RAG 引擎&#xff09;3.2.2 工作流編排3.2.3 模…

homebrew 1

文章目錄brew(1) – macOS&#xff08;或 Linux&#xff09;上缺失的包管理器概要描述術語表基本命令install *formula*uninstall *formula*listsearch \[*text*|/*text*/]命令alias \[--edit] \[*alias*|*alias**command*]analytics \[*subcommand*]autoremove \[--dry-run]bu…

設計索引的原則有哪些?

MySQL 索引設計的核心原則是 在查詢性能與存儲成本之間取得平衡。以下是經過實踐驗證的 10 大設計原則及具體實現策略&#xff1a;一、基礎原則原則說明示例/反例1. 高頻查詢優先為 WHERE、JOIN、ORDER BY、GROUP BY 頻繁出現的列建索引? SELECT * FROM orders WHERE user_id1…

使用影刀RPA實現快遞信息抓取

最近公司項目有個需求&#xff0c;要求抓取快遞單號快遞信息&#xff0c;比如簽收地點、簽收日期等。該項目對應的快遞查詢網站是一個國外的網站&#xff0c;他們有專門的快遞平臺可以用于查詢。該平臺提供了快遞接口進行查詢&#xff0c;但需要付費。同時也提供了免費的查詢窗…

蟻劍--安裝、使用

用途限制聲明&#xff0c;本文僅用于網絡安全技術研究、教育與知識分享。文中涉及的滲透測試方法與工具&#xff0c;嚴禁用于未經授權的網絡攻擊、數據竊取或任何違法活動。任何因不當使用本文內容導致的法律后果&#xff0c;作者及發布平臺不承擔任何責任。滲透測試涉及復雜技…

Varjo XR虛擬現實軍用車輛駕駛與操作培訓

Patria基于混合現實的模擬器提供了根據現代車輛乘員需求定制的培訓&#xff0c;與傳統顯示設置相比&#xff0c;全新的模擬解決方案具有更好的沉浸感和更小的物理空間需求。Patria是芬蘭領先的國防、安全和航空解決方案提供商。提供尖端技術和全面的培訓系統&#xff0c;以支持…

Java 10 新特性及具體應用

目錄 1. 局部變量類型推斷&#xff08;JEP 286&#xff09; 2. 不可修改集合&#xff08;JEP 269&#xff09; 3. 并行全垃圾回收&#xff08;JEP 307&#xff09; 4. 應用類數據共享&#xff08;JEP 310&#xff09; 5. 線程局部管控&#xff08;JEP 312&#xff09; 總結…

【力扣 Hot100】刷題日記

D8 全排列(非回溯法) 全排列原題鏈接 在刷leetcode的時候&#xff0c;看到這道題目并沒法使用像STL的next_permutation方法&#xff0c;感嘆C便利的同時&#xff0c;又惋惜Java并沒有類似的API&#xff0c;那我們只能從原理入手了&#xff0c;仿寫此算法。 其實回溯法更應該…

JetPack系列教程(七):Palette——讓你的APP色彩“飛”起來!

JetPack系列教程&#xff08;七&#xff09;&#xff1a;Palette——讓你的APP色彩“飛”起來&#xff01; 各位開發小伙伴們&#xff0c;還在為APP的配色發愁嗎&#xff1f;別擔心&#xff0c;今天咱們就來聊聊JetPack家族里的“色彩魔法師”——Palette&#xff01;這個神奇的…

力扣hot100 | 矩陣 | 73. 矩陣置零、54. 螺旋矩陣、48. 旋轉圖像、240. 搜索二維矩陣 II

73. 矩陣置零 力扣題目鏈接 給定一個 m x n 的矩陣&#xff0c;如果一個元素為 0 &#xff0c;則將其所在行和列的所有元素都設為 0 。請使用 原地 算法。 示例 1&#xff1a; 輸入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 輸出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]…

ARC與eARC是什么?主要用在哪?

在家庭影音設備不斷升級的今天&#xff0c;人們對音視頻體驗的要求越來越高。無論是追劇、玩游戲還是觀看電影大片&#xff0c;很多用戶不再滿足于電視自帶的揚聲器&#xff0c;而是希望借助回音壁、功放或家庭影院系統&#xff0c;獲得更加震撼的沉浸式聲音體驗。一、ARC是什么…