当前位置: 首页 > article >正文

《Linux 网络架构:基于 TCP 协议的多人聊天系统搭建详解》

一、系统概述

        本系统是一个基于 TCP 协议的多人聊天系统,由一个服务器和多个客户端组成。客户端可以连接到服务器,向服务器发送消息,服务器接收到消息后将其转发给其他客户端,实现多人之间的实时聊天。系统使用 C 语言编写,利用了 Unix 系统的网络编程接口和多线程、I/O 多路复用等技术。

二、文件结构

  • server.c:服务器端程序,负责监听客户端连接、接收客户端消息并将消息转发给其他客户端。
  • client1.c:客户端程序,使用 poll 函数实现 I/O 多路复用,同时处理用户输入和服务器消息。
  • client2.c:客户端程序,使用多线程技术,一个线程负责接收服务器消息,另一个线程负责处理用户输入。

三、代码详细分析

1. 数据包结构体

        在三个文件中都定义了相同的数据包结构体 Packet,用于在客户端和服务器之间传输数据。

typedef struct {int type;  // 0 for message, 1 for disconnectchar data[BUFFER_SIZE];
} Packet;
  • type:数据包类型,0 表示消息,1 表示断开连接。
  • data:数据包携带的数据,最大长度为 BUFFER_SIZE

2. 服务器端程序(server.c)

2.1 主要变量

  • server_fd:服务器套接字文件描述符。
  • client_fd:客户端套接字文件描述符。
  • max_fd:记录最大的文件描述符,用于 select 函数。
  • activity:记录 select 函数返回的活动文件描述符数量。
  • valread:记录从客户端读取的数据长度。
  • server_addr:存储服务器的地址信息。
  • client_addr:存储客户端的地址信息。
  • client_sockets:数组用于存储所有客户端的套接字文件描述符。
  • readfds:文件描述符集合,用于 select 函数监听可读事件。

2.2 主要步骤

  1. 创建套接字:使用 socket 函数创建一个 TCP 套接字。
  2. 绑定地址:使用 bind 函数将套接字绑定到指定的地址和端口。
  3. 监听连接:使用 listen 函数开始监听客户端连接。
  4. 循环处理:使用 select 函数监听服务器套接字和客户端套接字的可读事件。
    • 若服务器套接字有可读事件,说明有新的客户端连接请求,使用 accept 函数接受连接。
    • 若客户端套接字有可读事件,从客户端读取数据包,根据数据包类型进行相应处理。
      • 若数据包类型为消息,将消息转发给其他客户端。
      • 若数据包类型为断开连接或读取到的数据长度为 0,说明客户端断开连接,关闭客户端套接字。

3. 客户端程序(client1.c)

3.1 主要变量

  • client_fd:客户端套接字文件描述符。
  • server_addr:存储服务器的地址信息。
  • packet:用于存储要发送或接收的数据包。
  • fds:数组用于存储要监听的文件描述符及其事件。

3.2 主要步骤

  1. 创建套接字:使用 socket 函数创建一个 TCP 套接字。
  2. 连接服务器:使用 connect 函数连接到服务器。
  3. 初始化 poll 结构体:监听标准输入和客户端套接字的可读事件。
  4. 循环处理:使用 poll 函数监听文件描述符集合中的可读事件。
    • 若标准输入有可读事件,从标准输入读取数据,设置数据包类型为消息,发送给服务器。
    • 若客户端套接字有可读事件,从服务器读取数据包,根据数据包类型进行相应处理。
      • 若数据包类型为消息,输出接收到的消息。
      • 若数据包类型为断开连接或读取数据失败,说明服务器断开连接,关闭客户端套接字,跳出循环。

4. 客户端程序(client2.c)

4.1 主要变量

  • client_fd:客户端套接字文件描述符。
  • server_addr:存储服务器的地址信息。
  • packet:用于存储要发送或接收的数据包。
  • thread_id:存储线程的标识符。

4.2 主要步骤

  1. 创建套接字:使用 socket 函数创建一个 TCP 套接字。
  2. 连接服务器:使用 connect 函数连接到服务器。
  3. 创建线程:创建一个线程来接收服务器发送的消息。
  4. 循环处理:在主线程中,从标准输入读取数据,设置数据包类型为消息,发送给服务器。
  5. 线程函数:在子线程中,持续接收服务器消息,根据数据包类型进行相应处理。
    • 若数据包类型为消息,输出接收到的消息。
    • 若数据包类型为断开连接或读取数据失败,说明服务器断开连接,关闭客户端套接字,退出程序。

