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

使用libmodbus库开发modbusTcp从站(支持多个主站连接)

使用libmodbus库开发modbusTcp从站(支持多个主站连接)

  • Chapter1 使用libmodbus库开发modbusTcp从站(支持多个主站连接)
    • rdsmodbusslave.h
    • rdsmodbusslave.cpp
    • main.cpp

Chapter1 使用libmodbus库开发modbusTcp从站(支持多个主站连接)

参考链接:https://blog.csdn.net/v6543210/article/details/127426450

https://blog.csdn.net/qq_38158479/article/details/120928043

当我们需要自己搞一个C/C++版的 modbus Server时,总想像C#里面借助个好用的库来实现,但是libmodbus这个库封装的并不好用,从官方的源码中连个example都没有,能参考的也就tests目录下有几个可以借鉴。

但是仔细看了一下,random-test-server.c 还是会阻塞的,单线程。与拿来即用的标准相差甚远。

如果需要实现对多个客户端提供服务,需要参考 bandwidth-server-many-up.c

本文借鉴这篇文章,进行了一点优化,实现了可以为多个客户端提供服务的modbus tcp Server,可以拿来即用。

使用libmodbus库开发modbusTcp从站(支持多个主站连接)_酸菜。的博客-CSDN博客_libmodbus tcp

如果需要自己实现逻辑可以直接在另一个线程函数中对modbus的变量进行修改。

rdsmodbusslave.h

#ifndef RDSMODBUSSLAVE_H
#define RDSMODBUSSLAVE_H#include <iostream>
#include <thread>
#include <stdlib.h>
#include <iostream>
#include <mutex>
#include <string>
using namespace std;
/*如果是windows平台则要加载相应的静态库和头文件*/
#ifdef _WIN32
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <winsock2.h>
#include <windows.h>
//#include <modbus.h>
#pragma comment(lib, "Ws2_32.lib")
//#pragma comment(lib, "modbus.lib")
/*linux平台*/
#else
//#include <modbus/modbus.h>
#include <unistd.h>
#include <error.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/select.h>
#endif//#define  MAX_POINT  50000
#include <QObject>
#include <QThread>
#include <stdio.h>
#include <libmodbus/config.h>
#include <libmodbus/modbus.h>
#include <libmodbus/modbus-rtu.h>
#include <QTimer>
#include <QDebug>
#include <QStringList>
#include <QSerialPortInfo>
#include <QSerialPort>class RDSModbusSlave : public QObject
{Q_OBJECT
public:explicit RDSModbusSlave(QObject *parent = nullptr);RDSModbusSlave(string host="0.0.0.0", uint16_t port=502);~RDSModbusSlave();public:void recieveMessages();bool modbus_set_slave_id(int id);bool initModbus(std::string Host_Ip, int port, bool debugging);uint8_t getTab_Input_Bits(int NumBit);bool setTab_Input_Bits(int NumBit, uint8_t Value);uint16_t getHoldingRegisterValue(int registerStartaddress);float getHoldingRegisterFloatValue(int registerStartaddress);bool setHoldingRegisterValue(int registerStartaddress, uint16_t Value);bool setHoldingRegisterValue(int registerStartaddress, float Value);bool setInputRegisterValue(int registerStartaddress, uint16_t Value);bool setInputRegisterValue(int registerStartaddress, float Value);private:std::mutex slavemutex;int m_errCount{ 0 };int m_modbusSocket{ -1 };bool m_initialized{ false };modbus_t* ctx{ nullptr };modbus_mapping_t* mapping{ nullptr };/*Mapping*/int m_numBits{ 60000 };int m_numInputBits{ 60000 };int m_numRegisters{ 60000 };int m_numInputRegisters{ 60000 };public:void loadFromConfigFile();void run();signals:};/*annotation:
(1)https://www.jianshu.com/p/0ed380fa39eb
(2)typedef struct _modbus_mapping_t
{int nb_bits;                //线圈int start_bits;int nb_input_bits;          //离散输入int start_input_bits;int nb_input_registers;     //输入寄存器int start_input_registers;int nb_registers;           //保持寄存器int start_registers;uint8_t *tab_bits;uint8_t *tab_input_bits;uint16_t *tab_input_registers;uint16_t *tab_registers;
}modbus_mapping_t;*/#endif // RDSMODBUSSLAVE_H

