基于 Modbus 的工业数据采集、控制(part 1)

在这里插入图片描述

HTTP 协议

简介

HTTP 是 Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于 Web Browser(浏览器)到 Web Server(服务器)进行数据交互的传输协议。HTTP 是一个基于 TCP 通信协议传输来传递数据(HTML 文件, 图片文件, 查询结果等)的应用层协议
HTTP 协议工作于 B/S 架构 上,浏览器作为 HTTP 客户端通过 URL 主动向 HTTP 服务端即 WEB 服务器发送所有请求,Web 服务器根据接收到的请求后,向客户端发送响应信息。
HTTP 默认端口号为 80,但是也可以改为其他端口。

特点

HTTP 是 短连接 的:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
HTTP 是 媒体独立 的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的 MIME-type 内容类型。
HTTP 是 无状态 的:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

协议格式

客户端请求消息格式

客户端发送一个 HTTP 请求到服务器,请求消息由:
请求行、请求头部、空行请求数据 四个部分组成。
在这里插入图片描述

请求行

由 请求方法字段、url 字段、HTTP 协议版本字段 3个部分组成。请求行定义了本次请求的方式,格式如下:GET /example.html HTTP/1.1(CRLF)。

请求方式

HTTP 协议中共定义了八种数据的请求方法。分别是:OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE、CONNECT;在实际应用中常用的是 get 和 post,其他请求方式也都可以通过这两种方式间接实现。(增 POST 删 DELETE 改 PUT 查 GET)

GET 方法 和 POST 方法 的区别:

GET 通常用来从服务器上获得数据,而非修改信息;POST 用来向服务器传递数据。
1、请求的数据带参数时,GET 请求的数据会附加在 URL 之后,以“?”分割 URL 和 传输数据,多个参数用 & 连接。POST 请求会把请求的数据放置在 HTTP 请求包的包体中。
因此,GET 请求的数据会暴露在地址栏中,而 POST 请求则不会。
2、 传输数据的大小:在 HTTP 规范中,没有对 URL 的长度和传输数据的大小进行限制。但是对于GET,在实际开发过程中,特定的浏览器和服务器对 URL 的长度有限制。因此,在使用 GET 请求时,传输数据会受到 URL 长度的限制。对于 POST,由于不是 URL 传值,理论上是不会受限制的,但是实际上,各个服务器会规定对 POST 提交数据大小进行限制,Apache、IIS 都有各自的配置。
3、GET 请求返回的内容可以被浏览器缓存起来。而每次提交的 POST,在按下 F5 的时候,会跳出确认框,浏览器不会缓存 POST 请求返回的内容。
4、GET 对数据进行查询,POST 主要对数据进行增删改!简单说,GET 是只读,POST 是写。
5、对于参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制。

请求头

也被称作消息报头,请求头是由一些键值对组成,每行一对,关键字和值用英文冒号“:”分隔。允许客户端向服务器发送一些附加信息或者客户端自身的信息,典型的请求头如下:
在这里插入图片描述

空行

最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头。

请求体

请求数据:请求数据不在 GET 方法中使用,而是在 POST 方法中使用。POST 方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是 Content-Type 和 Content-Length。

服务器响应消息格式

HTTP 响应也由四个部分组成,分别是:状态行、消息报头、空行响应正文
状态行:由三部分组成,HTTP 协议的版本号、状态码、以及对状态码的文本描述。
例如:HTTP/1.1 200 OK(CRLF)。(200表示请求已经成功)
在这里插入图片描述

WebServer

入门

// 循环服务器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>

#define PORT 8080
#define BUFFER_SIZE 1024

void handle_request(int client_socket) {
    char buffer[BUFFER_SIZE];
    char response[]= "HTTP/1.1 200 OK\nContent-Type: text/html\n\n<html><body><h1>Hello, World!</h1></body></html>";

    // 从客户端读取请求
    ssize_t bytes_read = read(client_socket, buffer, BUFFER_SIZE - 1);
    if (bytes_read == -1) {
        perror("读取请求失败");
        return;
    }
    buffer[bytes_read] = '\0';

    // 打印请求内容
    printf("收到请求:\n%s\n", buffer);

    // 发送响应给客户端
    ssize_t bytes_written = write(client_socket, response, strlen(response));
    if (bytes_written == -1) {
        perror("发送响应失败");
    }
}

