accept函數及示例

這次我們介紹 accept 函數,它是 TCP 服務器用來接受客戶端連接請求的核心系統調用。


1. 函數介紹

accept 是一個 Linux 系統調用,專門用于TCP 服務器(使用 SOCK_STREAM 套接字)。它的主要功能是從監聽套接字(通過 listen 設置的套接字)的未決連接隊列(pending connection queue)中取出第一個連接請求,并為這個新連接創建一個全新的、獨立的套接字文件描述符

你可以把 accept 想象成總機接線員

  1. 有很多電話(客戶端連接請求)打進來,響鈴并排隊在總機(監聽套接字)那里。
  2. 接線員(accept 調用)拿起一個響鈴的電話。
  3. 接線員把這條線路接到一個新的、專用的電話線(新的套接字文件描述符)上。
  4. 接線員可以繼續去接下一個電話(下一次 accept 調用),而第一個通話(與第一個客戶端的通信)則通過那條專用線路進行,互不干擾。

這個新創建的套接字文件描述符專門用于與那一個特定的客戶端進行雙向數據通信。原始的監聽套接字則繼續保持監聽狀態,等待并接受更多的連接請求。


2. 函數原型

#include <sys/socket.h> // 必需// 標準形式
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);// 帶有標志的變體 (Linux 2.6.28+)
int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags);

3. 功能

  • 從隊列中取出連接: 從監聽套接字 sockfd 維護的未決連接隊列中提取第一個已完成或正在完成的連接請求。
  • 創建新套接字: 為這個新連接創建一個新的、非監聽狀態的套接字文件描述符。
  • 返回通信端點: 返回這個新的套接字文件描述符,服務器程序可以使用它來與特定的客戶端進行數據交換(read/write)。
  • 獲取客戶端信息: 如果 addraddrlen 參數不為 NULL,則將連接到服務器的客戶端的地址信息(IP 地址和端口號)填充到 addr 指向的緩沖區中。

4. 參數

  • int sockfd: 這是監聽套接字的文件描述符。它必須是:

    1. 通過 socket() 成功創建的。
    2. 通過 bind() 綁定了本地地址(IP 和端口)的。
    3. 通過 listen() 進入監聽狀態的。
  • struct sockaddr *addr: 這是一個指向套接字地址結構的指針,用于接收客戶端的地址信息。

    • 如果你不關心客戶端是誰,可以傳入 NULL
    • 如果傳入非 NULL 值,則它通常指向一個 struct sockaddr_in (IPv4) 或 struct sockaddr_in6 (IPv6) 類型的變量。
    • 該結構體在 accept 返回后會被填入客戶端的地址信息。
  • socklen_t *addrlen: 這是一個指向 socklen_t 類型變量的指針。

    • 輸入: 在調用 accept 時,這個變量必須被初始化為 addr 指向的緩沖區的大小(以字節為單位)。例如,如果 addr 指向 struct sockaddr_in,則 *addrlen 應初始化為 sizeof(struct sockaddr_in)
    • 輸出: accept 返回時,這個變量會被更新為實際存儲在 addr 中的地址結構的大小。這對于處理不同大小的地址結構(如 IPv4 和 IPv6)很有用。
  • int flags (accept4 特有): 這個參數允許在創建新套接字時設置一些屬性,類似于 socket()type 參數可以使用的修飾符。

    • SOCK_NONBLOCK: 將新創建的套接字設置為非阻塞模式。
    • SOCK_CLOEXEC: 在調用 exec()自動關閉該套接字。

5. 返回值

  • 成功時: 返回一個新的、非負的整數,即為新連接創建的套接字文件描述符。服務器應使用這個返回的文件描述符與客戶端進行后續的數據通信。
  • 失敗時: 返回 -1,并設置全局變量 errno 來指示具體的錯誤原因(例如 EAGAINEWOULDBLOCK 套接字被標記為非阻塞且沒有未決連接,EBADF sockfd 無效,EINVAL 套接字未監聽,EMFILE 進程打開的文件描述符已達上限等)。

