一、项目框架及流程
二、http简介
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于Web Browser(浏览器)到Web Server(服务器)进行数据交互的传输协议。
HTTP是应用层协议
HTTP是一个基于TCP通信协议传输来传递数据(HTML 文件, 图片文件, 查询结果等)
HTTP协议工作于B/S架构上,浏览器作为HTTP客户端通过URL主动向HTTP服务端即WEB服务器发送所有请求,Web服务器根据接收到的请求后,向客户端发送响应信息。
HTTP默认端口号为80,但是你也可以改为8080或者其他端口
三、http特点
HTTP是短连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
四、代码实现
采用多线程模式,一个线程用来循环读取传感器数据,一个线程用来发送控制命令。
//数据采集代码
#include <modbus.h>
#include <modbus-tcp.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/msg.h>
#include <errno.h>modbus_t *ctx;
struct msgbuf
{
long type; // 必须有,在第一个,表示消息的类型,值>0!
int num1; // 消息正文,自己定义
int num2;
};void *handler(void *arg);
int main(int argc, char const *argv[])
{
int shmid;
key_t key;
uint16_t *p;
key = ftok("00.c", 'a');
if (key < 0)
{
perror("key err");
return -1;
}
printf("key: %#x\n", key);// 打开或创建共享内存
shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0777); // 如果共享内存不存在则创建,存在则返回-1
if (shmid <= 0)
{
if (errno == EEXIST) // 如果共享内存已存在则,直接打开
shmid = shmget(key, 128, 0777); // 直接打开已有的共享内存并且获得共享内存id
else
{
perror("shmget err");
return -1;
}
}
printf("shmid: %d\n", shmid);// 映射共享内存
p = (uint16_t *)shmat(shmid, NULL, 0);
if (p == (uint16_t *)-1)
{
perror("shmat err");
return -1;
}// 1.创建实例
ctx = modbus_new_tcp(argv[1], 502);
// 3.设置从机id(主从线程)
modbus_set_slave(ctx, 0x01);
// 4.创建连接(主从线程)
modbus_connect(ctx);
// 2.创建新线程
pthread_t tid;
pthread_create(&tid, NULL, handler, NULL);
// 5.主线程:04功能码读输入寄存器循环
uint16_t dest[32] = {0};
while (1)
{
modbus_read_input_registers(ctx, 0x00, 2, p);
printf("temp:%02x\nhumi:%02x\n", p[0], p[1]);
sleep(1);
}
pthread_join(tid, NULL);
modbus_free(ctx);
modbus_close(ctx);
// 取消映射
shmdt(p);// 删除共享内存
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
// 6.从线程:05功能码写单个线圈
void *handler(void *arg)
{
int a, b;
while (1)
{
key_t key;
int msgid;if ((key = ftok("00.c", 'a')) < 0)
{
perror("ftok err");
}
printf("key: %#x\n", key);// 打开或创建消息队列
msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0777);
if (msgid <= 0)
{
if (errno == EEXIST)
msgid = msgget(key, 0777); // 如果已经存在消息队列那直接打开该消息队列
else
{
perror("msgget err");
}
}
printf("msgid: %d\n", msgid);
struct msgbuf m;
msgrcv(msgid, &m, sizeof(m) - sizeof(long), 20, 0); // 0:阻塞,读完消息再返回
modbus_write_bit(ctx, m.num1, m.num2);
}
pthread_exit(NULL);
}
//服务代码
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <modbus.h>
#include <modbus-tcp.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/msg.h>
#include <errno.h>
#include "custom_handle.h"#define KB 1024
#define HTML_SIZE (64 * KB)
struct msgbuf
{
long type; // 必须有,在第一个,表示消息的类型,值>0!
int num1; // 消息正文,自己定义
int num2;
};// 普通的文本回复需要增加html头部
#define HTML_HEAD "Content-Type: text/html\r\n" \
"Connection: close\r\n"static int handle_login(int sock, const char *input)
{
char reply_buf[HTML_SIZE] = {0};
char *uname = strstr(input, "username=");
uname += strlen("username=");
char *p = strstr(input, "password");
*(p - 1) = '\0';
printf("username = %s\n", uname);char *passwd = p + strlen("password=");
printf("passwd = %s\n", passwd);if (strcmp(uname, "admin") == 0 && strcmp(passwd, "admin") == 0)
{
sprintf(reply_buf, "<script>localStorage.setItem('usr_user_name', '%s');</script>", uname);
strcat(reply_buf, "<script>window.location.href = '/index.html';</script>");
send(sock, reply_buf, strlen(reply_buf), 0);
}
else
{
printf("web login failed\n");//"用户名或密码错误"提示,chrome浏览器直接输送utf-8字符流乱码,没有找到太好解决方案,先过渡
char out[128] = {0xd3, 0xc3, 0xbb, 0xa7, 0xc3, 0xfb, 0xbb, 0xf2, 0xc3, 0xdc, 0xc2, 0xeb, 0xb4, 0xed, 0xce, 0xf3};
sprintf(reply_buf, "<script charset='gb2312'>alert('%s');</script>", out);
strcat(reply_buf, "<script>window.location.href = '/login.html';</script>");
send(sock, reply_buf, strlen(reply_buf), 0);
}return 0;
}static int handle_add(int sock, const char
*input)
{
int number1, number2;// input必须是"data1=1data2=6"类似的格式,注意前端过来的字符串会有双引号
sscanf(input, "data1=%ddata2=%d", &number1, &number2);
printf("num1 = %d\n", number1);char reply_buf[HTML_SIZE] = {0};
printf("num = %d\n", number1 + number2);
sprintf(reply_buf, "%d", number1 + number2);
printf("resp = %s\n", reply_buf);
send(sock, reply_buf, strlen(reply_buf), 0);return 0;
}static int handle_collect(int sock, const char *input)
{
int shmid;
key_t key;
uint16_t *p;
key = ftok("00.c", 'a');
if (key < 0)
{
perror("key err");
return -1;
}
printf("key: %#x\n", key);// 打开或创建共享内存
shmid = shmget(key, 128, IPC_CREAT | IPC_EXCL | 0777); // 如果共享内存不存在则创建,存在则返回-1
if (shmid <= 0)
{
if (errno == EEXIST) // 如果共享内存已存在则,直接打开
shmid = shmget(key, 128, 0777); // 直接打开已有的共享内存并且获得共享内存id
else
{
perror("shmget err");
return -1;
}
}
printf("shmid: %d\n", shmid);// 映射共享内存
p = (uint16_t *)shmat(shmid, NULL, 0);
if (p == (uint16_t *)-1)
{
perror("shmat err");
return -1;
}char reply_buf[HTML_SIZE];
printf("temp:%02x\nhumi:%02x\n", p[0], p[1]);
sprintf(reply_buf, "温度:%d 湿度:%d", p[0], p[1]);
printf("%s\n", reply_buf);
send(sock, reply_buf, strlen(reply_buf), 0);
return -1;
}static int handle_set(int sock, const char *input)
{
key_t key;
int msgid;if ((key = ftok("00.c", 'a')) < 0)
{
perror("ftok err");
return -1;
}
printf("key: %#x\n", key);// 打开或创建消息队列
msgid = msgget(key, IPC_CREAT | IPC_EXCL | 0777);
if (msgid <= 0)
{
if (errno == EEXIST)
msgid = msgget(key, 0777); // 如果已经存在消息队列那直接打开该消息队列
else
{
perror("msgget err");
return -1;
}
}
printf("msgid: %d\n", msgid);int number1, number2;
// input必须是"data1=1data2=6"类似的格式,注意前端过来的字符串会有双引号
sscanf(input, "set %d %d", &number1, &number2);
printf("num1 = %d\n", number1);
printf("num2 = %d\n", number2);// 添加消息
struct msgbuf msg;
msg.type = 20;
msg.num1 = number1;
msg.num2 = number2;
msgsnd(msgid, &msg, sizeof(msg) - sizeof(long), 0); // 0:发完消息再返回,而不是立即返回函数
return -1;
}/**
* @brief 处理自定义请求,在这里添加进程通信
* @param input
* @return
*/
int parse_and_process(int sock, const char *query_string, const char *input)
{
// query_string不一定能用的到// 先处理登录操作
if (strstr(input, "username=") && strstr(input, "password="))
{
return handle_login(sock, input);
}
// 处理求和请求
else if (strstr(input, "data1=") && strstr(input, "data2="))
{
return handle_add(sock, input);
}
else if (strstr(input, "get"))
{
return handle_collect(sock, input);
}
else if (strstr(input, "set"))
{
return handle_set(sock, input);
}else // 剩下的都是json请求,这个和协议有关了
{
// 构建要回复的JSON数据
const char *json_response = "{\"message\": \"Hello, client!\"}";// 发送HTTP响应给客户端
send(sock, json_response, strlen(json_response), 0);
}return 0;
}
标签:基于,return,int,采集,WebServer,key,printf,input,include From: https://blog.csdn.net/weixin_63207763/article/details/142465615//.h文件
#ifndef CUSTOM_HANDLE_H
#define CUSTOM_HANDLE_H#include <stdio.h>
#include <string.h>int parse_and_process(int sock, const char *query_string, const char *input);
#endif // CUSTOM_HANDLE_H