【网络】UDP和TCP之间的差别和回显服务器
文章目录
- UDP 和 TCP 之间的差别
- 有连接/无连接
- 可靠传输/不可靠传输
- 面向字节流/面向数据报
- 全双工/半双工
- UDP/TCP API 的使用
- UDP API
- DatagramSocket
- 构造方法
- 方法
- DatagramPacket
- 构造方法
- 方法
- 回显服务器(Echo Server)
- 1. 接收请求
- 2. 根据请求计算响应
- 3. 将响应写回客户端
- 完整代码
学习多线程,打破了以往对于程序的认知
学习网络编程,将会再次打破对于程序的认知
套接字:Socket 单词
操作系统给应用程序(传输层给应用层)提供的 API,起了个名字,就叫 Socket API
Socket 本身是“插槽”的意思
- 电脑的主板,插着各种其他的硬件
接下来学习的就是操作系统提供的 Socket API(Java 版本的)
UDP 和 TCP 之间的差别
socket API 提供了两组不同的 API,UDP 有一套,TCP 也有一套
TCP 有连接,可靠传输,面向字节流,全双工
UDP 无连接,不可靠传输,面向数据报,全双工
有连接/无连接
此处谈到的连接,是“抽象”的连接
- 通信双方,如果保存了通信对端的信息,就相当与是“有连接”;如果不保存对端的信息,就是“无连接”
- 连接:通信双方 A 保存了 B 的信息(IP 和端口号),B 也保存了 A 的信息
- 如果通信双方,各自把对方的信息删除掉,此时就相当与“断开了连接”
举个栗子:
- 将来你和你的另一半去领证,结婚证上就会写上两个人的名字,贴上照片。一式两份,你保存一份,你的另一半保存一份
- 你的本上保留了 ta 的信息,你翻开本就能看到另一个人是 ta
- ta 的本上保留了你的信息,ta 翻开本就能看到另一个人是你
- 此时你们俩就相当于建立了“抽象的/逻辑上的连接”
可靠传输/不可靠传输
此处谈到的“可靠”,不是指 100% 能到达对方,而是 “尽可能”到达对方
- 因为网络环境非常复杂,存在很多的不确定因素(你再厉害的技术,也抵不过挖掘机一铲子)
相对来说,不可靠就是完全不考虑数据是否能到达对方
TCP 内置了一些机制,能够保证可靠传输
- 感知到对方是不是收到了
- 重传机制,在对方没收到的时候进行重试
UDP 则没有这种可靠性机制,完全不管发出去的数据是否顺利到达对方
直观感觉,可靠比不可靠传输更好?
- 但可靠传输要付出代价,TCP 协议设计就要比 UDP 复杂很多,也会损失一些传输数据的效率
面向字节流/面向数据报
TCP 是面向字节流的,TCP 的传输过程就和文件流/水流是一样的特点
- 从文件读写 100 个字节
- 一次读写 100 字节
- 两次,一次读写 50 字节
- 十次,一次读写 10 字节
- …
- TCP 读写,和文件读写是一摸一样的
UDP 是面向数据报的,传输数据的基本单位不是字节,而是“UDP 数据报”
- 一次发送/接收,必须是完整的 UDP 数据报
这些差别,会直接影响到代码的写法
全双工/半双工
全双工:一个通信链路,可以发送数据,也可以接收数据(双向通信)
半双工:一个通信链路,只能发送/只能接收(单向通信)
有一根网线,怎么进行双向通信呢?
- 全双工这个事情,物理层面上,并非是只有一根线在连接
- 一根网线里,有 8 根铜线,分成 4 4 一组(四根就可以正常工作,另外四根是防止意外情况发生的铜线备份)
- 主要的四根线中,两根线用来负责发送,两根用来接收
UDP/TCP API 的使用
UDP API
API 就是一组函数/一组类
DatagramSocket
网卡的遥控器
代表一个 Socket 对象
- 属于操作系统的概念,
Socket就可以认为是操作系统中,广义的文件里面的一种文件类型- 这样的文件,就是网卡/控制台/键盘/显卡…这种硬件设备抽象的表示形式
- 所以
Socket也具有一些文件的特性,操作文件需要先打开、再读写、再关闭。Socket也是这样 - 包括创建一个
Socket对象,也会占用一个文件描述符表里面的资源
- 所以
- 在这里
Socket对象,就是网卡的代言人- 因为我们通过代码直接操作网卡是不好操作的
- 网卡有很多种型号,之间提供的
API都会有差别 - 于是操作系统就把网卡概念封装成
Socket,应用程序员就不需要关注硬件的差异和细节,直接统一操作Socket对象就能间接的操作网卡了 Socket就像万能遥控器一样
- 这样的文件,就是网卡/控制台/键盘/显卡…这种硬件设备抽象的表示形式
构造方法
| 方法签名 | 方法说明 | |
|---|---|---|
| DatagramSocket () | 创建⼀个 UDP 数据报套接字的 Socket,绑定到本机任意⼀个随机端⼝(⼀般⽤于客⼾端) | |
| DatagramSocket (int port) | 创建⼀个 UDP 数据报套接字的 Socket,绑定到本机指定的端⼝(需要指定端口号,⼀般⽤于服务端) |
方法
| 方法签名 | 方法说明 | |
|---|---|---|
| void receive (DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报,该⽅法会阻塞等待) | |
| void send (DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待,直接发送) | |
| void close () | 关闭此数据报套接字 |
DatagramPacket
UDP 传输数据的基本单位
代表一个 UDP 数据报
构造方法
| 方法签名 | 方法说明 | |
|---|---|---|
| DatagramPacket(byte[] buf, int length) | 构造⼀个 DatagramPacket 以⽤来接收数据报,接收的数据保存在字节数组(第⼀个参数 buf)中,接收指定 ⻓度(第⼆个参数 length) | |
| DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) | 构造⼀个 DatagramPacket 以⽤来发送数据报,发送的数据为字节数组(第⼀个参数 buf)中,从 0 到指定⻓ 度(第⼆个参数 length)。address 指定⽬的主机的 IP 和端⼝号 | |
方法
| 方法签名 | 方法说明 | |
|---|---|---|
| InetAddress getAddress() | 从接收的数据报中,获取发送端主机 IP 地址;或从发送的数据报中,获取接收端主机 IP 地址 | |
| int getPort() | 从接收的数据报中,获取发送端主机的端⼝号;或从发送的数据报中,获取接收端主机端口号 | |
| byte[] getData() | 获取数据报中的数据 |
回显服务器(Echo Server)
最简单的客户端服务器程序,不涉及到业务流程,只是对与 API 的用法做演示
客户端发送什么样的请求,服务器就返回什么样的响应,没有任何业务逻辑,没有进行任何计算或者处理
- 网络编程必须要使用网卡,就需要用到
Socket对象- 创建一个
DatagramSocket对象,之后在基于这个对象进行操作
- 创建一个
import java.net.DatagramSocket;
import java.net.SocketException; public class UdpEchoServer { private DatagramSocket socket = null; public UdpEchoServer(int port) throws SocketException { //SocketException 异常是 IOException 的子类socket = new DatagramSocket(port); }
}
- 对于服务器这一端来说,需要在
socket对象创建的时候,就指定一个端口号port,作为构造方法的参数 - 后续服务器开始运行之后,操作系统就会把端口号和该进程关联起来
- 端口号的作用就是来区分进程的,一台主机上可能有很多个进程很多个程序,都要去操作网络。当我们收到数据的时候,哪个进程来处理,就需要通过端口号去区分
- 所以就需要在程序一启动的时候,就把这个程序关联哪个端口指明清楚
- 在调用这个构造方法的过程中,
JVM就会调用系统的Socket API,完成“端口号-进程”之间的关联动作- 这样的操作也叫“绑定端口号”(系统原生
API名字就叫bind) - 绑定好了端口号之后,就明确了端口号和进程之间的关联关系
- 这样的操作也叫“绑定端口号”(系统原生
- 对于一个系统来说,同一时刻,一个端口号只能被一个进程绑定;但是一个进程可以绑定多个端口号(通过创建多个
Socket对象来完成)- 因为端口号是用来区分进程,收到数据之后,明确说这个数据要给谁,如果一个端口号对应到多个进程,那么就难以起到区分的效果
- 如果有多个进程,尝试绑定一个端口号,只有一个能绑定成功,后来的都会绑定失败
1. 接收请求
- 通过
start来启动服务器的核心流程 - 对于服务器来说,主要的工作,就是不停地处理客户端发来的请求,因为客户端什么时候会发来请求是未知的,所以要时刻待命
public void start() { System.out.println("服务器启动!"); //通过一个死循环来不停地处理请求 while(true) { //1. 读取客户端的请求并解析socket.receive(); }
}
- 对
7*24小时工作的服务器来说,服务器里面有死循环是很正常的,不是说死循环就是代码bug
- 读取客户端的请求并解析
receive是从网卡上读取数据,但是调用receive的时候,网卡上不一定就有数据- 当调用
start方法之后程序启动,就立刻调用了receive,一调用receive,就会立刻从网卡中读取数据,但这个时候客户端可能还没来,网卡中还没有数据 - 如果网卡上收到数据了,
receive立刻返回,获取收到的数据;如果没有收到数据,receive就会阻塞等待,直到真正收到数据为止 - 此处
receive也是通过“输出型参数”获取到网卡上收到的数据的
receive的参数是DatagramPacket- 我们就需要构造一个空的
DatagramPacket对象,将其作为参数传递给receive
- 我们就需要构造一个空的
public void start() throws IOException { System.out.println("服务器启动!"); //通过一个死循环来不停地处理请求 while(true) { //1. 读取客户端的请求并解析 DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096); socket.receive(requestPacket); }
}
-
DatagramPacket自身需要存储数据,但是数据的空间具体多大,需要外部来定义,自身不负责 -
需要指定
requestPacket所需要存储数据/持有数据的基数- 指定一个字节数组,和其长度
- 大小没什么讲究,只要能确保能够存储下你通讯的一个数据包即可
-
收到的请求数据是通过二进制
byte[]的形式来体现的,而我们后续要将其进行处理,最好将它转成字符串才好处理
public void start() throws IOException { System.out.println("服务器启动!"); //通过一个死循环来不停地处理请求 while(true) { //1. 读取客户端的请求并解析 DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096); socket.receive(requestPacket); //将收到的二进制 byte[] 数据转换成字符串 String request = new String(requestPacket.getData(),0,requestPacket.getLength()); }
}
- 构造
String可以基于字节数组构造,也可以基于字符数组进行构造- 此处
DatagramPacket里面持有的就是字节数组,我们就取出里面包含的字节数 - 此处就指定了:是哪个字节数组、从哪开始构造、构造多长
- 此处
2. 根据请求计算响应
- 请求(request):客户端主动给服务器发起的数据
- 响应(response):服务器给客户端返回的数据
此处是一个回显服务器,响应就是请求
public void start() throws IOException { System.out.println("服务器启动!"); //通过一个死循环来不停地处理请求 while(true) { //1. 读取客户端的请求并解析 DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096); socket.receive(requestPacket); //将收到的二进制 byte[] 数据转换成字符串 String request = new String(requestPacket.getData(),0,requestPacket.getLength()); //2. 根据请求计算响应 String response = process(request); }
} //请求是什么,响应就是什么
private String process(String request) { return request;
}
3. 将响应写回客户端
此时需要主动的将数据通过网卡发送回客户端
- 与
receive相似,send的参数是DatagramPacket- 我们就需要构造一个
DatagramPacket对象,将其作为参数传递给send - 但此时不能使用空的数组来构造
DatagramPacket对象 - 需要使用刚刚的
response数据进行构造
- 我们就需要构造一个
public void start() throws IOException { System.out.println("服务器启动!"); //通过一个死循环来不停地处理请求 while(true) { //1. 读取客户端的请求并解析 DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096); socket.receive(requestPacket); //将收到的二进制 byte[] 数据转换成字符串 String request = new String(requestPacket.getData(),0,requestPacket.getLength()); //2. 根据请求计算响应 String response = process(request); //3. 把响应写回到客户端 DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length, requestPacket.getSocketAddress()); socket.send(responsePacket); }
} //请求是什么,响应就是什么
private String process(String request) { return request;
}
String可以基于字节数组来构造,也可以随时取出里面的字节数组response.getBytes().length不能写成response.length- 前者是在获取字节数组,得到字节数组的长度,单位是“字节”
- 后者是在获取字符串中字符的个数,单位是“字符”
UDP有一个特点——无连接- 所谓的连接,就是通信双方保存对方的信息(IP+端口号)
- 就是说
DatagramSocket这个对象中,不持有对方(客户端)和 IP 端口的,进行send的时候,就需要在send的数据包里,把要“发给谁”这样的信息,写进去,才能够正确的把数据进行返回 - 所以要将信息也作为参数,传入
responsePacket中- 客户端刚才给服务器发了一个请求
requestPacket,这个包记录了这个数据是从哪来,从哪来就让它回哪去,所以直接获取这个requestPacket的信息就可以了 - 客户端的 IP 和端口就都包含在
requestPacket.getSocketAddress()中 - 后续往外发送数据包的时候,就知道该发去哪了
>- 相比之下,TCP代码中,因为TCP是有连接的,则无需关心对端的 IP 和端口,只管发送数据即可
- 客户端刚才给服务器发了一个请求
- 如果字符串里都是英文字母/阿拉伯数字/英文标点符号的话,都是
ASCII编码的,一个字符也就是一个字节这么长- 如果字符串里有中文,是
UTF8编码的,一个中文就是 3 个字节UTF8也是能兼容ASCII,当使用UTF8表示英文的时候,和ASCII表示英文是完全相同的
完整代码
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException; public class UdpEchoServer { private DatagramSocket socket = null; public UdpEchoServer(int port) throws SocketException { socket = new DatagramSocket(port); } public void start() throws IOException { System.out.println("服务器启动!"); //通过一个死循环来不停地处理请求 while(true) { //1. 读取客户端的请求并解析 DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096); socket.receive(requestPacket); //将收到的二进制 byte[] 数据转换成字符串 String request = new String(requestPacket.getData(),0,requestPacket.getLength()); //2. 根据请求计算响应 String response = process(request); //3. 把响应写回到客户端 DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length, requestPacket.getSocketAddress()); socket.send(responsePacket); //4. 打印日志 System.out.printf("[%s:%d req=%s, res=%s\n",requestPacket.getAddress(),requestPacket.getPort(),request,response); } } //请求是什么,响应就是什么 private String process(String request) { return request; } public static void main(String[] args) throws IOException { UdpEchoServer server = new UdpEchoServer(9090); server.start(); }
}
相关文章:
【网络】UDP和TCP之间的差别和回显服务器
文章目录 UDP 和 TCP 之间的差别有连接/无连接可靠传输/不可靠传输面向字节流/面向数据报全双工/半双工 UDP/TCP API 的使用UDP APIDatagramSocket构造方法方法 DatagramPacket构造方法方法 回显服务器(Echo Server)1. 接收请求2. 根据请求计算响应3. 将…...
Electron:摄像头录制和屏幕录制
摄像头录制 main.js const { app, BrowserWindow} require(electron)let mainWin null const createWindow () > {mainWin new BrowserWindow({width: 800,height: 600,title: 自定义菜单,webPreferences: {// 允许渲染进程使用nodejsnodeIntegration: true,// 允许渲…...
【uniapp】vue3+vite配置tailwindcss
安装 npm install autoprefixer tailwindcss uni-helper/vite-plugin-uni-tailwind -Dautoprefixer :自动管理浏览器前缀的插件,可以解析css文件并且添加前缀到css内容里。uni-helper/vite-plugin-uni-tailwind: 将 Tailwind CSS 框架集成到使用 Vite 作…...
从源码到应用:医疗陪诊系统与在线问诊小程序开发详解
在数字化医疗时代,医疗陪诊系统与在线问诊小程序的开发成为了医疗机构和技术公司关注的焦点。接下来,小编将与您一同深入了解。 一、医疗陪诊系统的核心功能 医疗陪诊系统旨在为患者提供更贴心的医疗服务,通过专业人员陪同患者完成就医过程。…...
mysql数据库中decimal数据类型比较大小
在MySQL中,DECIMAL数据类型用于存储精确的数值,它非常适合用于需要高精度计算的场景,如金融应用。当我们需要在MySQL数据库中比较DECIMAL类型数据的大小时,可以使用标准的比较运算符,如>, <, >, <, 和 &l…...
掌控库存,简化管理 — InvenTree 开源库存管理系统
InvenTree :简化您的库存管理,让效率和控制力触手可及。- 精选真开源,释放新价值。 概览 InvenTree,一款专为精细化库存管理而设计的开源系统,以其高效和灵活性在众多库存管理工具中脱颖而出。它以Python和Django框架…...
Linux---项目自动化构建工具-make/Makefile
一、背景 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的 规则来指定,哪些文件需要先编译,哪些文件…...
嘉立创EDA个人学习笔记1(PCB板介绍)
前言 本篇文章属于嘉立创EDA的学习笔记,来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记,只能做参考,细节方面建议观看视频,肯定受益匪浅。 嘉立创EDA-PCB设计零基础入门课程(54集全)_…...
(转)Restful接口设计(1)
.representational:代表性的 URI(Universal Resouce Identifier):Universal :普遍的;共同的。Identifier:标识符。统一资源标识符。 31-RESTful接口介绍-02_哔哩哔哩_bilibili 31-RESTful接口介绍-03_哔哩…...
Python进阶之3D图形
Python进阶之3D图形 在数据可视化中,2D图形通常可以满足大多数需求。然而,对于一些复杂的数据或分析,3D图形可以提供更多的视角和洞察。在Python中,使用 Matplotlib 和 Plotly 等库可以轻松创建各种3D图形。本文将介绍如何使用这…...
机器学习深度学习中的搜索算法浅谈
机器学习&深度学习中的搜索算法浅谈 搜索算法是计算机科学中的核心算法,用于在各种数据结构(如数组、列表、树、图等)中查找特定元素或信息。这些算法不仅在理论上具有重要意义,还在实际应用中扮演着关键角色。本文将详细探讨…...
基于IMX8M_plus+FPGA+AI监护仪解决方案
监护仪是一种以测量和控制病人生理参数,并可与已知设定值进行比较,如果出现超标可发出警报的装置或系统。 (1)监护仪主要采集测量人体生理参数,心电、血压、血氧、体温等需要采集处理大量的数据,系统需要多…...
仿RabbitMq实现简易消息队列正式篇(路由匹配篇)
TOC 目录 路由匹配模块 代码展示 路由匹配模块 决定了一条消息是否能够发布到指定的队列 在每个队列根交换机的绑定信息中,都有一个binding_key(在虚拟机篇有说到)这是队列发布的匹配规则 在每条要发布的消息中,都有一个rout…...
一套完整的NVR网络硬盘录像机解决方案和NVR程序源码介绍
随着网络技术的发展,视频数据存储的需求激增,促使硬盘录像机(DVR)逐渐演变为具备网络功能的网络视频录像机(NVR)。NVR,即网络视频录像机,负责网络视音频信号的接入、存储、转发、解码…...
2024年人工智能固态硬盘采购容量预计超过45 EB
根据TrendForce发布的最新市场报告,人工智能(AI)服务器客户在过去两个季度显著增加了对企业级固态硬盘(SSD)的订单。为了满足AI应用中不断增长的SSD需求,上游供应商正在加速工艺升级,并计划在20…...
Java的反射原理
反射允许程序在运行时检查或修改其类、接口、字段和方法的行为。反射主要通过java.lang.reflect包中的类和接口实现,它主要用于以下目的: 在运行时分析类的能力:通过反射,可以在运行时检查类的结构,比如它的方法、构造…...
vue.config.js 配置
vue.config.js 文件是 Vue CLI 项目中的全局配置文件,它允许你以 JavaScript 的形式来配置构建选项,而不是通过命令行参数或者 .vue-clirc 的 JSON 格式。 官方文档: https://cli.vuejs.org/zh/config/#全局-cli-配置 基础配置 publicPath 设置构建好的…...
C ++ 也可以搭建Web?高性能的 C++ Web 开发框架 CPPCMS + MySQL 实现快速入门案例
什么是CPPCMS? CppCMS 是一个高性能的 C Web 开发框架,专为构建快速、动态的网页应用而设计,特别适合高并发和低延迟的场景。其设计理念类似于 Python 的 Django 或 Ruby on Rails,但针对 C 提供了更细粒度的控制和更高效的性能。…...
Taos 常用命令工作笔记(二)
最近测试创建一个涛思的数据库和一堆表进行测试,通过json配置文件配置字段的类型、名称等,程序通过解析json文件的配置,动态创建数据库的表。 其中表字段为驼峰结构的规则命名,创建表也是成功的,插入的测试数据也是成功…...
idea安装二进制文本阅读插件
引言 在软件开发过程中,有时需要查看二进制文件的内容以调试或分析问题。虽然有许多专用工具可以处理这类任务,但直接在 IDE 内集成这些功能无疑更加方便高效。本文将介绍如何在 IntelliJ IDEA 2023中安装和配置一个名为 BinEd的插件,以及如…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
pam_env.so模块配置解析
在PAM(Pluggable Authentication Modules)配置中, /etc/pam.d/su 文件相关配置含义如下: 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块,负责验证用户身份&am…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)
安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...
Unity中的transform.up
2025年6月8日,周日下午 在Unity中,transform.up是Transform组件的一个属性,表示游戏对象在世界空间中的“上”方向(Y轴正方向),且会随对象旋转动态变化。以下是关键点解析: 基本定义 transfor…...
水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