阻塞與非阻塞:

  • 阻塞套接字(默認):如果監聽隊列中沒有待處理的連接,accept 調用會阻塞(掛起)當前進程,直到有新的連接到達。
  • 非阻塞套接字(如果監聽套接字被設置為非阻塞):如果監聽隊列中沒有待處理的連接,accept 會立即返回 -1,并將 errno 設置為 EAGAINEWOULDBLOCK

6. 相似函數,或關聯函數

  • socket: 用于創建原始的監聽套接字。
  • bind: 將監聽套接字綁定到本地地址。
  • listen: 使套接字進入監聽狀態,開始接收連接請求。
  • connect: 客戶端使用此函數向服務器發起連接。
  • close: 服務器在與客戶端通信結束后,需要關閉 accept 返回的那個套接字文件描述符。通常也需要關閉原始的監聽套接字(在服務器退出時)。
  • fork / 多線程: 服務器通常在 accept 之后調用 fork 或創建新線程來處理與客戶端的通信,以便主服務器進程可以繼續調用 accept 接受新的連接。

7. 示例代碼

示例 1:基本的 TCP 服務器 accept 循環

這個例子演示了一個典型的、順序處理的 TCP 服務器如何使用 accept 循環來接受和處理客戶端連接。

// sequential_tcp_server.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h> // inet_ntoa (注意:不是線程安全的)#define PORT 8080
#define BACKLOG 10void handle_client(int client_fd, struct sockaddr_in *client_addr) {char buffer[1024];ssize_t bytes_read;printf("Handling client %s:%d (fd: %d)\n",inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port), client_fd);// 讀取客戶端發送的數據while ((bytes_read = read(client_fd, buffer, sizeof(buffer) - 1)) > 0) {buffer[bytes_read] = '\0'; // 確保字符串結束printf("Received from client: %s", buffer); // buffer 可能已包含 \n// 將收到的數據回顯給客戶端if (write(client_fd, buffer, bytes_read) != bytes_read) {perror("write to client failed");break;}}if (bytes_read < 0) {perror("read from client failed");} else {printf("Client %s:%d disconnected (fd: %d)\n",inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port), client_fd);}close(client_fd); // 關閉與該客戶端的連接
}int main() {int server_fd, client_fd;struct sockaddr_in address, client_address;socklen_t client_addr_len = sizeof(client_address);int opt = 1;// 1. 創建套接字if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 2. 設置套接字選項if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {perror("setsockopt failed");close(server_fd);exit(EXIT_FAILURE);}// 3. 配置服務器地址address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);// 4. 綁定套接字if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}// 5. 監聽連接if (listen(server_fd, BACKLOG) < 0) {perror("listen failed");close(server_fd);exit(EXIT_FAILURE);}printf("Server listening on port %d\n", PORT);// 6. 主循環:接受并處理連接while (1) {printf("Waiting for a connection...\n");// 7. 接受連接 (阻塞調用)client_fd = accept(server_fd, (struct sockaddr *)&client_address, &client_addr_len);if (client_fd < 0) {perror("accept failed");continue; // 或 exit(EXIT_FAILURE);}printf("New connection accepted.\n");// 8. 處理客戶端 (順序處理,同一時間只能處理一個)handle_client(client_fd, &client_address);// 處理完一個客戶端后,循環繼續 accept 下一個}// 注意:在實際程序中,需要有退出機制和清理代碼// close(server_fd); // 不會執行到這里return 0;
}

代碼解釋:

  1. 創建、綁定、監聽服務器套接字,這部分與之前 socket, bind, listen 的例子相同。
  2. 進入一個無限的 while(1) 循環。
  3. 在循環內部,調用 accept(server_fd, (struct sockaddr *)&client_address, &client_addr_len)
    • server_fd: 監聽套接字。
    • &client_address: 指向 sockaddr_in 結構的指針,用于接收客戶端地址。
    • &client_addr_len: 指向 socklen_t 變量的指針,該變量在調用前被初始化為 sizeof(client_address)
  4. accept 是一個阻塞調用。如果沒有客戶端連接,程序會在此處掛起等待。
  5. 當有客戶端連接到達時,accept 返回一個新的文件描述符 client_fd
  6. 調用 handle_client 函數處理與該客戶端的通信。這個函數會讀取客戶端數據并回顯回去。
  7. handle_client 函數結束時(客戶端斷開或出錯),會調用 close(client_fd) 關閉這個連接。
  8. 主循環繼續,再次調用 accept 等待下一個客戶端。

