04.基于C++實現多線程TCP服務器與客戶端通信

基于C++實現多線程TCP服務器與客戶端通信

目錄

  • 一、項目背景與目標
  • 二、從零開始理解網絡通信
  • 三、相關技術背景知識
    • 1. 守護進程(Daemon Process)
    • 2. 線程池(Thread Pool)
    • 3. RAII設計模式
  • 四、項目整體結構與邏輯
  • 五、核心模塊詳細分析
    • 1. TCP服務器模塊
    • 2. 線程池模塊
    • 3. 任務處理模塊
    • 4. 日志模塊
    • 5. 守護進程模塊
    • 6. 鎖管理模塊
  • 六、從實踐到理論:關鍵設計模式與技術
  • 七、進階主題與擴展思考
  • 八、總結與展望

一、項目背景與目標

在網絡編程中,TCP協議因其可靠性和穩定性被廣泛應用于各類網絡服務。本項目使用C++語言,基于Linux平臺實現了一個完整的TCP服務器與客戶端通信程序,服務器端采用了線程池技術實現高效并發處理,支持守護進程運行,并實現了完整的日志系統。

本項目的目標是:

  • 掌握TCP協議的基本編程方法
  • 掌握線程池的設計與實現
  • 學習守護進程的創建與管理
  • 掌握日志系統的設計與實現
  • 理解RAII設計模式在資源管理中的應用

二、從零開始理解網絡通信

網絡通信的本質

想象一下,當你給朋友發送一條短信時,這條信息是如何從你的手機傳遞到朋友的手機的?這個過程涉及:

  1. 你的手機將信息編碼
  2. 通過無線信號發送到基站
  3. 基站將信息路由到目標手機
  4. 目標手機接收并解碼信息

計算機網絡通信也遵循類似的原理,只是更加復雜和規范化。TCP/IP協議就像是計算機之間溝通的"語言規則",確保信息能夠正確傳遞。

套接字(Socket):網絡通信的基礎

套接字可以理解為網絡通信的"插座",就像家里的電源插座連接電器一樣,套接字連接網絡中的應用程序。

應用程序 <---> 套接字 <---> 網絡 <---> 套接字 <---> 應用程序

在我們的項目中:

// 創建套接字
_sock = socket(AF_INET, SOCK_STREAM, 0);

這行代碼就像是安裝了一個"網絡插座",其中:

  • AF_INET表示使用IPv4地址
  • SOCK_STREAM表示使用TCP協議
  • 0表示使用默認協議

三、相關技術背景知識

1. 守護進程(Daemon Process):服務器的"隱形模式"

想象一下,如果你的手機應用必須保持前臺運行才能接收消息,那將是多么不便!守護進程就像是手機的"后臺應用",即使你關閉了終端窗口,它仍然在默默工作。

守護進程的創建過程可以類比為一個員工的"獨立":

  1. 創建子進程并退出父進程:就像員工從公司分離出來成立自己的工作室
  2. 創建新會話:員工不再接受原公司的直接管理
  3. 重定向輸入輸出:員工建立了自己的溝通渠道
  4. 更改工作目錄:員工搬到了新的辦公地點
// 創建守護進程的關鍵步驟
if (fork() > 0) exit(0);  // 父進程退出
pid_t n = setsid();       // 創建新會話

2. 線程池(Thread Pool):高效的"工作團隊"

想象一家餐廳:

  • 如果每來一位客人就雇傭一名新服務員,成本會非常高
  • 如果只有一名服務員,客人可能需要長時間等待
  • 最佳方案是維持一個固定數量的服務員團隊,隨時準備服務新客人

線程池就是這樣的"服務員團隊":

  • 預先創建多個線程,等待任務分配
  • 當新任務到來時,從線程池中分配一個空閑線程處理
  • 任務完成后,線程返回池中等待下一個任務
