这次我们介绍 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;一定要放对&…