缺點: 這種順序處理的方式效率很低。服務器在處理一個客戶端時,無法接受其他客戶端的連接,直到當前客戶端處理完畢。

示例 2:并發 TCP 服務器 (使用 fork)

這個例子演示了如何使用 fork 創建子進程來并發處理多個客戶端連接。

// concurrent_tcp_server.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/wait.h> // waitpid#define PORT 8080
#define BACKLOG 10void handle_client(int client_fd, struct sockaddr_in *client_addr) {char buffer[1024];ssize_t bytes_read;printf("Child %d: Handling client %s:%d (fd: %d)\n",getpid(), inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port), client_fd);while ((bytes_read = read(client_fd, buffer, sizeof(buffer) - 1)) > 0) {buffer[bytes_read] = '\0';printf("Child %d: Received from client: %s", getpid(), buffer);if (write(client_fd, buffer, bytes_read) != bytes_read) {perror("Child: write to client failed");break;}}if (bytes_read < 0) {perror("Child: read from client failed");} else {printf("Child %d: Client %s:%d disconnected.\n",getpid(), inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port));}close(client_fd);printf("Child %d: Connection closed. Exiting.\n", getpid());_exit(EXIT_SUCCESS); // 子進程使用 _exit 退出
}int main() {int server_fd, client_fd;struct sockaddr_in address, client_address;socklen_t client_addr_len = sizeof(client_address);int opt = 1;pid_t pid;if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {perror("setsockopt failed");close(server_fd);exit(EXIT_FAILURE);}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}if (listen(server_fd, BACKLOG) < 0) {perror("listen failed");close(server_fd);exit(EXIT_FAILURE);}printf("Concurrent Server (PID: %d) listening on port %d\n", getpid(), PORT);while (1) {client_fd = accept(server_fd, (struct sockaddr *)&client_address, &client_addr_len);if (client_fd < 0) {perror("accept failed");continue;}printf("Main process (PID: %d): New connection accepted.\n", getpid());// Fork a new process to handle the clientpid = fork();if (pid < 0) {perror("fork failed");close(client_fd); // Important: close the client fd on fork failure} else if (pid == 0) {// --- Child process ---close(server_fd); // Child doesn't need the listening sockethandle_client(client_fd, &client_address);// handle_client calls close(client_fd) and _exit()// so nothing more needed here} else {// --- Parent process ---close(client_fd); // Parent doesn't need the client-specific socketprintf("Main process (PID: %d): Forked child process (PID: %d) to handle client.\n", getpid(), pid);// Optional: Clean up any finished child processes (non-blocking)// This prevents zombie processes if children finish quicklypid_t wpid;int status;while ((wpid = waitpid(-1, &status, WNOHANG)) > 0) {printf("Main process (PID: %d): Reaped child process (PID: %d)\n", getpid(), wpid);}}}close(server_fd);return 0;
}

代碼解釋:

  1. 服務器設置部分與順序服務器相同。
  2. accept 成功返回后,立即調用 fork()
  3. fork 返回后
    • 子進程 (pid == 0):
      • 關閉不需要的監聽套接字 server_fd
      • 調用 handle_client(client_fd, ...) 處理客戶端。
      • handle_client 處理完畢后會關閉 client_fd 并調用 _exit() 退出。
    • 父進程 (pid > 0):
      • 關閉不需要的客戶端套接字 client_fd(因為子進程在處理它)。
      • 打印信息,表明已派生子進程處理客戶端。
      • 可選地調用 waitpid(-1, &status, WNOHANG)非阻塞地清理已經結束的子進程(回收僵尸進程)。如果省略這一步,結束的子進程會變成僵尸進程,直到父進程退出。
  4. 父進程繼續循環,調用 accept 等待下一個客戶端連接。
示例 3:使用 accept4 設置非阻塞客戶端套接字

這個例子演示了如何使用 accept4 函數在創建新連接套接字的同時就將其設置為非阻塞模式。

