C/S模型的简单实现(UDP服务器)、本地套接字(sockaddr_un )的讲解

news2024/10/10 17:05:47

目录

1.UDP

1.1 UDP服务器

1.2 TPC和UDP的比较

1.3 C/S模型 -- UDP

recvfrom、sendto

server

client

2.本地套接字

2.1 套接字比较

2.2 函数参数选用

2.3 server

2.4 client

2.5 实现对比


1.UDP

1.1 UDP服务器

UDP 是一种无连接的传输协议,类似于发送短信,不需要在通信前建立连接,也不需要维持连接状态。发送方只需将数据打包成独立的“数据报”(datagram)发给接收方。接收方收到这些数据报,但并不保证顺序、可靠性或数据的完整性。

由于UDP是“无连接的、不可靠的报文传递”,通信速度会更快,但它不提供像TCP那样的流量控制和错误恢复机制。因此,UDP常被用于对实时性要求高,但对丢包、顺序错误容忍度较高的场景。

由于UDP不保证数据可靠性,因此在实际应用中需要采取一些措施来弥补其不足。以下是两种常见的UDP传输中可能遇到的问题以及对应的解决方法:

1. 缓冲区溢出及丢包

当接收方的缓冲区被填满,无法继续接收数据时,接收方会丢弃后续到达的数据报。UDP 没有像 TCP 那样的滑动窗口机制来控制流量,这种丢包现象需要通过应用层的设计或系统参数调整来解决。

解决方法:

  • 应用层流量控制:在应用层设计一个控制机制,比如让服务器根据接收方的处理能力来动态调整数据发送的速率。
  • 调整缓冲区大小:通过 setsockopt() 函数来调整接收方的缓冲区大小。例如,通过设置 SO_RCVBUF 选项增加接收方的缓冲区,以便在高流量传输时能更好地处理数据。
int sockfd;
int rcvbuf_size = 65535;  // 增大接收缓冲区大小
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf_size, sizeof(rcvbuf_size));

2. 缺乏可靠性

UDP 的本质是不可靠的,因此在数据传输过程中可能会出现丢包、重复包或顺序错乱等问题。如果应用对数据可靠性有一定要求,需要通过应用层协议来弥补。

解决方法:

  • 应用层协议:设计自己的应用层协议来提供确认机制、重传机制等。例如,每个数据报附带序号,接收方在收到数据后返回确认消息,如果发送方没有收到确认则重发。
  • FEC(前向纠错)技术:在发送数据时加入冗余信息,这样即使接收方丢失部分数据,也能通过冗余信息还原完整数据。

1.2 TPC和UDP的比较

特性

TCP

UDP

连接管理

面向连接(需要三次握手建立连接)

无连接(不需要建立连接)

可靠性

提供可靠传输,保证数据不丢失、顺序不乱

不保证可靠性,可能丢包或乱序

流量控制和拥塞控制

有流量控制和拥塞控制机制

没有流量控制和拥塞控制

传输方式

面向字节流,适合大量数据传输

面向数据报,适合发送小而独立的数据

实时性

较差(有重传和流量控制)

强(无连接和握手,实时性好)

应用场景

文件传输、电子邮件、Web 浏览等需要可靠传输

视频会议、实时游戏、广播等对速度要求高

UDP的优缺点

优点:

  1. 开销小,通信速度快:UDP 不需要像 TCP 一样在通信前通过三次握手来建立连接,也不需要在通信过程中维持状态。因此,传输数据时的开销要比 TCP 小,适合快速、频繁地传递数据。
  2. 实时性强:UDP 适用于需要低延迟的场景,因为它不会等待确认,数据报会直接发送出去。它经常用于需要即时传输的应用,比如视频会议、电话会议、网络直播等。
  3. 面向数据报:UDP 不像 TCP 那样面向流,数据是以独立的报文方式发送的。因此每个数据报是独立的单位,不依赖前后数据报的顺序,这使得它更适合广播和多播应用。

缺点:

  1. 不可靠传输:UDP 不保证数据的可靠性,也就是说,数据报可能丢失、重复或到达时顺序错乱。UDP 也不保证对丢失或损坏的数据进行重传。
  2. 无流量控制:UDP 缺少 TCP 的流量控制和拥塞控制机制,不能有效防止接收端处理不过来时发生丢包。如果发送方发送数据的速度过快,接收方可能会丢失一部分数据。
  3. 无连接状态:由于 UDP 是无连接的,所以无法维护通信双方之间的状态。这意味着每个数据报都是独立的,无法像 TCP 那样提供长时间的可靠连接。

1.3 C/S模型 -- UDP

