嵌入式学习的第三十三天-进程间通信-UDP

一、网络

1.定义

不同主机间进程通信

主机间在硬件层面互联互通

主机在软件层面互联互通

2.国际网络体系结构

    OSI模型(7层): open system interconnect -------理论模型------定义了网络通信中不同层的协议
1977   国际标准化组织
各种不同体系结构的计算机能在世界范围内互联成网。

应用层:要传输的数据信息,如文件传输,电子邮件等
表示层:数据加密,解密操作,压缩,解压缩
会话层:建立数据传输通道
传输层:传输的方式  UDP  TCP   端口号
网络层:实现数据路由    路由器  ip
数据链路层:封装成帧,点对点通信(局域网内通信),差错检测   交换机  ARP
物理层:定义物理设备标准,比如网线,光纤等传输介质   比特流  bit  0 1

协议簇
TCP/IP模型:  工业模型(4层)
应用层:HTTP、HTTPS、FTP、TFTP、MQTT
传输层:TCP、UDP
网络层:IP
网络接口层:网络接口层既是传输数据的物理媒介,也可以为网络层提供一条准确无误的线路

5层
应用层:HTTP、HTTPS、FTP、TFTP、MQTT
传输层:TCP、UDP
网络层:IP
数据链路层:封装成帧,点对点通信(局域网内通信),差错检测   交换机
物理层:定义物理设备标准,比如网线,光纤等传输介质   比特流  bit

 3.协议

应用层协议:
FTP:文件传输协议(实现文件上传/下载)
TFTP:简单文件传输协议(实现文件上传/下载)
HTTP:超文本传输协议(实现超文本(集视频、图片、文字于一体的文件类型)传输)
HTTPS:加密版超文本传输协议
MQTT:消息队列遥测传输协议(物联网传输)----减少带宽

DNS域名解析协议

传输层协议:
UDP:用户数据报协议
TCP:传输控制协议

网络层:IP协议
IPv4    32位     IPv6    128位

4.IP地址

IP地址 = 网络位 + 主机位 ----区分不同主机,软件地址

网络位:该IP地址位于哪个网段(局域网)内
主机位:这个网段(局域网)第几台主机

子网掩码:
如:255.255.255.0 (点分十进制 )  
11111111.11111111.11111111.00000000  (计算机存储形式) 32bits
用来区分IP地址的网络位和主机位,搭配IP地址使用。
子网掩码是1的部分对应IP地址的网络位
子网掩码是0的部分对应IP地址的主机位

eg:192.168.0.121/24   24:网络位的位数

网段号: IP地址网络位不变,主机位全为0,则为该IP地址的网段号   网段内的IP能直接通信                              192.168.1.0

广播号:IP地址网络位不变,主机位全为1,则为该IP地址的广播号    向广播号发送信息,所有局域网内              IP都能收到此信息    192.168.1.255

网关地址:192.168.1.1

5.IP地址的划分

(1)A类地址:
范围:1.0.0.0 - 126.255.255.255
子网掩码:255.0.0.0          126*2^24        
用于管理大规模网络

私有IP地址:10.0.0.0 - 10.255.255.255

127.0.0.0   回环地址
(2)B类地址:
范围:128.0.0.0 - 191.255.255.255
子网掩码:255.255.0.0         2^16
管理大中规模网络

私有IP地址:172.16.0.0 - 172.31.255.255

(3)C类地址:
范围:192.0.0.0 - 223.255.255.255
子网掩码:255.255.255.0        2^8
管理中小规模网络

私有IP地址:192.168.0.0 - 192.168.255.255

(4)D类地址:
224.0.0.0 - 239.255.255.255
组播和广播使用

(5)E类地址:
240.0.0.0 - 255.255.255.254
用来进行实验

公有IP:由电信公司直接分配,并需要付费的IP地址, 可以直接访问internet
私有IP:不能直接访问internet的ip地址

6.端口号