// accept4_example.c
#define _GNU_SOURCE // 必須定義以使用 accept4
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h> // F_GETFL, F_SETFL, O_NONBLOCK#define PORT 8080
#define BACKLOG 10int main() {int server_fd, client_fd;struct sockaddr_in address, client_address;socklen_t client_addr_len = sizeof(client_address);int opt = 1;int flags;if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {perror("setsockopt failed");close(server_fd);exit(EXIT_FAILURE);}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}if (listen(server_fd, BACKLOG) < 0) {perror("listen failed");close(server_fd);exit(EXIT_FAILURE);}printf("Server listening on port %d. Accepting connections...\n", PORT);while (1) {printf("Waiting for a connection...\n");// 使用 accept4 直接創建非阻塞的客戶端套接字client_fd = accept4(server_fd, (struct sockaddr *)&client_address, &client_addr_len, SOCK_NONBLOCK);if (client_fd < 0) {perror("accept4 failed");continue;}printf("New connection accepted (fd: %d). Checking if it's non-blocking...\n", client_fd);// 驗證套接字是否確實是非阻塞的flags = fcntl(client_fd, F_GETFL, 0);if (flags == -1) {perror("fcntl F_GETFL failed");close(client_fd);continue;}if (flags & O_NONBLOCK) {printf("Confirmed: Client socket (fd: %d) is non-blocking.\n", client_fd);} else {printf("Warning: Client socket (fd: %d) is NOT non-blocking.\n", client_fd);}// --- 在這里,你可以對非阻塞的 client_fd 進行 read/write/select/poll 操作 ---// 例如,將其添加到 epoll 或 select 的監視集合中// 為了演示,我們簡單地關閉它printf("Closing client socket (fd: %d).\n", client_fd);close(client_fd);}close(server_fd);return 0;
}

代碼解釋:

  1. 服務器設置部分與之前相同。
  2. 在調用 accept4 時,傳入了 SOCK_NONBLOCK 標志作為第四個參數。
  3. 如果 accept4 成功,返回的 client_fd已經被設置為非阻塞模式。
  4. 代碼通過 fcntl(client_fd, F_GETFL, 0) 獲取套接字標志,并檢查 O_NONBLOCK 位是否被設置,以驗證 accept4 的效果。
  5. 在實際應用中,得到非阻塞的 client_fd 后,通常會將其加入到 selectpollepoll 的監視集合中,以便高效地管理多個并發連接。

重要提示與注意事項:

  1. 返回新的文件描述符: accept 返回的文件描述符與原始監聽套接字 sockfd 完全不同。原始套接字繼續用于監聽,新套接字用于與特定客戶端通信。
  2. 必須關閉: 服務器在與客戶端通信結束后,必須調用 close() 關閉 accept 返回的那個文件描述符,以釋放資源。
  3. 獲取客戶端地址: 利用 addraddrlen 參數獲取客戶端的 IP 和端口對于日志記錄、訪問控制、調試等非常有用。
  4. 并發處理: 對于需要同時處理多個客戶端的服務器,必須使用 fork、多線程或 I/O 多路復用(select/poll/epoll)等技術。簡單的順序處理無法滿足實際需求。
  5. 錯誤處理: 始終檢查 accept 的返回值。在繁忙的服務器上,非阻塞 accept 可能會因為沒有連接而返回 EAGAIN
  6. accept4 的優勢: accept4 可以在原子操作中設置新套接字的屬性,避免了先 acceptfcntl 的兩步操作,理論上更高效且沒有競態條件。

總結:

accept 是 TCP 服務器模型的核心。它使得服務器能夠從監聽狀態進入與客戶端的實際數據交換狀態。理解其阻塞/非阻塞行為、返回值含義以及如何與并發處理技術(如 fork)結合使用,是構建健壯網絡服務器的基礎。accept4 則為需要精細控制新連接套接字屬性的場景提供了便利。

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

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

相關文章

【Java】在一個前臺界面中動態展示多個數據表的字段及數據

企業的生產環境中&#xff0c;如果不允許直接操作數據表中的數據&#xff0c;則需要開發一個前臺界面&#xff0c;在必要時實現對多個數據表中數據的增刪改查&#xff0c; 此時就需要后端將Oracle表字段及數據查詢返回前端動態展示…… 一、Oracle特定元數據查詢 使用JDBC獲取O…