由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,保证通讯可靠性的机制需要在应用层实现。

编译运行server,在两个终端里各开一个client与server交互,看看server是否具有并发服务的能力。用Ctrl+C关闭server,然后再运行server,看此时client还能否和server联系上。和前面TCP程序的运行结果相比较,体会无连接的含义。

recvfrom、sendto

recv() / send只能用于TCP通信,代替read和write,具体参数去按K查看man手册

而在UDP中能替换read和write则是:recvfrom()和sendto

ssize_t recvfrom(int sockfd,   //自己的套接字
                 void *buf,   //读取数据后存放的缓冲区
                 size_t len,   //缓冲区的大小
                 int flags,    //默认传0
                 struct sockaddr *src_addr,  //传出对端的地址结构,传出参数
                 socklen_t *addrlen)   //对端套接字的大小,传入传出
返回值:成功接收数据:字节数;失败:-1,errno;对端关闭:0

ssize_t sendto(int sockfd,       //自己的套接字
               const void *buf,  //存储数据的缓冲区
               size_t len,       //数据的长度
               int flags,        //默认0
               const struct sockaddr *dest_addr,  //发送数据给目标的地址结果,传入
               socklen_t addrlen);  //目标地址结构体的长度
返回值:成功:字节数;失败:-1,errno

server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

#define SERVER_PORT 9876
#define BUFFER_SIZE 1024

int main() {
    int sockfd;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_len = sizeof(client_addr);
    char buffer[BUFFER_SIZE];

    // 创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 绑定地址和端口
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;  // 绑定到本地所有IP
    server_addr.sin_port = htons(SERVER_PORT); // 指定端口

    if (bind(sockfd, (const struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("UDP 服务器启动,等待客户端发送数据...\n");

    // 接收客户端数据
    while (1) {
        memset(buffer, 0, BUFFER_SIZE);
        int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &client_len);
        if (n < 0) {
            perror("recvfrom error");
            continue;
        }

        buffer[n] = '\0';
        printf("客户端: %s\n", buffer);

        // 回复客户端
        char *message = "服务器已收到消息";
        sendto(sockfd, message, strlen(message), 0, (struct sockaddr *)&client_addr, client_len);
    }

    close(sockfd);
    return 0;
}

client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>

#define SERVER_PORT 9876
#define SERVER_IP "127.0.0.1"  // 服务器 IP 地址
#define BUFFER_SIZE 1024

int main() {
    int sockfd;
    struct sockaddr_in server_addr;
    char buffer[BUFFER_SIZE];
    socklen_t server_len = sizeof(server_addr);

    // 创建UDP套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 服务器地址设置
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    while (1) {
        // 获取用户输入
        printf("请输入发送给服务器的消息: ");
        fgets(buffer, BUFFER_SIZE, stdin);
        buffer[strcspn(buffer, "\n")] = '\0';  // 移除换行符

        // 发送数据给服务器
        sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&server_addr, server_len);

        // 接收服务器的回应
        int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, NULL, NULL);
        if (n < 0) {
            perror("recvfrom error");
            continue;
        }

        buffer[n] = '\0';
        printf("服务器回复: %s\n", buffer);
    }

    close(sockfd);
    return 0;
}

2.本地套接字

IPC:pipe、fifo、mmap、信号、本地套接字(domain)

UNIX Domain Socket(也称为本地套接字)是一种用于同一台主机上不同进程间通信的机制,是在网络 Socket 的框架上发展出来的进程间通信 (IPC) 方式。与网络套接字不同的是,UNIX Domain Socket 不需要经过网络协议栈的处理,因此效率更高。下面是对 UNIX Domain Socket 的详细讲解。IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的

虽然可以通过网络套接字在同一台主机的进程间进行通信(使用 loopback 地址 127.0.0.1),但是网络套接字的设计初衷是用于跨网络通信。网络协议需要经过较为复杂的协议栈(TCP/IP),包括数据打包、拆包、校验和计算、维护序号和应答等步骤,这些操作对本地主机上的通信是多余的。

与网络套接字通过 IP 地址和端口号标识通信双方不同,UNIX Domain Socket 通过文件系统中的路径来标识。地址结构为 struct sockaddr_un,其中包含一个文件路径,表示 socket 文件的位置。这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。

使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAMSOCK_STREAM,protocol参数仍然指定为0即可。socket函数介绍点击这里

  • SOCK_STREAM:面向流的通信,类似于 TCP,提供双向、可靠、无边界的字节流通信。
  • SOCK_DGRAM:面向数据报的通信,类似于 UDP,虽然消息是有边界的,但不同于 UDP,它是可靠的,数据包不会丢失或乱序。