四、编译和运行

4.1 编译

gcc server.c -o server
gcc client1.c -o client1
gcc client2.c -o client2 -lpthread

4.2 运行

  1. 启动服务器:
./server
  1. 启动客户端:
./client1
./client2

4.3运行结果展示

五、源码

5.1服务器端程序(server.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>// 定义服务器监听的端口号
#define PORT 8080
// 定义数据缓冲区的大小
#define BUFFER_SIZE 1024
// 定义服务器允许的最大客户端连接数
#define MAX_CLIENTS 10/*** 定义数据包结构体,用于在服务器和客户端之间传输数据* type 数据包类型,0 表示消息,1 表示断开连接* data 数据包携带的数据*/
typedef struct {int type;  // 0 for message, 1 for disconnectchar data[BUFFER_SIZE];
} Packet;/*** 主函数,服务器程序的入口点* @return 程序的退出状态码,0 表示正常退出*/
int main() {// server_fd 为服务器套接字文件描述符,client_fd 为客户端套接字文件描述符// max_fd 记录最大的文件描述符,用于 select 函数// activity 记录 select 函数返回的活动文件描述符数量// valread 记录从客户端读取的数据长度int server_fd, client_fd, max_fd, activity, valread;// server_addr 存储服务器的地址信息,client_addr 存储客户端的地址信息struct sockaddr_in server_addr, client_addr;// client_len 存储客户端地址结构体的长度socklen_t client_len = sizeof(client_addr);// packet 用于存储从客户端接收的数据包Packet packet;// client_sockets 数组用于存储所有客户端的套接字文件描述符int client_sockets[MAX_CLIENTS] = {0};// readfds 是一个文件描述符集合,用于 select 函数监听可读事件fd_set readfds;// 创建服务器套接字,使用 IPv4 地址族和 TCP 协议if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {// 若套接字创建失败,输出错误信息并退出程序perror("socket failed");exit(EXIT_FAILURE);}// 设置服务器地址信息server_addr.sin_family = AF_INET;// 监听所有可用的网络接口server_addr.sin_addr.s_addr = INADDR_ANY;// 将端口号从主机字节序转换为网络字节序server_addr.sin_port = htons(PORT);// 将服务器套接字绑定到指定的地址和端口if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {// 若绑定失败,输出错误信息,关闭套接字并退出程序perror("bind failed");close(server_fd);exit(EXIT_FAILURE);}// 开始监听客户端连接,允许的最大连接请求队列长度为 3if (listen(server_fd, 3) < 0) {// 若监听失败,输出错误信息,关闭套接字并退出程序perror("listen");close(server_fd);exit(EXIT_FAILURE);}// 输出服务器启动信息,显示监听的端口号printf("Server started on port %d\n", PORT);// 进入无限循环,持续处理客户端连接和数据while (1) {// 清空文件描述符集合FD_ZERO(&readfds);// 将服务器套接字添加到文件描述符集合中,监听其可读事件FD_SET(server_fd, &readfds);// 初始化最大文件描述符为服务器套接字文件描述符max_fd = server_fd;// 遍历客户端套接字数组for (int i = 0; i < MAX_CLIENTS; i++) {// 获取当前客户端的套接字文件描述符int sd = client_sockets[i];if (sd > 0) {// 若该客户端套接字有效,将其添加到文件描述符集合中FD_SET(sd, &readfds);}if (sd > max_fd) {// 更新最大文件描述符max_fd = sd;}}// 调用 select 函数监听文件描述符集合中的可读事件,无超时时间activity = select(max_fd + 1, &readfds, NULL, NULL, NULL);if ((activity < 0) && (errno != EINTR)) {// 若 select 函数调用失败且不是被信号中断,输出错误信息perror("select error");}if (FD_ISSET(server_fd, &readfds)) {// 若服务器套接字有可读事件,说明有新的客户端连接请求if ((client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len)) < 0) {// 若接受连接失败,输出错误信息并继续循环perror("accept");continue;}// 输出新客户端连接的信息,包括套接字文件描述符、IP 地址和端口号printf("New connection, socket fd is %d, ip is : %s, port : %d\n",client_fd, inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));// 遍历客户端套接字数组,找到一个空闲位置存储新客户端的套接字文件描述符for (int i = 0; i < MAX_CLIENTS; i++) {if (client_sockets[i] == 0) {client_sockets[i] = client_fd;break;}}}// 遍历客户端套接字数组,检查每个客户端是否有可读事件for (int i = 0; i < MAX_CLIENTS; i++) {int sd = client_sockets[i];if (FD_ISSET(sd, &readfds)) {// 从客户端读取数据包valread = read(sd, &packet, sizeof(Packet));if (valread == 0) {// 若读取到的数据长度为 0,说明客户端断开连接getpeername(sd, (struct sockaddr*)&client_addr, &client_len);// 输出客户端断开连接的信息,包括 IP 地址和端口号printf("Host disconnected, ip %s, port %d\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));// 关闭客户端套接字close(sd);// 将该位置的客户端套接字文件描述符置为 0,表示空闲client_sockets[i] = 0;} else {if (packet.type == 0) {// 若数据包类型为消息,输出接收到的消息printf("Received from client %d: %s", sd, packet.data);// 将消息转发给其他客户端for (int j = 0; j < MAX_CLIENTS; j++) {if (client_sockets[j] != sd && client_sockets[j] != 0) {// 发送数据包给其他客户端send(client_sockets[j], &packet, sizeof(Packet), 0);}}} else if (packet.type == 1) {// 若数据包类型为断开连接,输出客户端断开连接的信息printf("Client %d disconnected\n", sd);// 关闭客户端套接字close(sd);// 将该位置的客户端套接字文件描述符置为 0,表示空闲client_sockets[i] = 0;}}}}}return 0;
}

5.2客户端程序(client1.c)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <poll.h>
#include <errno.h>// 定义服务器监听的端口号
#define PORT 8080
// 定义数据缓冲区的大小
#define BUFFER_SIZE 1024/*** 定义数据包结构体,用于在客户端和服务器之间传输数据* type 数据包类型,0 表示消息,1 表示断开连接* data 数据包携带的数据*/
typedef struct {int type;  // 0 for message, 1 for disconnectchar data[BUFFER_SIZE];
} Packet;/*** 主函数,客户端程序的入口点* @return 程序的退出状态码,0 表示正常退出*/
int main() {// client_fd 为客户端套接字文件描述符int client_fd;// server_addr 存储服务器的地址信息struct sockaddr_in server_addr;// packet 用于存储要发送或接收的数据包Packet packet;// fds 数组用于存储要监听的文件描述符及其事件struct pollfd fds[2];// 创建客户端套接字,使用 IPv4 地址族和 TCP 协议if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {// 若套接字创建失败,输出错误信息并退出程序perror("socket failed");exit(EXIT_FAILURE);}// 设置服务器地址信息server_addr.sin_family = AF_INET;// 将端口号从主机字节序转换为网络字节序server_addr.sin_port = htons(PORT);// 将服务器的 IP 地址转换为网络字节序server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");// 连接到服务器if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {// 若连接失败,输出错误信息,关闭套接字并退出程序perror("connect");close(client_fd);exit(EXIT_FAILURE);}// 输出连接成功的信息printf("Connected to server\n");// 初始化 poll 结构体// 监听标准输入的可读事件fds[0].fd = STDIN_FILENO;fds[0].events = POLLIN;// 监听客户端套接字的可读事件fds[1].fd = client_fd;fds[1].events = POLLIN;// 进入无限循环,持续处理输入和服务器消息while (1) {// 调用 poll 函数监听文件描述符集合中的可读事件,无超时时间int activity = poll(fds, 2, -1);if ((activity < 0) && (errno != EINTR)) {// 若 poll 函数调用失败且不是被信号中断,输出错误信息perror("poll error");}if (fds[0].revents & POLLIN) {// 若标准输入有可读事件// 清空数据包的数据部分memset(packet.data, 0, BUFFER_SIZE);if (fgets(packet.data, BUFFER_SIZE, stdin) != NULL) {// 若成功从标准输入读取数据// 设置数据包类型为消息packet.type = 0;// 发送数据包给服务器send(client_fd, &packet, sizeof(Packet), 0);}}if (fds[1].revents & POLLIN) {// 若客户端套接字有可读事件// 清空数据包memset(&packet, 0, sizeof(Packet));if (read(client_fd, &packet, sizeof(Packet)) > 0) {// 若成功从服务器读取数据if (packet.type == 0) {// 若数据包类型为消息,输出接收到的消息printf("Received from server: %s", packet.data);} else if (packet.type == 1) {// 若数据包类型为断开连接,输出服务器断开连接的信息printf("Server disconnected\n");// 关闭客户端套接字close(client_fd);// 跳出循环break;}} else {// 若读取数据失败,说明服务器断开连接printf("Server disconnected\n");// 关闭客户端套接字close(client_fd);// 跳出循环break;}}}// 关闭客户端套接字close(client_fd);return 0;
}

5.3客户端程序(client2.c)

// 包含标准输入输出库,用于使用 printf、perror 等函数进行输入输出操作
#include <stdio.h>
// 包含标准库,提供 exit 等函数用于程序退出等操作
#include <stdlib.h>
// 包含字符串处理库,提供 memset、strlen 等字符串操作函数
#include <string.h>
// 包含 Unix 标准库,提供 close、read、write 等系统调用函数
#include <unistd.h>
// 包含网络地址转换库,提供 inet_ntoa、htons 等网络地址转换函数
#include <arpa/inet.h>
// 包含线程相关的头文件,用于创建和管理线程
#include <pthread.h>// 定义服务器监听的端口号
#define PORT 8080
// 定义数据缓冲区的大小
#define BUFFER_SIZE 1024/*** 定义数据包结构体,用于在客户端和服务器之间传输数据* type 数据包类型,0 表示消息,1 表示断开连接* data 数据包携带的数据*/
typedef struct {int type;  // 0 for message, 1 for disconnectchar data[BUFFER_SIZE];
} Packet;// 全局变量,存储客户端套接字文件描述符
int client_fd;/*** 线程函数,用于接收服务器发送的消息* arg 线程函数的参数,此处未使用* @return 线程返回值,此处为 NULL*/
void *receive_messages(void *arg) {// 定义数据包变量,用于存储从服务器接收的数据包Packet packet;// 进入无限循环,持续接收服务器消息while (1) {// 清空数据包memset(&packet, 0, sizeof(Packet));if (read(client_fd, &packet, sizeof(Packet)) > 0) {// 若成功从服务器读取数据if (packet.type == 0) {// 若数据包类型为消息,输出接收到的消息printf("Received from server: %s", packet.data);} else if (packet.type == 1) {// 若数据包类型为断开连接,输出服务器断开连接的信息printf("Server disconnected\n");// 关闭客户端套接字close(client_fd);// 退出程序,返回失败状态exit(EXIT_FAILURE);}} else {// 若读取数据失败,说明服务器断开连接printf("Server disconnected\n");// 关闭客户端套接字close(client_fd);// 退出程序,返回失败状态exit(EXIT_FAILURE);}}return NULL;
}/*** 主函数,客户端程序的入口点* @return 程序的退出状态码,0 表示正常退出*/
int main() {// server_addr 存储服务器的地址信息struct sockaddr_in server_addr;// packet 用于存储要发送的数据包Packet packet;// thread_id 存储线程的标识符pthread_t thread_id;// 创建客户端套接字,使用 IPv4 地址族和 TCP 协议if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {// 若套接字创建失败,输出错误信息并退出程序perror("socket failed");exit(EXIT_FAILURE);}// 设置服务器地址信息server_addr.sin_family = AF_INET;// 将端口号从主机字节序转换为网络字节序server_addr.sin_port = htons(PORT);// 将服务器的 IP 地址转换为网络字节序server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");// 连接到服务器if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {// 若连接失败,输出错误信息,关闭套接字并退出程序perror("connect");close(client_fd);exit(EXIT_FAILURE);}// 输出连接成功的信息printf("Connected to server\n");// 创建线程来接收消息if (pthread_create(&thread_id, NULL, receive_messages, NULL) != 0) {// 若线程创建失败,输出错误信息,关闭套接字并退出程序perror("pthread_create");close(client_fd);exit(EXIT_FAILURE);}// 进入无限循环,持续从标准输入读取数据并发送给服务器while (1) {// 清空数据包的数据部分memset(packet.data, 0, BUFFER_SIZE);if (fgets(packet.data, BUFFER_SIZE, stdin) != NULL) {// 若成功从标准输入读取数据// 设置数据包类型为消息packet.type = 0;// 发送数据包给服务器send(client_fd, &packet, sizeof(Packet), 0);}}// 关闭客户端套接字close(client_fd);return 0;
}

相关文章:

《Linux 网络架构:基于 TCP 协议的多人聊天系统搭建详解》

一、系统概述 本系统是一个基于 TCP 协议的多人聊天系统&#xff0c;由一个服务器和多个客户端组成。客户端可以连接到服务器&#xff0c;向服务器发送消息&#xff0c;服务器接收到消息后将其转发给其他客户端&#xff0c;实现多人之间的实时聊天。系统使用 C 语言编写&#x…...

目前主要虚拟世界平台在单一实例承载人数和伺服器架构的综合比较分析(从开资料和技术推估):

目前主要虚拟世界平台在单一实例承载人数和伺服器架构的综合比较分析&#xff08;从开资料和技术推估&#xff09;&#xff1a; 1. 《Fortnite》&#xff08;Epic Games&#xff09; 一般游戏模式约 100人/场&#xff0c;但大型活动&#xff08;如演唱会&#xff09;采用分层串…...

鸿蒙NEXT项目实战-百得知识库04

代码仓地址&#xff0c;大家记得点个star IbestKnowTeach: 百得知识库基于鸿蒙NEXT稳定版实现的一款企业级开发项目案例。 本案例涉及到多个鸿蒙相关技术知识点&#xff1a; 1、布局 2、配置文件 3、组件的封装和使用 4、路由的使用 5、请求响应拦截器的封装 6、位置服务 7、三…...

函数的介绍

1.函数的概念 在C语言中也有函数的概念&#xff0c;有些翻译为&#xff1a;子程序&#xff0c;这种翻译更为准确。C语言的函数就是一个完成某项特定的任务的一小段代码。这段代码是有特殊的写法和调用方法的。 C语言的程序其实是有无数个小的函数组合而成的&#xff0c;也可以…...

源自Deformable Convolutional Networks的一种可变形卷积实现解析

衍生记录&#xff1a;深度学习pytorch之简单方法自定义9类卷积即插即用 文章目录 概述1. 可变形卷积的背景2. DeformConv2D概述2.1 构造函数分析2.2 前向传播函数解析2.2.1 偏移量的计算与应用2.2.2 目标位置的计算2.2.3 四个角的插值2.2.4 双线性插值的权重2.2.5 特征图的采样…...

记一次性能调优-20250320

2月份年后上班&#xff0c;刚过完年&#xff0c;还没从喜悦中解放出来&#xff0c;凌晨3点的时候同事就给我打电话&#xff0c;晚上的批量处理任务卡住了&#xff0c;快帮忙看看&#xff0c;做了几分钟的心里建设之后从被窝爬起来&#xff0c;看着手机上好几电话&#xff0c;赶…...

Postman高级功能深度解析:Mock Server与自动化监控——构建高效API测试与监控体系

引言&#xff1a;Postman在API开发中的核心价值 在数字化时代&#xff0c;API&#xff08;应用程序编程接口&#xff09;已成为系统间交互的“神经网络”&#xff0c;其质量直接影响用户体验与业务连续性。然而&#xff0c;传统API测试面临两大挑战&#xff1a; 开发阶段依赖…...

【最后203篇系列】020 rocksdb agent

今天还是挺开心的一天&#xff0c;又在工具箱里加了一个工具。嗯&#xff0c;但是快下班的时候也碰到一些不太顺心的事&#xff0c;让我有点恼火。我还真没想到一个专职的前端&#xff0c;加测试&#xff0c;以及其他一堆人&#xff0c;竟然不知道后端返回的markdown,在前端渲染…...

OpenCV旋转估计(2)用于自动检测波浪校正类型的函数autoDetectWaveCorrectKind()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::detail::autoDetectWaveCorrectKind 是 OpenCV 中用于自动检测波浪校正类型的函数&#xff0c;它根据输入的旋转矩阵集合来决定使用哪种波浪…...

mysql-connector-python 报错(0xC0000005)

报错情况&#xff1a; 原因&#xff1a; mysql-connector-python版本不对&#xff0c;我们的mysql版本为sql8.0需要下载mysql-connector-python8.0....的库 方法&#xff1a; pip install mysql-connector-python8.1.0 即可...

从零开始实现Stable Diffusion本地部署

1. 依赖安装 文件打包下载地址&#xff08;Stable Diffusion&#xff09; # git &#xff1a; 用于下载源码 https://git-scm.com/downloads/win # Python 作为基础编译环境 https://www.python.org/downloads/ # Nvidia 驱动&#xff0c;用于编译使用GPU显卡硬件 https://ww…...

RAG各类方法python源码解读与实践:利用Jupyter对RAG技术综合评测【3万字长文】

检索增强生成&#xff08;RAG &#xff09;是一种结合信息检索与生成模型的混合方法。它通过引入外部知识来提升语言模型的性能&#xff0c;从而提高回答的准确性和事实正确性。为了简单易学&#xff0c;不使用LangChain框架或FAISS向量数据库&#xff0c;而是利用Jupyter Note…...

transform C++标准库算法用法(对容器中元素进行转换操作:大小写转换、向量的加法乘法运算)

std::transform 是 C 标准库中的一个算法&#xff0c;用于对容器&#xff08;如数组、向量、字符串等&#xff09;中的元素进行转换操作&#xff0c;并将结果存储到指定的目标位置。它可以对单个范围或两个范围的元素进行操作&#xff0c;并将结果写入另一个容器。 1. 头文件 …...

Java File 类与文件操作

一、引言 在 Java 编程中,文件操作是一项非常常见且重要的任务。无论是读取配置文件、保存用户数据,还是进行日志记录,都离不开对文件的操作。Java 提供了 File 类来表示文件和目录的抽象路径名,通过该类可以对文件和目录进行创建、删除、重命名等操作。同时,Java 还提供…...

Python 字符串的编码格式

在 Python 中,字符串(str)默认使用 Unicode 编码,具体来说,Python 3 中的字符串是以 UTF-8 编码存储的 Unicode 字符序列。Unicode 是一种国际标准,旨在为世界上所有的字符提供唯一的编码,而 UTF-8 是 Unicode 的一种实现方式,具有兼容性和高效性。 1. Unicode 与 UTF-…...

RPA+AI 技术到底好在哪里?

在自动化领域&#xff0c;RPA与生成式AI都是强大的技术&#xff0c;都可以用来实现自动执行重复耗时的任务。 主要区别是&#xff1a;传统RPA擅长处理结构化与规则明确简单的流程&#xff0c;而在非结构化数据处理、动态上下文适应、智能决策等能力上有欠缺&#xff1b;而基于…...

flowable适配达梦7 (2.1)

经过第一版的问题解决&#xff0c;后端项目可以启动&#xff0c;前端页面也集成进去。 前端在流程设计页面报错 之后发现主要是组件中modelerStore这个值没有 解决方法:在data增加对象 给component/process/designer.vue 中涉及到的每个子组件传入 :modelerStore“modeler…...

基于java的ssm+JSP+MYSQL的母婴用品网站(含LW+PPT+源码+系统演示视频+安装说明)

系统功能 管理员功能模块&#xff1a; 主页 个人中心 用户管理 商品分类管理 商品信息管理 留言板管理 成长交流 系统管理 订单管理 留言管理 用户功能模块&#xff1a; 主页 个人中心 我的收藏管理 订单管理 前台首页功能模块&#xff1a; 首页 商品信息 论…...

面试八股 —— Redis篇

重点&#xff1a;缓存 和 分布式锁 缓存&#xff08;穿透&#xff0c;击穿&#xff0c;雪崩&#xff09; 降级可作为系统的保底策略&#xff0c;适用于穿透&#xff0c;击穿&#xff0c;雪崩 1.缓存穿透 2.缓存击穿 3.缓存雪崩 缓存——双写一致性 1.强一致性业务&#xff08…...

gradle-8.13

gradle-8.13 稍微看了下&#xff0c;基于Maven改造的 https://gradle.org/install/https://github.com/gradle/gradle-distributions/releaseshttps://github.com/gradle/gradle-distributions/releases/download/v8.13.0/gradle-8.13-all.zip https://github.com/gradle/gra…...

不使用负压电源,ADC如何测量正负压?

电路图来自ZLinear的开源数据采集板卡DL8884_RFN&#xff0c;是一个比较常见的电压偏置采集法 对电路进行分析&#xff0c;所以说可以先看下采集卡的模拟输入部分的参数如下&#xff1a; 通道数量: 8通道单端输入/4通道差分输入 分辨率: 16位逐次逼近型(SAR) ADC 采样速率: 40…...

在 Linux 系统上部署 Deepseek AI 的全面指南‌

对于所有希望亲身体验 AI 魅力的玩家来说&#xff0c;本文将提供一个详尽的教程&#xff0c;指导你在 Linux 系统上部署 Deepseek AI。无论你是技术小白还是有一定基础的用户&#xff0c;都能轻松跟随本文完成部署。 ‌一、关于 Ollama‌ Ollama 是一款功能强大的开源应用&am…...

SinoSteel生产企业ERP实施建议书final(143页PPT)(文末有下载方式)

资料解读&#xff1a;SinoSteel 生产企业 ERP 实施建议书 final 详细资料请看本解读文章的最后内容。 在当今竞争激烈的商业环境中&#xff0c;企业的信息化建设对于提升竞争力和实现可持续发展至关重要。中钢集团作为一家大型跨国企业集团&#xff0c;在钢铁行业占据重要地位。…...

贴片陶瓷天线和蓝牙天线的layout注意事项

板载天线&#xff0c;也有封装成器件进行使用&#xff1a; 看到这里&#xff0c;细心的人发现&#xff0c;天线接入芯片的时候&#xff0c;旁边也直接接地了&#xff1a; F型天线&#xff08;Inverted F Antenna, IFA&#xff09;的一端接地&#xff0c;看起来像是“短路”&am…...

关于波士顿动力2025年3月的人形机器人最新视频

这是完整的视频&#xff1a; 波士顿动力最新逆天表演-机器人Atlas行走、奔跑、爬行、杂技_哔哩哔哩_bilibili 至少从目前来看&#xff0c;综合对比运动的幅度、各关节的协调性、整体的顺遂性、动作的拟人程度&#xff0c;波士顿动力是已知人形机器人中最好的。 尤其需要关注…...

fontTools工具的使用介绍

前言 python工具库fontTools&#xff0c;我是用来压缩前端字体的&#xff0c;优化前端请求速度的&#xff1b;使用的过程中&#xff0c;遇到了不少的坑&#xff0c;把这个过程记录下来&#xff0c;防止再犯。 安装 # fontTools 4.56.0 pip install fontTools提取子字体集 方…...

深入理解计算机网络:OSI 与 TCP/IP 各层结构与功能

目录 1. 引言 2. OSI 模型 2.1 OSI 各层的详细功能 2.1.1 物理层 2.1.2 数据链路层 2.1.3 网络层 2.1.4 传输层 2.1.5 会话层 2.1.6 表示层 2.1.7 应用层 3. TCP/IP 模型 3.1 TCP/IP 各层的详细功能 3.1.1 网络接口层 3.1.2 网络层 3.1.3 传输层 3.1.4 应用层 …...

Wi-Fi NAN 架构(Wi-Fi Aware Specification v4.0,第2章:2.3~2.6)

1. NAN 数据通信架构 1.1 单播支持 要在两个NAN设备之间启动单播数据通信&#xff0c;服务需发起一个NAN数据路径&#xff08;NDP&#xff0c;NAN Data Path&#xff09;请求。这对NAN设备之间会建立一个NAN设备链路&#xff08;NDL&#xff0c;NAN Device Link&#xff09;&…...

Arduino示例代码讲解:Multiple tone player 多音播放器

Arduino示例代码讲解:Multiple tone player 多音播放器 Multiple tone player 多音播放器代码功能代码逐行解释1. 注释部分2. `setup()` 函数3. `loop()` 函数硬件连接**扬声器连接**:**Arduino板**:运行结果修改建议关于`tone()`和`noTone()`函数视频讲解Multiple tone pla…...

Windows电脑 打开 Docker Desktop 提示的“Virtual Machine Platform not enabled”的问题解决

文章目录 问题来源解决方案 问题来源 因为要在本地推送 docker 镜像 到镜像库&#xff0c;所以安装了 Docker Desktop &#xff0c;刚开始是好的&#xff0c;后来因为要调试 uniapp 安卓App&#xff0c;又装了雷电模拟器。然后就冲突了&#xff0c;打不开 Docker Desktop了&am…...