MySQL(174)如何理解MySQL的多版本并發控制(MVCC)?

MySQL的多版本并發控制&#xff08;MVCC, Multi-Version Concurrency Control&#xff09;是一種用于實現高并發性的機制&#xff0c;它允許多個事務同時讀取和寫入數據&#xff0c;而不會相互阻塞。MVCC主要在InnoDB存儲引擎中實現&#xff0c;通過維護數據的多個版本來實現一…

Docker--將非root用戶添加docker用戶組,解決頻繁sudo執行輸入密碼的問題

一、為什么要有docker用戶組&#xff1f; 根本原因&#xff1a; Linux的設備訪問權限控制機制 Docker守護進程&#xff08;dockerd&#xff09;運行時會創建一個特殊的Unix套接字文件&#xff0c;如&#xff1a;/var/run/docker.sock。 這個文件就像一個“門”&#xff0c;所有…

C語言---函數的遞歸與迭代

遞歸的理解與限制條件 所謂函數遞歸就是遞推加回歸的過程&#xff0c;就是函數自己調用自己。遞歸的思想就是把復雜的問題拆分成與原來那個大問題相似的子問題來求解&#xff0c;大事化小&#xff0c;像剝洋蔥一樣&#xff0c;最終把問題解決。 遞歸的限制條件&#xff1a; 一個…

freqtrade在docker運行一個dryrun實例

檢查配置 freqtrade trade --config user_data/config.json --strategy MlStrategy config文件,這個配置做期貨為主&#xff0c;靜態配置了交易對&#xff0c;同時端口和第一個bot要不一樣&#xff0c;不然沒有辦法進行監控&#xff0c;甚至要沖突了。10S鐘進行循環&#xff0c…

單片機學習筆記.PWM

PWM原理&#xff1a; 頻率占空比&#xff1a;精度占空比變化步距 電機驅動電路&#xff1a;利用PWM實現呼吸燈代碼 sbit LEDP2^0;//引腳定義unsigned char Time,i;//變量定義void Delay(unsigned int t)//定義延時 {while(t--); }main函數里&#xff1a;int main() {unsigned c…

【Git】解決使用SSH連接遠程倉庫時需要多次輸入密碼的問題

問題產生的原因&#xff1a;你的SSH私鑰設置了密碼短語&#xff08;passphrase&#xff09;。解決問題的方法&#xff1a;使用SSH代理&#xff08;ssh-agent&#xff09;&#xff0c;ssh-agent是一個后臺運行程序&#xff0c;它會記住你解鎖過的SSH私鑰的密碼短語&#xff0c;這…

機器學習—邏輯回歸

一介紹邏輯回歸是處理二分類問題的線性模型&#xff0c;通過sigmoid函數將線性輸出映射到[0,1]&#xff0c;輸出事件發生概率&#xff0c;廣泛用于預測與分類。如果做坐標的話&#xff0c;特征就是p1和p2&#xff0c;結果就是y紅的與綠的 二Sigma函數代碼說明Sigmoid 函數定義&…

深入解讀OpenTelemetry分布式鏈路追蹤:原理與實踐指南

深入解讀OpenTelemetry分布式鏈路追蹤&#xff1a;原理與實踐指南 分布式系統在微服務架構下&#xff0c;服務調用鏈越來越復雜&#xff0c;追蹤單次請求在各個微服務之間的執行情況成為運維與性能優化的關鍵。作為新一代開源標準&#xff0c;OpenTelemetry為分布式追蹤、指標與…

【0基礎PS】PS工具詳解--圖案圖章工具

目錄前言一、圖案圖章工具基礎認知?二、工具選項欄參數詳解?三、圖案圖章工具應用案例?總結前言 在 Adobe Photoshop 這一強大的圖像處理軟件中&#xff0c;圖案圖章工具是一個獨具特色的功能&#xff0c;它允許用戶利用預先定義好的圖案進行繪畫操作。 一、圖案圖章工具基…

劇本殺小程序系統開發:構建數字化劇本殺生態圈