范围:16位的数值  0-65535 2字节
作用:唯一的标识一个进程。每一个应用进程都有一个端口号;通讯时用来区分数据包属于哪一个进程。区分同一主机上的不同网络进程.

分类:
1)任何TCP/IP实现所提供的服务都用1-1023之间的端口号。
http : 80    FTP: 20/21     TFPT: 69     HTTPS: 443   MQTT: 833
2)端口号从1024-49151是被注册的端口号,被IANA指定为特殊服务使用。
3)从49152-65535是动态或私有端口号。
IP+PORT : 可以找到目标主机上的目标进程

7.TCP/IP封包,拆包过程

二、网络配置

1.ping命令

  检查两个主机之间是否网络联通
ping +IP地址/域名
ping www.baidu.com ----->dns(域名解析服务)

2.永久配置IP地址

(1)将虚拟机设置为桥接模式

   虚拟机 --》设置---》网络适配器---》桥接模式---》确定

(2)将虚拟机的IP地址桥接到无线网卡上

  编辑 --》虚拟网络编辑器---》更改设置---》桥接到能上网的网卡上去---》应用---》确定

(3)设置虚拟机的IP地址

 1) 打开虚拟机网卡配置文件:sudo vim /etc/network/interfaces

  2)将文件内容修改为如下:自动获取IP地址配置如下:(推荐)
auto lo
iface lo inet loopback 
auto ens33
iface ens33 inet dhcp

  3)重启网络服务 sudo /etc/init.d/networking restart

 3.ifconfig命令

 查看当前操作系统中网卡信息  
ens33:虚拟机中的网卡
lo:回环地址(127.0.0.1)
inet:IPv4地址
netmask:子网掩码
broadcast:广播号
inet6:IPv6地址
ether:MAC地址 

三、网络编程之 UDP

UDP(User Datagram Protocol):用户数据报协议---传输层

1.特性

(1).发送数据时不需要建立链接,节省资源开销
(2).不安全不可靠的协议 (尽最大交付的协议,可能存在丢包,乱序)//一般用在实时性比较高的广播,组播//vnc
(3).面向报文。
(4).资源开销小,效率高.

应用:允许数据丢失,要求实时性高的场景

2.网络编程模型

B/S :browser/server-->浏览器/服务器 

        (1)通用的客户端 (2)客户端无需开发 (3)请求的资源均来自于服务端

C/S :client/server--->客户端/服务器 

       (1)专用的客户端 (2)客户端和服务端都需开发 (3) 客户端可以存储部分资源

3.编程

网络套接字:文件描述符  为网络通信抽象出的一个通信端口

(1).socket 

  套接字:通信对象的抽象, 网络通信的端口,一个通信链的句柄。
int socket(int domain, int type, int protocol);
功能:创建一个用来通信的接口(文件描述符)
参数:domain:通信的协议族(AF_INET:IPv4协议族 AF_INET6:IPv6)
type:SOCK_DGRAM:数据报套接字 (UDP使用此类型)
SOCK_STEAM:流式套接字 (TCP使用此类型)
SOCK_RAW:原始套接字
protocol:默认传0 按照协议的默认属性创建
返回值:成功返回用来进行通信的文件描述符;失败返回-1 

(2).sendto 

 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
功能:向一个IP地址和端口发送数据信息
端口号:区分一台主机不同的应用程序(0 - 65535)
参数:sockfd:套接字文件描述符
buf:发送数据空间首地址
len:发送数据的长度
flags:发送数据属性(默认为0)
dest_addr:目的IP地址和端口
addrlen:目的IP地址和端口的长度
返回值:成功返回实际发送字节数;失败返回-1 

 man 7 ip  

struct sockaddr_in {
sa_family_t    sin_family; /* address family: AF_INET */
in_port_t      sin_port;   /* port in network byte order */
struct in_addr sin_addr;   /* internet address */
};

struct in_addr {
uint32_t       s_addr;     /* address in network byte order */
};