rdsmodbusslave.cpp

#include "rdsmodbusslave.h"#ifdef _WIN32
typedef int socklen_t;
#endifRDSModbusSlave::RDSModbusSlave(QObject *parent) : QObject(parent)
{}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      Constructor* @version    v1* @return     null* @date       2021/10/6**************************************************************/
RDSModbusSlave::RDSModbusSlave(string host, uint16_t port)
{initModbus(host, port, false);//TODO:this->setHoldingRegisterValue(0, (uint16_t)0x1122);this->setHoldingRegisterValue(3, (uint16_t)0x3022);this->setHoldingRegisterValue(6, (uint16_t)0x6022);
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      Destructor* @version    v1* @return     null* @date       2021/10/6**************************************************************/
RDSModbusSlave::~RDSModbusSlave()
{modbus_mapping_free(mapping);modbus_close(ctx);modbus_free(ctx);
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      支持多个master同时连接* @version    v1* @return     null* @date       2021/10/6**************************************************************/
void RDSModbusSlave::recieveMessages()
{uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];int master_socket;int rc;fd_set refset;fd_set rdset;/* Maximum file descriptor number */int fdmax;/* Clear the reference set of socket */FD_ZERO(&refset);/* Add the server socket */FD_SET(m_modbusSocket, &refset);/* Keep track of the max file descriptor */fdmax = m_modbusSocket;while( true ){rdset = refset;if (select(fdmax+1, &rdset, NULL, NULL, NULL) == -1){perror("Server select() failure.");break;}/* Run through the existing connections looking for data to be* read */for (master_socket = 0; master_socket <= fdmax; master_socket++){if (!FD_ISSET(master_socket, &rdset)){continue;}if (master_socket == m_modbusSocket){/* A client is asking a new connection */socklen_t addrlen;struct sockaddr_in clientaddr;int newfd;/* Handle new connections */addrlen = sizeof(clientaddr);memset(&clientaddr, 0, sizeof(clientaddr));newfd = accept(m_modbusSocket, (struct sockaddr *)&clientaddr, &addrlen);if (newfd == -1){perror("Server accept() error");} else{FD_SET(newfd, &refset);if (newfd > fdmax){/* Keep track of the maximum */fdmax = newfd;}printf("New connection from %s:%d on socket %d\n", inet_ntoa(clientaddr.sin_addr), clientaddr.sin_port, newfd);}} else{modbus_set_socket(ctx, master_socket);rc = modbus_receive(ctx, query);if (rc > 0){modbus_reply(ctx, query, rc, mapping);} else if (rc == -1){/* This example server in ended on connection closing or* any errors. */printf("Connection closed on socket %d\n", master_socket);#ifdef _WIN32closesocket(master_socket);#elseclose(master_socket);#endif/* Remove from reference set */FD_CLR(master_socket, &refset);if (master_socket == fdmax){fdmax--;}}}}}m_initialized = false;
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      modbus_set_slave_id* @param      id* @version    v1* @return     null* @date       2021/10/19**************************************************************/
bool RDSModbusSlave::modbus_set_slave_id(int id)
{int rc = modbus_set_slave(ctx, id);if (rc == -1){fprintf(stderr, "Invalid slave id\n");modbus_free(ctx);return false;}return true;
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      modbus initialization* @param      IP/PORT/debugflag* @version    v1* @return     null* @date       2021/10/6**************************************************************/
bool RDSModbusSlave::initModbus(std::string Host_Ip = "127.0.0.1", int port = 502, bool debugging = false)
{ctx = modbus_new_tcp(Host_Ip.c_str(), port);modbus_set_debug(ctx, debugging);if (ctx == NULL){fprintf(stderr, "There was an error allocating the modbus\n");throw - 1;}m_modbusSocket = modbus_tcp_listen(ctx, 1);/*设置线圈, 离散输入, 输入寄存器, 保持寄存器个数(数组元素个数))*/mapping = modbus_mapping_new(m_numBits, m_numInputBits, m_numInputRegisters, m_numRegisters);if (mapping == NULL){fprintf(stderr, "Unable to assign mapping:%s\n", modbus_strerror(errno));modbus_free(ctx);m_initialized = false;return false;}m_initialized = true;return true;
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      getTab_Input_Bits(获取输入寄存器某一位的值)* @param      NumBit(输入寄存器相应的bit位)* @version    v1* @return     null* @date       2021/10/8**************************************************************/
uint8_t RDSModbusSlave::getTab_Input_Bits(int NumBit)
{if (!m_initialized){return -1;}return mapping->tab_input_bits[NumBit];
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      setTab_Input_Bits(设置输入寄存器某一位的值)* @param      NumBit(输入寄存器的起始地址)* @param      Value(输入寄存器的值)* @version    v1* @return     null* @date       2021/10/8**************************************************************/
bool RDSModbusSlave::setTab_Input_Bits(int NumBit, uint8_t Value)
{if (NumBit > (m_numInputBits - 1)){return false;}slavemutex.lock();mapping->tab_input_bits[NumBit] = Value;slavemutex.unlock();return true;
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      getRegisterValue(获取保存寄存器的值)* @param      registerStartaddress(保存寄存器的起始地址)* @version    v1* @return     null* @date       2021/10/6**************************************************************/
uint16_t RDSModbusSlave::getHoldingRegisterValue(int registerStartaddress)
{if (!m_initialized){return -1;}return mapping->tab_registers[registerStartaddress];
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      获取寄存器里的浮点数* @param      registerStartaddress寄存器起始地址* @version    v1* @return     两个uint16_t拼接而成的浮点值* @date       2021/10/6**************************************************************/
float RDSModbusSlave::getHoldingRegisterFloatValue(int registerStartaddress)
{if (!m_initialized){return -1.0f;}return modbus_get_float_badc(&mapping->tab_registers[registerStartaddress]);
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      setRegisterValue(设置保存寄存器的值,类型为uint16_t)* @param      registerStartaddress(保存寄存器的起始地址)* @param      Value(写入到保存寄存器里的值)* @version    v1* @return     null* @date       2021/10/6**************************************************************/
bool RDSModbusSlave::setHoldingRegisterValue(int registerStartaddress, uint16_t Value)
{if (registerStartaddress > (m_numRegisters - 1)){return false;}slavemutex.lock();mapping->tab_registers[registerStartaddress] = Value;slavemutex.unlock();return true;
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      setRegisterFloatValue(设置浮点值)* @param      (Value:浮点值,registerStartaddress寄存器起始地址)* @version    v1* @return     null* @date       2021/10/8**************************************************************/
bool RDSModbusSlave::setHoldingRegisterValue(int registerStartaddress, float Value)
{if (registerStartaddress > (m_numRegisters - 2)){return false;}/*小端模式*/slavemutex.lock();modbus_set_float(Value, &mapping->tab_registers[registerStartaddress]);slavemutex.unlock();return true;
}bool RDSModbusSlave::setInputRegisterValue(int registerStartaddress, uint16_t Value)
{if (registerStartaddress > (m_numRegisters - 1)){return false;}slavemutex.lock();mapping->tab_input_registers[registerStartaddress] = Value;slavemutex.unlock();return true;
}bool RDSModbusSlave::setInputRegisterValue(int registerStartaddress, float Value)
{if (registerStartaddress > (m_numRegisters - 2)){return false;}/*小端模式*/slavemutex.lock();modbus_set_float(Value, &mapping->tab_input_registers[registerStartaddress]);slavemutex.unlock();return true;
}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      loadFromConfigFile* @version    v1* @return     null* @date       2021/10/18**************************************************************/
void RDSModbusSlave::loadFromConfigFile()
{}/**************************************************************** @file       RDSModbusSlave.cpp* @author     seer-txj* @brief      run* @version    v1* @return     null* @date       2021/10/18**************************************************************/
void RDSModbusSlave::run()
{std::thread loop([this](){while (true){if (m_initialized){recieveMessages();}else{m_initialized = true;}}});loop.detach();return;
}

main.cpp

#include "mainwindow.h"#include <QApplication>//#include "rdsmodbusslave.h"using namespace std;void modbusRunner(RDSModbusSlave* server)
{server->recieveMessages();
}RDSModbusSlave modServer("127.0.0.1", 502);int main(int argc, char *argv[])
{QApplication a(argc, argv);MainWindow w;w.show();std::thread modServerThread(modbusRunner, &modServer);modServerThread.join();return a.exec();
}

相关文章:

使用libmodbus库开发modbusTcp从站(支持多个主站连接)

使用libmodbus库开发modbusTcp从站&#xff08;支持多个主站连接&#xff09; Chapter1 使用libmodbus库开发modbusTcp从站(支持多个主站连接)rdsmodbusslave.hrdsmodbusslave.cppmain.cpp Chapter1 使用libmodbus库开发modbusTcp从站(支持多个主站连接) 参考链接&#xff1a…...

GPT系列论文解读:GPT-2

GPT系列 GPT&#xff08;Generative Pre-trained Transformer&#xff09;是一系列基于Transformer架构的预训练语言模型&#xff0c;由OpenAI开发。以下是GPT系列的主要模型&#xff1a; GPT&#xff1a;GPT-1是于2018年发布的第一个版本&#xff0c;它使用了12个Transformer…...

(四)激光线扫描-光平面标定

在上一章节,已经实现了对激光线条的中心线提取,并且在最开始已经实现了对相机的标定,那么相机标定的作用是什么呢? 就是将图像二维点和空间三维点之间进行互相转换。 1. 什么是光平面 激光发射器投射出一条线,形成的一个扇形区域平面就是光平面,也叫光刀面,与物体相交…...

妙不可言的Python之旅----(二)

Python基础语法 什么是字面量 字面量&#xff1a;在代码中&#xff0c;被写下来的的固定的值&#xff0c;称之为字面量 常用的值类型 类型 描述 说明 数字&#xff08;Number&#xff09; 支持 • 整数&#xff08;int&#xff09; • 浮点数&#xff08;float&#xff…...

cartographer(1)-运行

1.下载数据集 #1.下载数据集&#xff1a; mkdir /home/tang/bagfiles#2.开始二维建图 cd /home/tang/carto_ws/cartographer_detailed_comments_ws/install_isolated/source install_isolated/setup.bash rospack profile #新装的包索引地址存在ros的环境里 roslaunch ca…...

C++:模板进阶与继承

模板进阶与继承 模板进阶1.非类型的模板参数2.模板的特化2.1特化的概念2.2函数模板特化2.3类模板特化2.4全特化和偏特化2.4.1全特化2.4.2偏特化 3.模板的分离编译3.1同文件分离3.2不同文件下分离 继承1.继承的概念和定义1.1继承的概念1.2继承的定义1.2.1定义格式1.2.2继承关系和…...

vue-img-cutter 实现图片裁剪[vue 组件库]

借助 vue-img-cutter 可以在网页端实现图片裁剪功能&#xff0c;最终功能效果如下&#xff1a; 组件 npm 安装 npm install vue-img-cutter2 --save-dev # for vue2 npm install vue-img-cutter3 --save-dev # for vue3vue-img-cutter使用 template模板标签模块&#xff0c…...

手把手教你从零开始腾讯云服务器部署(连接建站教程)

使用腾讯云服务器搭建网站全流程&#xff0c;包括轻量应用服务器和云服务器CVM建站教程&#xff0c;轻量可以使用应用镜像一键建站&#xff0c;云服务器CVM可以通过安装宝塔面板的方式来搭建网站&#xff0c;腾讯云服务器网txyfwq.com分享使用腾讯云服务器建站教程&#xff0c;…...

微信开放平台第三方开发,实现代小程序备案申请

大家好&#xff0c;我是小悟 微信小程序备案整体流程总共分为五个环节&#xff1a;备案信息填写、平台初审、工信部短信核验、通管局审核和备案成功。 服务商可以代小程序发起备案申请。在申请小程序备案之前&#xff0c;需要确保小程序基本信息已填写完成、小程序至少存在一个…...

设计模式——11. 享元模式

1. 说明 享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在减少系统中相似对象的内存占用或计算开销,通过共享相同的对象来达到节省资源的目的。 享元模式的核心思想是将对象的状态分为内部状态(Intrinsic State)和外部状态(Extrinsic State): 内部状态是对象…...

【LLM】主流大模型体验(文心一言 科大讯飞 字节豆包 百川 阿里通义千问 商汤商量)

note 智谱AI体验百度文心一言体验科大讯飞大模型体验字节豆包百川智能大模型阿里通义千问商汤商量简要分析&#xff1a;仅从测试“老婆饼为啥没有老婆”这个问题的结果来看&#xff0c;chatglm分点作答有条理&#xff08;但第三点略有逻辑问题&#xff09;&#xff1b;字节豆包…...

CSS小计

1&#xff1a;设置图片随窗缩放 使用百分比 width: 100%;height: 100%; 使用vmin: 将可视区域分为100vmin width: 100vmin;height: 100vmin; 2:设置字体颜色与背景色融合 mix-blend-mode: difference 3: 设置宽度自适应 width:fit-content 4:外边距合并 当两个相领的两个容…...

机器学习:决策树

决策树 决策树是一种基于树形结构的模型&#xff0c;决策树从根节点开始&#xff0c;一步步走到叶子节点&#xff08;决策&#xff09;&#xff0c;所有的数据最终都会落到叶子节点&#xff0c;既可以做分类也可以做回归。 特征选择 根节点的选择该用哪一个特征呢&#xff…...

xxl-job的原理(2)—调度中心管理注册信息

一、调度中心管理注册信息 1.JobApiController 执行器调用调度中心的url来实现注册、下线、回调等操作&#xff1b;其主要的实现类是JobApiController&#xff0c;调用/api/registry接口注册执行器信息&#xff0c;调用/api/registryRemove接口下线执行器信息&#xff0c;调用…...

小白入门pytorch(二)----神经网络

本文为&#x1f517;[小白入门Pytorch]学习记录博客 文章目录 前言一、神经网络的组成部分1.神经元2.神经网络层3.损失函数4.优化器 二、Pytorch构建神经网络中的网络层全连接层2.卷积层3.池化层4.循环神经网络5.转置卷积层6.归一化层7.激活函数层 三、数据加载与预处理1.数据加…...

【进阶C语言】排序函数(qsort)与模拟实现(回调函数的实例)

本章大致内容目录&#xff1a; 1.认识回调函数 2.排序函数qsort 3.模拟实现qsort 回调函数为C语言重要知识点&#xff0c;以函数指针为主要知识&#xff1b;下面介绍回调函数的定义、回调函数的库函数举例即库函数模拟实现。 一、回调函数 1.回调函数定义 回调函数就是一…...

CentOS 7 上编译和安装 SQLite 3.9.0

文章目录 可能报错分析详细安装过程 可能报错分析 报错如下&#xff1a; django.core.exceptions.ImproperlyConfigured: SQLite 3.9.0 or later is required (found 3.7.17). 原因&#xff1a;版本为3.7.太低了&#xff0c;需要升级到3.9.0至少 详细安装过程 1.安装所需的…...

[GXYCTF2019]禁止套娃 无回显 RCE 过滤__FILE__ dirname等

扫除git 通过githack 获取index.php <?php include "flag.php"; echo "flag在哪里呢&#xff1f;<br>"; if(isset($_GET[exp])){if (!preg_match(/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i, $_GET[exp])) {if(; preg_replace(/[a-z,_]\(…...

Springboot使用Aop保存接口请求日志到mysql

1、添加aop依赖 <!-- aop日志 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency> 2、新建接口保存数据库的实体类RequestLog.java package com.example…...

网络安全面试题汇总(附答案)

作为从业多年的网络安全工程师&#xff0c;我深知在面试过程中面试官所关注的重点及考察的技能点。网络安全作为当前信息技术领域中非常重要的一部分&#xff0c;对于每一个从事网络安全工作的人员来说&#xff0c;不仅需要掌握一定的技术能力&#xff0c;更需要具备全面的综合…...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

动态 Web 开发技术入门篇

一、HTTP 协议核心 1.1 HTTP 基础 协议全称 &#xff1a;HyperText Transfer Protocol&#xff08;超文本传输协议&#xff09; 默认端口 &#xff1a;HTTP 使用 80 端口&#xff0c;HTTPS 使用 443 端口。 请求方法 &#xff1a; GET &#xff1a;用于获取资源&#xff0c;…...

C# 表达式和运算符(求值顺序)

求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如&#xff0c;已知表达式3*52&#xff0c;依照子表达式的求值顺序&#xff0c;有两种可能的结果&#xff0c;如图9-3所示。 如果乘法先执行&#xff0c;结果是17。如果5…...