在快節奏的現代生活中&#xff0c;人們越來越渴望在閑暇之余找到一種既能放松心情又能增進社交的方式。劇本殺&#xff0c;作為一種集推理、表演、社交于一體的新興娛樂形式&#xff0c;恰好滿足了這一需求。然而&#xff0c;隨著市場的不斷擴大&#xff0c;如何保持劇本殺的新…

【DL學習筆記】計算圖與自動求導

計算圖計算圖&#xff08;Computation Graph&#xff09;是一種用于描述計算過程的圖形化表示方法。在深度學習中&#xff0c;計算圖通常用于描述 網絡結構、運算過程 和數據流向。計算圖是一種有向無環圖&#xff0c;用圖形方式來表示算子與變量之間的關系&#xff0c;直觀高效…

大型地面光伏電站開發建設流程

?地面電站特特點&#xff1a;規模大&#xff0c;通常占用土地、水面等&#xff0c;地面式選址選項多&#xff0c;且不斷拓展出新的用地模式&#xff0c;地面式選址集中在山體、灘涂、沼澤、戈壁、沙漠、受污染土地等閑置或廢棄土地上。

除數博弈(動態規劃)

愛麗絲和鮑勃一起玩游戲&#xff0c;他們輪流行動。愛麗絲先手開局。最初&#xff0c;黑板上有一個數字 n 。在每個玩家的回合&#xff0c;玩家需要執行以下操作&#xff1a;選出任一 x&#xff0c;滿足 0 < x < n 且 n % x 0 。用 n - x 替換黑板上的數字 n 。如果玩家…

一起學springAI系列一:初體驗

Spring AI是干嘛的官網最權威&#xff0c;直接粘貼&#xff1a;“Spring AI”項目旨在簡化那些包含人工智能功能的應用程序的開發過程&#xff0c;同時避免不必要的復雜性。AI相關領域的功能對python的支持是最好的&#xff0c;相關供應商在出了啥功能的時候&#xff0c;都會優…

Ext JS極速項目之 Coworkee

ExtJS Coworkee 是什么? Ext JS 的 Coworkee 是一個由 Sencha 官方提供的完整員工管理應用示例,旨在展示 Ext JS 框架在企業級應用開發中的能力。 在線試用的地址是: https://examples.sencha.com/coworkee/#home 頁面效果與布局 登錄頁面: 主頁效果 左右分區結構:左…

飛算科技:原創技術重塑 Java 開發,引領行業數智化新浪潮

在科技革新的浪潮中&#xff0c;飛算科技作為一家堅持自主創新的數字科技企業&#xff0c;同時也是國家級高新技術企業&#xff0c;正深耕互聯網科技、大數據、人工智能等前沿領域&#xff0c;為眾多企業的數字化與智能化轉型提供強勁動力。?飛算科技的成長軌跡&#xff0c;是…

cesium FBO(一)渲染到紋理(RTT)

一聽到三維的RTT&#xff08;Render To Texture&#xff09;&#xff0c;似乎很神秘&#xff0c;但從底層實現一看&#xff0c;其實也就那樣&#xff0c;設計API的哪些頂級家伙已經幫你安排的明明白白了&#xff0c;咱們只需要學會怎么用就可以了。我認為得從WebGL入手&#xf…

PNP機器人機器人學術年會展示靈巧手動作捕捉方案。

2025年8月1-3日&#xff0c;第六屆中國機器人學術年會&#xff08;CCRS2025&#xff09;在長沙國際會議中心舉行&#xff0c;主題“人機共融&#xff0c;智向未來”。PNP機器人與靈巧智能聯合展出最新靈巧手模仿學習方案&#xff1a;基于少量示教數據即可快速復現復雜抓取動作&…

【45】C#入門到精通——C#調用C/C++生成動態庫.dll及C++ 生成動態庫.dll ,DllImport()方式導入 C++動態庫.dll方法總結

文章目錄1 C 生成動態庫.dll2 C#調用C/C生成動態庫.dll2.1 [DllImport()] 方式導入 C動態庫.dll2.2 調用測試3 C/C 生成通用dll,改進3.1改進后.h3.2 .cpp3.2 C# 調用4 [DllImport()] 方式導入C生成的 .dll 總結4.1 指定路徑導入4.2 .dll放在 執行目錄下&#xff08;一定要放對&…