网络字节序:大端    主机字节序:小端  

 (3).htons----》主机转网络字节序

  主机:小端   host    网络:大端   network
uint32_t htonl(uint32_t hostlong);          主机转网络
uint16_t htons(uint16_t hostshort);        主机转网络
uint32_t ntohl(uint32_t netlong);            网络转主机
uint16_t ntohs(uint16_t netshort);          网络转主机

h:host     n:net     l:long     s:short 

(4).inet_addr

 in_addr_t  inet_addr(const char *cp);
功能:将字符串IP地址转换成二进制IP地址形式
char *inet_ntoa(struct in_addr in);
功能:将二进制ip转换成字符串


(5).recvfrom

  ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
功能:接收网络发送的数据信息
参数:sockfd:套接字文件描述符
buf:存放数据空间首地址
len:最大能够接收的字节数
flags:属性默认为0(阻塞方式)
src_addr:存放发送端IP地址信息的空间首地址
addrlen:想要接收的数据长度的空间首地址
返回值:成功返回实际接收字节数;失败返回-1 
具有阻塞功能(直到接收到数据,才会继续向下执行

(6).bind

  int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
功能:将一个套接字与IP地址和端口号绑定(只能绑定自己的IP地址)
参数:sockfd:套接字文件描述符 
addr:保存自己IP地址和端口号结构体首地址
addrlen:地址的长度
返回值:成功返回0;失败返回-1 

4.UDP 报文头

UDP首部有8个字节,由4个字段构成,每个字段都是两个字节:

+----------------------------------------------------------+
| 16位源端口 (2字节)  |  16位目的端口(2字节)  |
|-------------------------------------------------------------|
| 16位数据长度(2字节) |  16位校验和 (2字节)   |
|-------------------------------------------------------------|
|                                 数据                                 |
+--------------------------------------------- --------------+

(1).源端口: 源主机的应用程序使用的端口号。

(2).目的端口:目的主机的应用程序使用的端口号。

(3).长度:是指UDP头部和UDP数据的字节长度。因为UDP头 部长度为8字节,所以该字段的最小值为8。

(4).校验和:检测UDP数据报在传输中是否有错,有错则丢弃。

四、wireshark抓包工具的使用

1. 在线安装wireshark抓包工具
sudo apt-get install wireshark
sudo wireshark    //启动wireshark

2. wireshark作用
可以抓取通过网卡的数据包  用于软件代码出错调试和错误分子

3.使用方法
(1). sudo wireshark
(2). 选择网卡  双击any网卡
(3). 选择过滤条件  tcp.port == 80 || udp.port == 80
(4). 进行一次网络通信

五、练习

1.使用UDP实现局域网内两个人的全双工聊天

//ser.c
#include "head.h"int init_udp_ser(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("fail socket");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("fail bind");return -1;}return sockfd;
}int main(int argc, const char *argv[])
{int sockfd = init_udp_ser("192.168.1.162", 50000);if (sockfd < 0){return -1;}char buff[1024] = {0};struct sockaddr_in cliaddr;socklen_t clilen = sizeof(cliaddr);recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&cliaddr, &clilen);pid_t pid = fork();if (pid > 0){while (1){memset(buff, 0, sizeof(buff));ssize_t size = recvfrom(sockfd, buff, sizeof(buff), 0, NULL, NULL);if (size < 0){perror("fail recvfrom");close(sockfd);return -1;}printf("A-->B : %s\n", buff);}close(sockfd);}else if (0 == pid){while (1){memset(buff, 0, sizeof(buff));fgets(buff, sizeof(buff), stdin);sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&cliaddr, clilen);}close(sockfd);}else{perror("fail fork");return -1;}return 0;
}
//cli.c
#include "head.h"struct sockaddr_in seraddr;int init_udp_cli()
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("fail socket");return -1;}seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.1.162");return sockfd;
}int main(int argc, const char *argv[])
{int sockfd = init_udp_cli();if (sockfd < 0){return -1;}sendto(sockfd, "hello", 5, 0, (struct sockaddr *)&seraddr, sizeof(seraddr));pid_t pid = fork();if (pid > 0){char buff[1024] = {0};while (1){memset(buff, 0, sizeof(buff));fgets(buff, sizeof(buff), stdin);ssize_t size = sendto(sockfd, buff, strlen(buff), 0, (struct sockaddr *)&seraddr, sizeof(seraddr));if (size < 0){perror("fail sendto");close(sockfd);return -1;}}close(sockfd);}else if (0 == pid){char buff[1024] = {0};while (1){memset(buff, 0, sizeof(buff));ssize_t size = recvfrom(sockfd, buff, sizeof(buff), 0, NULL, NULL);if (size < 0){perror("fail recvfrom");close(sockfd);return -1;}printf("B-->A: %s\n", buff);}close(sockfd);}else{perror("fail fork");return -1;}return 0;
}
#ifndef __HEAD_H__
#define __HEAD_H__#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>#endif