int main() {
    int server_socket, client_socket;
    struct sockaddr_in server_address, client_address;
    socklen_t client_address_len;

    // 创建套接字
    if ((server_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("创建套接字失败");
        exit(1);
    }

    // 设置地址重用
    int reuse = 1;
    if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1) {
        perror("设置地址重用失败");
        exit(1);
    }

    // 绑定地址
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(PORT);
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(server_socket, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) {
        perror("绑定地址失败");
        exit(1);
    }

    // 启动监听
    if (listen(server_socket, 10) == -1) {
        perror("启动监听失败");
        exit(1);
    }

    printf("服务器已启动,监听端口 %d\n", PORT);

    // 接受连接并处理请求
    while (1) {
        client_address_len = sizeof(client_address);
        if ((client_socket = accept(server_socket, (struct sockaddr *)&client_address, &client_address_len)) == -1){
            perror("接受连接失败");
            continue;
        }

        printf("接受新连接\n");

        // 处理请求
        handle_request(client_socket);
        
        // 关闭客户端套接字
        close(client_socket);
        
        printf("连接已关闭\n");
    }

    // 关闭服务器套接字
    close(server_socket);

    return 0;
}

效果如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

postman 模拟测试 HTML

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

然后一口吃成胖子

在这里插入图片描述
在这里插入图片描述

服务器源码分析:
1)服务器初始化;
2)循环等待连接,连接后创建线程,调用 msg_request 函数,并在该函数中调用 handler_msg 函数分析数据;
3)handler_msg 中,首先查看协议,其次读请求方法、url 、参数,判断方法是什么,并对need_handler 赋值,确定请求资源路径。如果请求地址没有携带任何资源,那么默认返回 index.html;如果资源不存在,返回 404;如果需要处理,调用 handle_request 函数;如果不需要处理,调用 echo_www 函数,直接返回资源;
4)handle_request 函数主要用来获取 post 请求数据,调用 parse_and_process 函数处理正文内容(自行添加所需功能)
结合 post.html 网页部分的内容,做测试。

注意

1、网页发送正文内容需要加“”,因为网页发送的数据是字符串。
2、共享内存和消息队列数据收发问题的解决方案:
1) 在代码中加打印语句,确保两个进程用的是同一个id;
2) 由于程序是强制结束,再下次运行代码时,将消息队列删除一下;

查看和删除共享内存和消息队列:
ipcs -m :查看共享内存
ipcrm -m shmid:删除共享内存
ipcs -q:查看消息队列
ipcrm -q semid:删除消息队列

3、key值的创建路径指定/目录下的某个新建文件。
4、多使用打印语句,排查错误位置。

服务器端

// custom_handle.c

parse_and_process(char * input)	{				// input :post请求发送的正文

    ...
    // 请求modbus数据
    else if(strstr(input, "modbus_get"))
    {
        return handle_get(sock, input);
    }
    // 控制modbus设备
    else if(strstr(input, "modbus_set"))
    {
        return handle_set(sock, input);
    }
	...
}

static int handle_get(int sock, const char *input) {
    // 1.创建key
    // 2.创建或打开共享内存
    // 3.映射
    // 4.读取共享内存中的数据
    // 5.发送
}

static int handle_set(int sock, const char *input){
    // 1.创建key
    // 2.创建或打开消息队列
    // 3.将postman中下发指令添加到消息队列
    // 4.发送
}

采集控制程序

// client.c


// 采集传感器数据线程
void *info(void *arg) {
    // 创建或打开共享内存、映射
    while(1) {
        // 读寄存器的数据
        // 写入共享内存
        sleep(1);
    }
}

// 控制设备线程
void *control(void *arg) {
    //创建或打开消息队列
    while(1) {
        // 读消息队列
        // 控制设备
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Modbus协议是一种通信协议,用于在工业控制系统(ICS)中传输数据。它是一种串行通信协议,允许设备通过Modbus协议进行通信。Modbus协议通常用于数据采集和监控,因为它可以在不同的设备之间传输数据,例如传感器、计量器和控制器。 要基于Modbus协议进行数据采集,需要使用Modbus通信库或驱动程序。这些库或驱动程序可以与Modbus设备通信,并将数据读取到计算机中进行处理和存储。可以使用各种编程语言(如C ++、Python和Java)来编写Modbus通信程序。 具体而言,Modbus协议的数据采集可以通过以下步骤实现: 1. 确定Modbus设备的类型和地址。Modbus设备通常具有唯一的地址,可以使用该地址与设备通信。 2. 使用Modbus通信库或驱动程序与设备进行通信。这通常需要指定通信端口和波特率等通信参数。 3. 发送Modbus命令以读取数据。Modbus协议提供了不同的命令,可以使用这些命令读取设备上的不同类型的数据。例如,可以使用读取保持寄存器的命令来读取设备上的寄存器值。 4. 处理读取的数据。读取的数据可以是二进制数据或ASCII数据,具体取决于Modbus设备的配置。数据采集程序需要将读取的数据解析为可读的格式,并进行处理和存储。 5. 可以将处理后的数据展示在界面上,或者将其发送到云端进行进一步处理和分析。 需要注意的是,Modbus协议的数据采集需要对设备的配置和通信进行了解,并具备一定的编程技能。在进行数据采集时,还需要注意设备的安全性和稳定性,以保证数据采集的准确性和可靠性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值