2.1 套接字比较

对比网络套接字地址结构和本地套接字地址结构:

网络套接字的称地址结构 -- 封装了IP和端口号 
struct sockaddr_in {
    __kernel_sa_family_t sin_family; /* Address family */  //地址结构类型 -- AF_INET(IPv4)
    __be16 sin_port;/* Port number */端口号
        struct in_addr sin_addr;/* Internet address */ IP地址
    };

本地套接字的地址结构:
struct sockaddr_un {
    __kernel_sa_family_t sun_family;/* AF_UNIX */ //地址结构类型 -- AF_UNIX(本地协议)
    char sun_path[UNIX_PATH_MAX]; /* pathname */ socket文件名(含路径)
    };
以下程序将UNIX Domain socket绑定到一个地址。
size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
#define offsetof(type, member) ((int)&((type *)0)->MEMBER)

2.2 函数参数选用

int socket(int domain, int type, int protocol);
domain:
AF_UNIX/AF_LOCAL 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用
type:下面随便选一个
    SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
    SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
protocol:
    传0 表示使用默认协议
返回值:
    成功:返回用于引用socket的文件描述符,失败:返回-1,设置errno
 要注意的是返回的fd是个文件描述符,在UDP中需要绑定上地址结构后该套接字才真正被创建,服务器和客户端都需要整一个伪文件出来进行绑定创建伪文件(套接字)
    所以都需要给bind传入地址结构 -- 创建传入的名为(struct sockaddr_un).sun_path的伪文件 -- 绑定 --  socket真正形成
 因此调用bind前需要用unlink对名为srv.socket的文件进行删除 -- 减少其连接数为0,系统就会将其回收,防止文件名冲突而创建不了

 而在TCP网络通信中,即使不用bind绑定上IP和端口号,系统也会自动分配,一调用socket函数套接字便创建

 TCP中IP和port就对应一个socket,而UDP中是一个名为(struct sockaddr_un).sun_path的伪文件对应一个socket                          

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:
    lfd = socket(AF_UNIX, SOCK_STREAM,0);  //空壳,是文件描述符 -- 套接字
addr:
    // struct sockaddr_un {
    // __kernel_sa_family_t sun_family;/* AF_UNIX */ //地址结构类型 -- AF_UNIX(本地协议)
    // char sun_path[UNIX_PATH_MAX]; /* pathname */ socket文件名(含路径)
    // };
    struct sockaddr_un srv_addr;
    srv_addr.faimly = AF_UNIX;  //根据socket函数参数domain指定一样的协议
    strcpy(srv_addr.sun_path,"srv.socket");   //给套接字起名字 -- 给伪文件命名
    addr = (struct sockaddr *)srv_addr;
addrlen:
    地址结构的长度
    addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(srv_addr.sun_path);
    // #define offsetof(type, member) ((int)&((type *)0)->MEMBER)
    //offsetof求的参2到参1的首地址偏移大小为多少,其实就是两字节

    传出一个绑定伪文件的套接字文件描述符 参1 socket,指向名为“srv.socket”的伪文件
    因此调用bind前需要用unlink对名为srv.socket的文件进行删除 -- 减少其连接数为0,系统就会将其回收
    防止文件名冲突而创建不了
    所以UDP中套接字是调用bind创建的 -- (struct sockaddr_un).sun.path的伪文件

2.3 server

使用本地套接字(也叫 Unix 域套接字,AF_UNIX 地址族)实现一个简单的 C/S 模型。与 TCP 或 UDP 不同,Unix 域套接字在本地通信中使用文件路径作为地址标识,通常用于同一主机内的进程间通信。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define SOCKET_PATH "/tmp/unix_socket"  // 本地套接字文件路径
#define BUFFER_SIZE 1024