2. 使用UDP实现一个文件的传输

//ser.c
#include "head.h"int init_udp_ser(const char *ip, unsigned short port)
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("fail socket");return -1;}struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(port);seraddr.sin_addr.s_addr = inet_addr(ip);int ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));if (ret < 0){perror("fail bind");return -1;}return sockfd;
}int recv_file(int sockfd, const char *filename)
{int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);if (fd < 0){perror("fail open file");return -1;}char buff[1024] = {0};off_t len;recvfrom(sockfd, &len, sizeof(len), 0, NULL, NULL);printf("len = %ld\n", len);while (1){ssize_t size = recvfrom(sockfd, buff, sizeof(buff), 0, NULL, NULL);len -= size;write(fd, buff, size);printf("len = %ld\n", len);if (len <= 0){break;}}close(fd);
}int main(int argc, const char *argv[])
{int sockfd = init_udp_ser("192.168.1.162", 50000);if (sockfd < 0){return -1;}recv_file(sockfd, "2.jpg");close(sockfd);return 0;
}
#include "head.h"struct sockaddr_in seraddr;int init_udp_cli()
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){perror("fail socket");return -1;}seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.1.162");return sockfd;
}int send_file(int sockfd, const char *filename)
{int fd = open(filename, O_RDONLY);if (fd < 0){perror("fail open file");return -1;}char buff[1024] = {0};off_t len = lseek(fd, 0, SEEK_END);lseek(fd, 0, SEEK_SET);sendto(sockfd, &len, sizeof(len), 0, (struct sockaddr *)&seraddr, sizeof(seraddr));printf("len = %ld\n", len);while (1){ssize_t size = read(fd, buff, sizeof(buff));if (size <= 0){break;}size = sendto(sockfd, buff, size, 0, (struct sockaddr *)&seraddr, sizeof(seraddr));if (size < 0){perror("fail sendto");close(fd);return -1;}usleep(1);}close(fd);return 0;
}int main(int argc, const char *argv[])
{int sockfd = init_udp_cli();if (sockfd < 0){return -1;}send_file(sockfd, "1.jpg");close(sockfd);return 0;
}

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

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

相关文章

4、Spring AI_DeepSeek模型_结构化输出

一、前言 Spring AI 提供跨 AI 供应商&#xff08;如 OpenAI、Hugging Face 等&#xff09;的一致性 API, 通过分装的ChatModel或ChatClient即可轻松调动LLM进行流式或非流式对话。 本专栏主要围绕着通过OpenAI兼容接口调用各种大语言模型展开学习&#xff08;因为大部分模型…

Spring Data Redis 从入门到精通:原理与实战指南

一、Redis 基础概念 Redis&#xff08;Remote Dictionary Server&#xff09;是开源的内存键值对数据库&#xff0c;以高性能著称。它支持多种数据结构&#xff08;String、Hash、List、Set、ZSet&#xff09;&#xff0c;并提供持久化机制&#xff08;RDB、AOF&#xff09;。 …