// 線程池的核心:等待并處理任務
while (true) {T t;{LockGuard lockguard(td->threadpool->mutex());while (td->threadpool->isQueueEmpty()) {td->threadpool->threadWait();  // 等待新任務}t = td->threadpool->pop();  // 獲取任務}t();  // 執行任務
}

3. RAII(Resource Acquisition Is Initialization):智能資源管理

RAII就像是一個自動化的"資源管家"。想象你去圖書館:

  • 進門時,你借了一本書(獲取資源)
  • 離開時,你必須歸還這本書(釋放資源)
  • 如果你忘記歸還,圖書館會有麻煩

RAII確保:

  • 當你"進門"(創建對象)時,自動借書(獲取資源)
  • 當你"離開"(對象銷毀)時,自動還書(釋放資源)
  • 即使發生意外(如異常),也能確保書被歸還
// RAII的典型應用:自動管理鎖
{LockGuard lockguard(&_mutex);  // 構造時自動加鎖_task_queue.push(in);pthread_cond_signal(&_cond);
}  // 離開作用域時自動解鎖

四、項目整體結構與邏輯

項目模塊關系圖

                  +-------------+| tcpServer.cc|+------+------+|v
+----------+      +------+-------+      +-----------+
| daemon.hpp|<-----| tcpServer.hpp|----->|  Task.hpp |
+----------+      +------+-------+      +-----+-----+|                    |v                    v+------+-------+     +------+------+|ThreadPool.hpp|<----|serviceIO()  |+------+-------+     +-------------+|v+------+-------+|  Thread.hpp  |+------+-------+|v+------+-------+| LockGuard.hpp|+-------------+

項目整體運行流程

想象一個餐廳的運作流程:

  1. 餐廳開業(服務器啟動):

    • 準備場地(創建套接字)
    • 掛出營業牌(綁定端口)
    • 組建服務團隊(初始化線程池)
    • 開始迎接客人(監聽連接)
  2. 客人到來(客戶端連接):

    • 服務員引導入座(accept接受連接)
    • 分配一名服務員(從線程池分配線程)
    • 開始點餐服務(處理客戶端請求)
  3. 服務過程(數據交換):

    • 客人點餐(客戶端發送數據)
    • 服務員記錄并確認(服務器處理并回應)
    • 上菜(服務器返回結果)
  4. 就餐結束(連接關閉):

    • 客人離開(客戶端斷開連接)
    • 服務員清理桌面(關閉socket)
    • 準備服務下一位客人(線程返回池中)

五、核心模塊詳細分析

1. TCP服務器模塊 (tcpServer.hpptcpServer.cc)

設計思路:建立通信的"橋梁"

TCP服務器就像是一個電話總機,負責接聽來電并將其轉接給合適的接線員。其主要職責是:

  • 創建通信渠道(套接字)
  • 公布聯系方式(綁定地址和端口)
  • 等待來電(監聽連接)
  • 接聽并轉接(接受連接并提交給線程池)
關鍵代碼解析
void initServer() {// 1. 創建通信渠道_listensock = socket(AF_INET, SOCK_STREAM, 0);// 2. 綁定地址和端口(公布聯系方式)struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = INADDR_ANY;bind(_listensock, (struct sockaddr *)&local, sizeof(local));// 3. 開始監聽(等待來電)listen(_listensock, gbacklog);
}void start() {// 4. 準備接線員團隊(初始化線程池)ThreadPool<Task>::getInstance()->run();for (;;) {// 5. 接聽來電struct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(_listensock, (struct sockaddr *)&peer, &len);// 6. 轉接給接線員(提交任務到線程池)ThreadPool<Task>::getInstance()->push(Task(sock, serviceIO));}
}
實現要點與技巧
  • 錯誤處理的重要性:網絡編程中,各種意外情況都可能發生(端口被占用、連接突然斷開等)。良好的錯誤處理能讓程序更加健壯。

  • 為什么使用INADDR_ANY:使用INADDR_ANY(值為0.0.0.0)允許服務器監聽所有網絡接口,無論客戶端從哪個網卡連接都能接受。

  • backlog參數的意義listen(_listensock, gbacklog)中的gbacklog表示等待連接隊列的最大長度。當連接請求過多時,超過這個值的連接會被拒絕。

2. 線程池模塊 (ThreadPool.hppThread.hpp)

設計思路:高效的"工作團隊"

線程池就像一個高效的工作團隊:

  • 預先組建團隊(創建線程)
  • 統一分配任務(任務隊列)
  • 團隊成員互相協作(線程同步)
  • 避免重復招聘(線程復用)
關鍵代碼解析
// 線程的工作循環
static void *handlerTask(void *args) {ThreadData<T> *td = (ThreadData<T> *)args;while (true) {T t;{// 1. 等待任務分配LockGuard lockguard(td->threadpool->mutex());while (td->threadpool->isQueueEmpty()) {td->threadpool->threadWait();  // 沒有任務時等待}// 2. 領取任務t = td->threadpool->pop();}// 3. 執行任務t();}return nullptr;
}// 添加新任務
void push(const T &in) {// 1. 鎖定任務隊列LockGuard lockguard(&_mutex);// 2. 添加任務_task_queue.push(in);// 3. 通知等待的線程pthread_cond_signal(&_cond);
}
實現要點與技巧
  • 為什么使用條件變量:條件變量允許線程在特定條件滿足前進入睡眠狀態,避免了忙等待(不斷檢查條件)帶來的CPU資源浪費。

  • 單例模式的優勢:整個程序只需要一個線程池實例,單例模式確保了資源的統一管理,避免了重復創建帶來的開銷。

  • 雙重檢查鎖定:在getInstance()方法中使用雙重檢查鎖定,既保證了線程安全,又避免了每次獲取實例都加鎖帶來的性能損失。

  • 模板設計的靈活性:使用模板設計線程池,使其能夠處理不同類型的任務,提高了代碼的復用性。

3. 任務處理模塊 (Task.hpp)

設計思路:統一的任務接口

任務處理模塊就像是一個標準化的"工作指南":

  • 定義了工作內容(處理客戶端連接)
  • 提供了統一的執行方式(operator())
  • 封裝了具體實現細節(回調函數)
關鍵代碼解析
// 具體的任務處理函數
void serviceIO(int sock) {char buffer[1024];while (true) {// 1. 接收客戶端數據ssize_t n = read(sock, buffer, sizeof(buffer) - 1);if (n > 0) {// 2. 處理數據buffer[n] = 0;std::cout << "recv message: " << buffer << std::endl;// 3. 準備響應std::string outbuffer = buffer;outbuffer += " server[echo]";// 4. 發送響應write(sock, outbuffer.c_str(), outbuffer.size());}else if (n == 0) {// 5. 客戶端斷開連接logMessage(NORMAL, "client quit, me too!");break;}}// 6. 關閉連接close(sock);
}// 任務封裝類
class Task {using func_t = std::function<void(int)>;public:Task(int sock, func_t func): _sock(sock), _callback(func) {}// 統一的任務執行接口void operator()() {_callback(_sock);}private:int _sock;func_t _callback;
};
實現要點與技巧
  • 為什么使用std::functionstd::function提供了一種類型安全的函數封裝,可以存儲、復制和調用任何可調用目標(函數、lambda表達式、函數對象等)。

  • 為什么重載operator():重載operator()使Task對象可以像函數一樣被調用,符合線程池對任務的要求,同時提供了更清晰的接口。

  • read/write vs recv/send:本項目使用read/write而非recv/send,因為前者更符合Unix “一切皆文件” 的哲學,可以統一處理文件、管道、套接字等I/O操作。

  • 為什么接收用char[]而發送用string

    • 接收數據時使用固定大小的char[]緩沖區,可以直接與系統調用配合,避免動態內存分配
    • 發送數據時使用string,便于字符串操作(如拼接)
    • 最后通過c_str()轉換回C風格字符串進行發送

4. 日志模塊 (log.hpp)

設計思路:系統的"黑匣子"

日志系統就像飛機的黑匣子,記錄系統運行的各種狀態和事件:

  • 不同級別的日志(從調試信息到致命錯誤)
  • 詳細的時間和上下文信息
  • 持久化存儲,便于后期分析
關鍵代碼解析
void logMessage(int level, const char *format, ...) {// 1. 構建日志前綴char logprefix[NUM];snprintf(logprefix, sizeof(logprefix), "[%s][%ld][pid: %d]",to_levelstr(level), (long int)time(nullptr), getpid());// 2. 處理可變參數char logcontent[NUM];va_list arg;va_start(arg, format);vsnprintf(logcontent, sizeof(logcontent), format, arg);va_end(arg);// 3. 選擇日志文件FILE *log = fopen(LOG_NORMAL, "a");FILE *err = fopen(LOG_ERR, "a");if(log != nullptr && err != nullptr) {FILE *curr = nullptr;if(level <= WARNING) curr = log;else curr = err;// 4. 寫入日志if(curr) fprintf(curr, "%s%s\n", logprefix, logcontent);fclose(log);fclose(err);}
}
實現要點與技巧
  • 可變參數的處理:使用va_listva_startva_endvsnprintf處理可變參數,實現了類似printf的靈活接口。

  • 日志分級的意義

    • DEBUG:詳細的調試信息,幫助開發者理解程序流程
    • NORMAL:正常操作信息,記錄系統的正常活動
    • WARNING:警告信息,表示可能的問題但不影響主要功能
    • ERROR:錯誤信息,表示功能受到影響但系統仍能運行
    • FATAL:致命錯誤,表示系統無法繼續運行
  • 為什么分文件存儲:將普通日志和錯誤日志分開存儲,便于快速定位問題,同時避免重要的錯誤信息被大量普通日志淹沒。

  • 時間戳和進程ID:記錄時間戳和進程ID,便于在多進程環境下追蹤問題,確定事件發生的順序。

5. 守護進程模塊 (daemon.hpp)

設計思路:服務器的"隱形模式"

守護進程就像是系統的"隱形服務員":

  • 脫離用戶控制(不依賴終端)
  • 在后臺默默工作(不顯示輸出)
  • 長期穩定運行(不受用戶登錄狀態影響)
關鍵代碼解析
void daemonSelf(const char *currPath = nullptr) {// 1. 忽略管道破裂信號signal(SIGPIPE, SIG_IGN);// 2. 創建子進程,父進程退出if (fork() > 0)exit(0);// 3. 創建新會話,脫離控制終端pid_t n = setsid();assert(n != -1);// 4. 重定向標準輸入輸出int fd = open(DEV, O_RDWR);if(fd >= 0) {dup2(fd, 0);  // 標準輸入dup2(fd, 1);  // 標準輸出dup2(fd, 2);  // 標準錯誤close(fd);}// 5. 更改工作目錄if(currPath) chdir(currPath);
}
實現要點與技巧
  • 為什么忽略SIGPIPE信號:當寫入一個已關閉的管道或套接字時,系統會發送SIGPIPE信號,默認處理是終止進程。忽略此信號可以防止服務器因客戶端異常斷開而崩潰。

  • 為什么使用fork():使用fork()創建子進程,然后父進程退出,使子進程成為孤兒進程,被init進程收養,從而脫離原來的控制終端。

  • setsid()的作用setsid()創建一個新的會話,使進程成為會話首進程,沒有控制終端,不會接收終端相關的信號。

  • 為什么重定向到/dev/null:重定向標準輸入輸出到/dev/null,確保進程不會因為讀寫終端而阻塞,同時避免輸出信息干擾系統運行。

6. 鎖管理模塊 (LockGuard.hpp)

設計思路:自動化的"資源管家"

鎖管理模塊就像是一個自動化的門禁系統:

  • 進入區域時自動上鎖(構造函數中加鎖)
  • 離開區域時自動解鎖(析構函數中解鎖)
  • 確保資源安全,避免沖突(線程安全)
關鍵代碼解析
class Mutex {
public:Mutex(pthread_mutex_t *lock_p = nullptr): lock_p_(lock_p) {}void lock() {if(lock_p_) pthread_mutex_lock(lock_p_);}void unlock() {if(lock_p_) pthread_mutex_unlock(lock_p_);}private:pthread_mutex_t *lock_p_;
};class LockGuard {
public:LockGuard(pthread_mutex_t *mutex): mutex_(mutex) {mutex_.lock();  // 構造時自動加鎖}~LockGuard() {mutex_.unlock();  // 析構時自動解鎖}private:Mutex mutex_;
};
實現要點與技巧
  • RAII的優勢:使用RAII模式管理鎖資源,無需手動解鎖,即使發生異常也能確保鎖被釋放,避免死鎖。

  • 分離Mutex和LockGuard:將Mutex和LockGuard分開實現,提高了代碼的復用性和靈活性。Mutex封裝了底層鎖操作,LockGuard提供了RAII風格的接口。

  • 空指針檢查:在lock()和unlock()方法中檢查指針是否為空,提高了代碼的健壯性,避免空指針異常。

  • 使用示例

    {LockGuard guard(&mutex);  // 進入作用域,自動加鎖// 臨界區代碼...
    }  // 離開作用域,自動解鎖
    

六、從實踐到理論:關鍵設計模式與技術

1. 單例模式(Singleton Pattern)

定義:確保一個類只有一個實例,并提供一個全局訪問點。

應用:線程池使用單例模式,確保整個程序只有一個線程池實例。

優勢

  • 節約系統資源,避免重復創建
  • 提供全局訪問點,方便使用
  • 確保所有組件使用同一個實例

實現

static ThreadPool<T> *getInstance() {if (nullptr == tp) {_singlock.lock();if (nullptr == tp) {tp = new ThreadPool<T>();}_singlock.unlock();}return tp;
}

2. 觀察者模式(Observer Pattern)的變體

定義:定義對象間的一種一對多依賴關系,使得當一個對象狀態改變時,所有依賴于它的對象都會得到通知。

應用:線程池中的條件變量機制實際上是觀察者模式的一種變體。

優勢

  • 解耦了任務生產者和消費者
  • 支持一對多的通知機制
  • 提高了系統的靈活性

實現

// 生產者(通知者)
void push(const T &in) {LockGuard lockguard(&_mutex);_task_queue.push(in);pthread_cond_signal(&_cond);  // 通知等待的線程
}// 消費者(觀察者)
while (td->threadpool->isQueueEmpty()) {td->threadpool->threadWait();  // 等待通知
}

3. 工廠方法模式(Factory Method Pattern)

定義:定義一個創建對象的接口,但由子類決定要實例化的類是哪一個。

應用:Task類使用了工廠方法模式的思想,通過回調函數創建不同的任務處理邏輯。

優勢

  • 將對象的創建與使用分離
  • 支持擴展,可以輕松添加新的任務類型
  • 提高了代碼的可維護性

實現

Task(int sock, func_t func): _sock(sock), _callback(func) {}void operator()() {_callback(_sock);  // 調用工廠方法創建的處理邏輯
}

七、進階主題與擴展思考

1. 性能優化

連接池:除了線程池,還可以實現連接池,預先建立和維護一組數據庫連接,避免頻繁創建和銷毀連接的開銷。

零拷貝技術:使用sendfile()等系統調用,減少數據在內核空間和用戶空間之間的拷貝,提高文件傳輸效率。

事件驅動模型:使用epollkqueue等I/O多路復用技術,實現高效的事件驅動模型,支持更多并發連接。

2. 安全性考慮

輸入驗證:對客戶端輸入進行嚴格驗證,防止緩沖區溢出、SQL注入等攻擊。

加密通信:實現SSL/TLS加密,保護數據傳輸安全。

資源限制:對連接數、請求頻率等進行限制,防止DoS攻擊。

3. 可擴展性設計

插件系統:設計插件接口,支持動態加載功能模塊。

配置中心:實現集中式配置管理,支持動態配置更新。

服務發現:集成服務發現機制,支持分布式部署。

八、總結與展望

本項目實現了一個完整的TCP服務器與客戶端通信系統,涵蓋了網絡編程、多線程編程、線程池、守護進程、日志系統等多個核心知識點。通過模塊化設計和面向對象編程,我們構建了一個結構清晰、功能完善的網絡服務框架。

從這個項目出發,你可以進一步探索:

  • 實現HTTP/WebSocket等應用層協議
  • 集成數據庫訪問功能
  • 實現負載均衡和高可用設計
  • 探索異步I/O和協程技術

網絡編程是現代軟件開發的基礎技能,希望這個項目能夠幫助你打開網絡編程的大門,為你的技術成長提供堅實的基礎。

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

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

相關文章

從0到1入門Linux

一、常用命令 ls 列出目錄內容 cd切換目錄mkdir創建新目錄rm刪除文件或目錄cp復制文件或目錄mv移動或重命名文件和目錄cat查看文件內容grep在文件中查找指定字符串ps查看當前進程狀態top查看內存kill終止進程df -h查看磁盤空間存儲情況iotop -o直接查看比較高的磁盤讀寫程序up…

Nginx負載均衡配置詳解:輕松實現高可用與高性能

在現代Web應用中&#xff0c;負載均衡是確保系統高可用性和高性能的關鍵技術之一。Nginx作為一款高性能的HTTP服務器和反向代理服務器&#xff0c;其負載均衡功能被廣泛應用于各種場景。本文將詳細介紹如何使用Nginx實現負載均衡配置&#xff0c;幫助開發者輕松應對高并發和大流…

使用chroot預安裝軟件到ubuntu22中

1、安裝依賴 # 安裝依賴工具 sudo apt update && sudo apt install -y \ squashfs-tools \ genisoimage \ xorriso \ isolinux \ syslinux-utils \ p7zip-full sudo apt update sudo apt install grub-pc-bin grub-efi-amd64-bin -y # 創建工作目錄 mkdir -p ./custom-…

php代碼審計工具-rips

代碼審計 代碼審計就是檢查所寫的代碼中是否有漏洞&#xff0c;檢查程序的源代碼是否有權限從而被黑客攻擊&#xff0c;同時也檢查了書寫的代碼是否規范。通過自動化的審查和人工審查的方式&#xff0c;逐行檢查源代碼&#xff0c;發現源代碼中安全缺陷所造成的漏洞&#xff0…

Docker參數,以及倉庫搭建

一。Docker的構建參數 注釋&#xff1a; 1.對于CMD&#xff0c;如果不想顯示&#xff0c;而是使用交互界面&#xff1a;docker run -ti --rm --name test2 busybox:v5 sh 2.對于CMD&#xff0c;一個交互界面只可以使用一個&#xff0c;如果想多次使用CMD&#xff0c;則用ENTR…

基于Python Django的人臉識別上課考勤系統(附源碼,部署)

博主介紹&#xff1a;?程序員徐師兄、7年大廠程序員經歷。全網粉絲12w、csdn博客專家、掘金/華為云/阿里云/InfoQ等平臺優質作者、專注于Java技術領域和畢業項目實戰? &#x1f345;文末獲取源碼聯系&#x1f345; &#x1f447;&#x1f3fb; 精彩專欄推薦訂閱&#x1f447;…

基于python實現的疫情數據可視化分析系統

基于python實現的疫情數據可視化分析系統 開發語言:Python 數據庫&#xff1a;MySQL所用到的知識&#xff1a;Django框架工具&#xff1a;pycharm、Navicat 系統功能實現 總體設計 系統實現 系統功能模塊 系統首頁可以查看首頁、疫情信息、核酸檢測、新聞資訊、個人中心、后…

(十 八)趣學設計模式 之 觀察者模式!

目錄 一、 啥是觀察者模式&#xff1f;二、 為什么要用觀察者模式&#xff1f;三、 觀察者模式的實現方式四、 觀察者模式的優缺點五、 觀察者模式的應用場景六、 總結 &#x1f31f;我的其他文章也講解的比較有趣&#x1f601;&#xff0c;如果喜歡博主的講解方式&#xff0c;…

Spring Boot 緩存最佳實踐:從基礎到生產的完整指南

Spring Boot 緩存最佳實踐&#xff1a;從基礎到生產的完整指南 引言 在現代分布式系統中&#xff0c;緩存是提升系統性能的銀彈。Spring Boot 通過 spring-boot-starter-cache? 模塊提供了開箱即用的緩存抽象&#xff0c;但如何根據業務需求實現靈活、可靠的緩存方案&#xf…

蘋果Siri升級遇阻,國行iPhone或將引入阿里、百度AI自救

AI整合進展緩慢 蘋果正加速將生成式AI技術整合至Siri&#xff0c;但內部消息稱其底層技術研發落后于競爭對手&#xff0c;進展未達預期。 國行iPhone將引入雙AI模型 蘋果計劃在2025年中期為國行iPhone引入AI功能&#xff0c;目前已敲定與 阿里巴巴、百度 合作&#xff0c;用戶…

阿里推出全新推理模型(因果語言模型),僅1/20參數媲美DeepSeek R1

阿里Qwen 團隊正式發布了他們最新的研究成果——QwQ-32B大語言模型&#xff01;這款模型不僅名字萌萌噠(QwQ)&#xff0c;實力更是不容小覷&#xff01;&#x1f60e; QwQ-32B 已在 Hugging Face 和 ModelScope 開源&#xff0c;采用了 Apache 2.0 開源協議。大家可通過 Qwen C…

TomcatServlet

https://www.bilibili.com/video/BV1UN411x7xe tomcat tomcat 架構圖&#xff0c;與 jre&#xff0c;應用程序之前的關系 安裝使用 tomcat 10 開始&#xff0c;api 從 javax.* 轉為使用 jakarta.*&#xff0c;需要至少使用 jdk 11 cmd 中默認 gbk 編碼&#xff0c;解決控制…

JDK ZOOKEEPER KAFKA安裝

JDK17下載安裝 mkdir -p /usr/local/develop cd /usr/local/develop 將下載的包上傳服務器指定路徑 解壓文件 tar -zxvf jdk-17.0.14_linux-x64_bin.tar.gz -C /usr/local/develop/ 修改文件夾名 mv /usr/local/develop/jdk-17.0.14 /usr/local/develop/java17 配置環境變量…

高考數學。。。

2024上 具體來說&#xff0c;直線的參數方程可以寫為&#xff1a; x1t y?t z1t 二、簡答題(本大題共5小題&#xff0c;每小題7分&#xff0c;共35分。) 12.數學學習評價不僅要關注結果評價&#xff0c;也要關注過程評價。簡要說明過程評價應關注哪幾個方面。…

C# 實現鼠標軌跡錄制與回放自動化功能(附源碼)

在軟件自動化測試或者重復性辦公任務中&#xff0c;鼠標操作的自動化可以大大減少人工干預&#xff0c;提高工作效率。這里將詳細介紹如何使用 C# 實現鼠標軌跡的錄制與回放功能&#xff0c;代碼結構清晰&#xff0c;具有較強的擴展性。 引用 NuGet 包 在開發這個功能時&…

Nacos 核心功能實戰筆記(超詳細)

Nacos 核心功能實戰筆記 一、Nacos 簡介 1. 是什么&#xff1f; 全稱&#xff1a;Nacos Naming and Configuration Service定位&#xff1a;阿里巴巴開源的 動態服務發現、配置管理、服務管理平臺核心功能&#xff1a;服務注冊與發現 統一配置管理 服務健康監測適用場景&…

安裝remixd,在VScode創建hardhat

在終端&#xff0c;以管理員身份&#xff0c;cmd 需要科學上網 npm install -g remix-project/remixd 在vscode插件中&#xff0c;安裝solidity插件&#xff0c;是暗灰色那款 1.將nodeJs的版本升級至18以上 2.在vscode打開一個新的文件&#xff0c;在終端輸入 npx hardhat 3.…

unity pico開發 四 物體交互 抓取 交互層級

文章目錄 手部設置物體交互物體抓取添加抓取抓取三種類型抓取點偏移抓取事件抓取時不讓物體吸附到手部 射線抓取交互層級 手部設置 為手部&#xff08;LeftHandController&#xff09;添加XRDirInteractor腳本 并添加一個球形碰撞盒&#xff0c;勾選isTrigger,調整大小為0.1 …

CyberRT(apollo) 定時器模塊簡述及bug分析

timer 模塊 timer的定義&#xff0c;cyberrt中timer模塊用于設置定時器任務&#xff0c;字面意思&#xff0c;設置設置定時周期及出發頻次&#xff08;周期 or oneshot)&#xff0c;到達指定時間時間觸發callback time wheel 時鐘節拍輪&#xff0c;常見的定時器設計&#x…

java八股文之消息中間件

1.RabbitMQ如何保證消息不丟失 開啟生產者確認機制&#xff0c;確保生產者的消息能到達隊列開啟持久化功能&#xff0c;確保消息未消費前在隊列中不會丟失&#xff08;交換機&#xff0c;隊列&#xff0c;消息都需要開啟持久化功能&#xff09;開啟消費者確認機制為auto,由spr…