int main() {
    int server_fd, client_fd;
    struct sockaddr_un server_addr, client_addr;
    socklen_t client_len;
    char buffer[BUFFER_SIZE];

    // 创建本地套接字
    server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }

    // 绑定地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);

    unlink(SOCKET_PATH);  // 删除已经存在的文件,避免 bind 失败
    if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind error");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 监听连接
    if (listen(server_fd, 5) < 0) {
        perror("listen error");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    printf("服务器正在等待客户端连接...\n");

    client_len = sizeof(client_addr);
    client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
    if (client_fd < 0) {
        perror("accept error");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 接收数据
    while (1) {
        memset(buffer, 0, BUFFER_SIZE);
        int n = read(client_fd, buffer, BUFFER_SIZE);
        if (n <= 0) {
            perror("read error");
            break;
        }
        printf("客户端发送: %s\n", buffer);

        // 回复客户端
        char *message = "服务器收到消息!";
        write(client_fd, message, strlen(message) + 1);
    }

    close(client_fd);
    close(server_fd);
    unlink(SOCKET_PATH);  // 关闭服务器时删除套接字文件

    return 0;
}

2.4 client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#define SOCKET_PATH "/tmp/unix_socket"
#define BUFFER_SIZE 1024

int main() {
    int client_fd;
    struct sockaddr_un server_addr;
    char buffer[BUFFER_SIZE];

    // 创建本地套接字
    client_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (client_fd < 0) {
        perror("socket error");
        exit(EXIT_FAILURE);
    }

    // 设置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sun_family = AF_UNIX;
    strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);

    // 连接服务器
    if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("connect error");
        close(client_fd);
        exit(EXIT_FAILURE);
    }

    while (1) {
        // 读取用户输入
        printf("请输入要发送给服务器的消息: ");
        fgets(buffer, BUFFER_SIZE, stdin);
        buffer[strcspn(buffer, "\n")] = '\0';  // 移除换行符

        // 发送数据给服务器
        write(client_fd, buffer, strlen(buffer) + 1);

        // 接收服务器的回复
        int n = read(client_fd, buffer, BUFFER_SIZE);
        if (n <= 0) {
            perror("read error");
            break;
        }
        printf("服务器回复: %s\n", buffer);
    }

    close(client_fd);
    return 0;
}

2.5 实现对比

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2202567.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot MyBatis连接数据库设置了encoding=utf-8还是不能用中文来查询

properties的MySQL连接时已经指定了字符编码格式&#xff1a; url: jdbc:mysql://localhost:3306/sky_take_out?useUnicodetrue&characterEncodingutf-8使用MyBatis查询&#xff0c;带有中文参数&#xff0c;查询出的内容为空。 执行的语句为&#xff1a; <select id&…

已经被这几种广告彻底逼疯……还好有救了

这个假期回家团聚&#xff0c;爸妈小心翼翼问我手机越来越难用了&#xff0c;让我帮忙看看是不是中病毒了&#xff0c;了解后才知道原来事情是这样的&#xff1a; 以前开屏广告不小心误触已经让人恼火&#xff0c;现在是手机轻微动一动就会进入广告&#xff0c;打开app最后都不…

quantlab_ai版本v0.1代码发布: 从研报中提取因子并建模(附代码与研报集下载)

原创内容第676篇&#xff0c;专注量化投资、个人成长与财富自由。 今天我们继续开发AI大模型自动读研报。 从研报到模型&#xff0c;大致分成几步&#xff1a; ["propose_hypo_exp", "propose", "exp_gen", "coding", "runnin…

【玩转 JS 函数式编程_010】3.2 JS 函数式编程筑基之:以函数式编程的方式活用函数(上)

写在前面 按照惯例&#xff0c;过长的篇幅分开介绍&#xff0c;本篇为 JavaScript 函数式编程核心基础的第二部分——以函数式编程的方式活用函数的上篇&#xff0c;分别介绍了 JS 函数在排序、回调、Promise 期约、以及连续传递等应用场景下的用法演示。和之前章节相比难度又有…

“Flash闪存”基础 及 “SD NAND Flash”产品的测试

本篇除了对flash闪存进行简单介绍外&#xff0c;另给读者推荐一种我本人也在用的小容量闪存。 自带坏块管理的SD NAND Flash&#xff08;贴片式TF卡&#xff09;&#xff0c;尺寸小巧&#xff0c;简单易用&#xff0c;兼容性强&#xff0c;稳定可靠&#xff0c;标准SDIO接口&a…

【C++】map 和 set

目录 一 基础概念 1 关联式容器 2 键值对 3 树形结构的关联式容器 二 map 1 概念 2 基础操作 3 使用实列 1 实例一 2 实例二 3 实例三 4 实例四 4 multimap 1 实例一 三 set 1 概念 2 基础操作 3 使用实例 1 实例一 2 实例二 3 实例三 4 multiset 1 实…

SpringBoot使用esayExcel根据模板导出excel

1、依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.3</version></dependency> 2、模板 3、实体类 package com.skybird.iot.addons.productionManagement.qualityTesting…

流程图 LogicFlow

流程图 LogicFlow 官方文档&#xff1a;https://site.logic-flow.cn/tutorial/get-started <script setup> import { onMounted, ref } from vue import { forEach, map, has } from lodash-es import LogicFlow, { ElementState, LogicFlowUtil } from logicflow/core …

字符编码发展史6 — BOM字节序标记