免费版酒店押金原路退回系统——仙盟创梦IDE

项目介绍​东方仙盟开源酒店押金管理系统是一款面向中小型酒店、民宿、客栈的轻量级前台管理工具&#xff0c;专注于简化房态管理、订单处理和押金跟踪流程。作为完全开源的解决方案&#xff0c;它无需依赖任何第三方服务&#xff0c;所有数据存储在本地浏览器中&#xff0c;确…

10. isaacsim4.2教程-RTX Lidar 传感器

1. 前言RTX Lidar 传感器Isaac Sim的RTX或光线追踪Lidar支持通过JSON配置文件设置固态和旋转Lidar配置。每个RTX传感器必须附加到自己的视口或渲染产品&#xff0c;以确保正确模拟。重要提示&#xff1a; 在运行RTX Lidar仿真时&#xff0c;如果你在Isaac Sim UI中停靠窗口&…

QT6 源,七章对话框与多窗体(14)栈式窗体 QStackedWidget:本类里代码很少。举例,以及源代码带注释。

&#xff08;1&#xff09;这不是本章节要用到的窗体组件&#xff0c;只是跟着标签窗体 QTabWidget 一起学了。这也是 QT 的 UI 界面里的最后几个容器了。而且本类也很简单。就了解一下它。 本类的继承关系如下 &#xff1a; UI 设计界面 &#xff1a;运行效果 &#xff1a;&…

魔百和M401H_国科GK6323V100C_安卓9_不分地区免拆卡刷固件包

魔百和M401H_国科GK6323V100C_安卓9_不分地区免拆卡刷固件包刷机说明&#xff1a;1&#xff0c;进机顶盒设置&#xff08;密码10086&#xff09;&#xff0c;在其他里&#xff0c;一直按左键约32下&#xff0c;打开调试模式2&#xff0c;进网络设置&#xff0c;查看IP地址。3&a…

MySQL基础02

一. 函数在 MySQL 中&#xff0c;函数是用于对数据进行特定处理或计算的工具&#xff0c;根据作用范围和返回结果的不同&#xff0c;主要分为单行函数和聚合函数&#xff08;又称分组函数&#xff09;。以下是详细介绍&#xff1a;1.单行函数单行函数对每一行数据单独处理&…

LabVIEW 视觉检测SIM卡槽

针对SIM 卡槽生产中人工检测效率低、漏检误检率高的问题&#xff0c;设计了基于 LabVIEW 机器视觉的缺陷检测系统。该系统通过光学采集与图像处理算法&#xff0c;实现对卡槽引脚折弯、变形、漏铜等缺陷的自动检测&#xff0c;误报率为 0&#xff0c;平均检测时间小于 750ms&am…

RocketMQ5.3.1的安装

1、下载安装 RocketMQ 的安装包分为两种&#xff0c;二进制包和源码包。1 下载 Apache RocketMQ 5.3.1的源码包后上传到linux https://dist.apache.org/repos/dist/release/rocketmq/5.3.1/rocketmq-all-5.3.1-source-release.zip2 解压编译 $ unzip rocketmq-all-5.3.1-source…

FunASR实时多人对话语音识别、分析、端点检测

核心功能&#xff1a;FunASR是一个基础语音识别工具包&#xff0c;提供多种功能&#xff0c;包括语音识别&#xff08;ASR&#xff09;、语音端点检测&#xff08;VAD&#xff09;、标点恢复、语言模型、说话人验证、说话人分离和多人对话语音识别等。FunASR提供了便捷的脚本和…

opencv--day01--opencv基础知识及基础操作

文章目录前言一、opencv基础知识1.opencv相关概念1.1背景1.2特点1.3主要功能与应用1.4.opencv-python2.计算机中的图像概念2.1图像表示2.2图像存储彩色图像二、opencv基础操作1.图像的读取2.图像的显示3.保存图像4.创建黑白图及随机像素彩图5. 图像切片&#xff08;图片剪裁&am…

如何撤销Git提交误操作

要撤销在主分支上的 git add . 和 git commit 操作&#xff0c;可以按照以下步骤安全回退&#xff1a; 完整回退步骤&#xff1a; # 1. 查看提交历史&#xff0c;确认要回退的commit git log --oneline# 示例输出&#xff1a; # d3f4g7h (HEAD -> main) 误操作提交 # a1b2c3…

React+Three.js实现3D场景压力/温度/密度分布可视化

本文介绍了一个基于React和Three.js的3D压力可视化解决方案&#xff0c;该方案能够&#xff1a; 加载并渲染3D压力模型数据 提供动态颜色映射功能&#xff0c;支持多种颜色方案&#xff1a;彩虹-rainbow,冷暖-cooltowarm,黑体-blackbody,灰度-grayscale 实现固定位置的颜色图…

Go 官方 Elasticsearch 客户端 v9 快速上手与进阶实践*

1、为什么选择 go-elasticsearch&#xff1f; 版本同步&#xff1a;与 Elasticsearch 主版本保持一一映射&#xff0c;当前稳定分支为 v9&#xff0c;对应 ES 9.x 系列。(GitHub)完全覆盖 REST API&#xff1a;所有 HTTP 端点都有等价方法&#xff0c;避免手写 JSON/HTTP。可插…

`/etc/samba/smb.conf`笔记250720

/etc/samba/smb.conf笔记250720 /etc/samba/smb.conf 是 Samba 服务的核心配置文件&#xff0c;用于实现 Linux/Unix 与 Windows 系统间的文件和打印机共享。以下详解其结构和常用参数&#xff1a; 配置文件结构 1. 全局设置段 [global] 控制 Samba 服务器的整体行为。 …

Java从入门到精通!第十六天,重点!(多线程和线程池)

一、多线程1&#xff0e;基本概念&#xff08;1&#xff09;程序&#xff08;Program&#xff09;&#xff1a;为了完成特定的任务&#xff0c;用某种计算机语言编写的一组指令的集合&#xff0c;即指一段静态的代码&#xff08;源代码经编译之后形成的二进制格式的文件&#x…

轨道交通为什么要有信号系统?

轨道交通为什么要有信号系统&#xff1f;轨道交通信号系统与公路信号系统有什么不同&#xff1f; 在轨道交通中信号系统是必不可少的&#xff0c;其根本原因在于&#xff1a;在轨道交通中已经没有办法纯靠人力去保证行车安全。 在公路交通中&#xff0c;信号其实是起辅助作用的…

docker 挂载卷

以下是针对您遇到的问题分步解答和解决方案&#xff1a;一、核心结论 ✅ 可以采用目录方式&#xff1a;您的命令中的 -v /root/nginx05-vol/:/usr/share/nginx/html/ 是正确的目录挂载语法。 ❌ 看不到新文件的可能原因主要集中在 权限问题、缓存机制 或 操作顺序错误 上。二、…

uniapp 报错 Not found ... at view.umd.min.js:1的问题

问题描述&#xff1a; uniapp的app中&#xff0c;当页面中使用多个v-if后会出现这个报错解决方案&#xff1a; 1、在v-if的地方加上key属性&#xff08;key属性要保证唯一&#xff09; 2、用v-show替换v-if&#xff08;不建议&#xff0c;可能会影响业务&#xff09;

水电站自动化升级:Modbus TCP与DeviceNet的跨协议协同应用

水电站的自动化系统就像一个精密的“神经中枢”&#xff0c;既要应对水流变化带来的动态负载&#xff0c;又得保证闸门启闭、水轮机调节等关键动作的精准性。我们去年参与的某水电站改造项目里&#xff0c;就遇到了一个典型问题&#xff1a;中控室的施耐德PLC采用Modbus TCP协议…