上一篇《字符编码发展史5 — UTF-16和UTF-32》我们讲解了UTF-16和UTF-32编码。本篇我们将继续讲解字符编码中的字节序标记(BOM)。 2.3. 第三个阶段 国际化 2.3.2. Unicode的编码方式 2.3.2.5. BOM 1. 什么是BOM&#xff1f; BOM是Byte Order Mark的缩写&#xff0c;翻译成…

研究生异地报名,需要社保缴费记录,没有社保记录怎么办。

1、户籍在安徽省&#xff0c;在北京工作&#xff0c;想报北京科技大学&#xff1b; 招生简章中没有提社保记录&#xff0c;但是在报名的时候&#xff0c;又出来要求&#xff1a;北京连续6个月的社保记录。这里是指在北京市考试的要求。没有连续社保缴费记录&#xff0c;肯定不能…

Python 与 Pycharm 的简易安装教程,包含Pycharm的修改

一. 官方网站 Python网址&#xff1a;python唯一的官方网址。 Pycharm网址&#xff1a;Pycharm的官方网址。 二. python安装步骤 滑动到红色框内 Downloads 导航栏。 红色框是选择适合自己电脑系统和版本的部分&#xff0c;蓝色框是选择系统的部分&#xff0c;黄色框是版本号。…

【大数据】数据分析之Spark框架介绍

文章目录 概述一、发展历程与背景二、核心特点三、生态系统与组件四、应用场景五、与其他大数据技术的比较 核心概念1. 弹性分布式数据集&#xff08;RDD, Resilient Distributed Dataset&#xff09;2. 转换&#xff08;Transformations&#xff09;和动作&#xff08;Actions…

Rust编程的函数

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust编程与项目实战_夏天又到了的博客-CSDN博客 7.1 函 数 定 义 在Rust中&#xff0c;函数使用fn关键字定义&#xff0c;后跟函数…

how to increase the height of the ps or cdm window

when the line reaches the bottom; directly pull up the top bar of the window after pulling down the bar

【Linux】ComfyUI和SD WebUI之PYTHON环境共享,模型共享,LORA等公共资源共享

需求 一般玩AI绘图都会装ComfyUI和SD WebUI。而且这俩的模型、lora等都是一致的。为了避免空间的浪费&#xff0c;一般会采用共享数据的方式。而且共享的数据可以任意指定分区&#xff0c;这让挂载NAS共享空间成为可能&#xff0c;实现多绘画机ComfyUI和SD WebUI共享资源。 实…

攀爬数据集,约500张 !VOC格式,yolo可直接使用~真实场景特征明显高清图,yolo可直接使用!

攀爬数据集&#xff0c;约500张&#xff01;&#xff01;&#xff01; VOC格式&#xff0c;yolo可直接使用&#xff5e; 真实场景特征明显高清图&#xff0c;yolo可直接使用&#xff01; 攀爬数据集&#xff0c;约500张&#xff01;&#xff01;&#xff01; VOC格式&#xff0…

用GPT-4o打造LLM+OS(10+实用技能),代码开源,指令曝光,科技演示惊艳全场!

目录 前言 LLM操作系统能力概况&#xff08;phidata中前5个已经实现&#xff09;&#xff1a; 可以读取/生成文本 拥有比任何单个人类更全面的知识 可以浏览互联网 可以使用现有的软件基础设施&#xff08;计算器、Python、鼠标/键盘&#xff09; 可以与其他LLMs通信 可…

一文了解,ARM 工业计算机的发展历程

ARM 工业计算机的发展历程主要经历了以下几个阶段&#xff1a; 早期探索阶段&#xff08;20 世纪 80 年代 - 90 年代初&#xff09;&#xff1a; 起源背景&#xff1a;20 世纪 80 年代&#xff0c;计算机工业蓬勃发展&#xff0c;英国的 Acorn 公司在这一时期积极探索芯片技术…

Unity实现自定义图集(四)

以下内容是根据Unity 2020.1.0f1版本进行编写的   在之前的篇章中已经把自定义图集在编辑器上的使用,以及运行时所需的信息都准备好了,接下来就是魔改UGUI的Image组件,使其能够像Image那样运行时如果引用的资源有打自定义图集,则加载对应自定义图集的Texture。 1、思路 …

Centos7通过jengkins实现自动发布和回滚

一、安装jenkins 注&#xff1a;这里不多说哈&#xff0c;百度遍地都是&#xff0c;安装方式不限。 二、jenkins创建项目 注&#xff1a;这里有个坑需要说一下&#xff0c;最开始我使用的是maven构建&#xff0c;但是如果按照我的这套方案会有一个编译死循环的问题&#xff0c;…