epoll多路485,超时重新发送的例子
时间: 2024-05-13 17:17:33 浏览: 168
IO多路复用之select实例
以下是一个简单的使用epoll实现多路485通讯,并在超时时自动重新发送的例子:
```c++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/epoll.h>
#include <termios.h>
#define MAX_EVENTS 10 // epoll监听的最大事件数
#define BUF_SIZE 256 // 读写缓冲区大小
int main(int argc, char *argv[])
{
int fd[MAX_EVENTS], i, n, ret, timeout; // fd数组用于存放文件描述符,i和n用于循环,ret用于保存读写返回值,timeout用于超时设置
char buf[MAX_EVENTS][BUF_SIZE]; // 缓冲区数组,每个串口对应一个缓冲区
struct termios oldtio[MAX_EVENTS], newtio[MAX_EVENTS]; // 用于保存串口设置
struct epoll_event ev, events[MAX_EVENTS]; // ev用于注册文件描述符,events用于存放激活的事件
int epfd; // epoll描述符
struct timeval tv; // 超时时间结构体
if (argc < 2) {
printf("Usage: %s /dev/ttyS0 [/dev/ttyS1 ...]\n", argv[0]);
exit(1);
}
// 打开串口设备并进行初始化
for (i = 1; i < argc; ++i) {
fd[i - 1] = open(argv[i], O_RDWR | O_NOCTTY);
if (fd[i - 1] < 0) {
perror("open");
exit(1);
}
// 保存原始串口设置
tcgetattr(fd[i - 1], &oldtio[i - 1]);
// 初始化新的串口设置
memset(&newtio[i - 1], 0, sizeof(newtio[i - 1]));
newtio[i - 1].c_cflag = B9600 | CS8 | CLOCAL | CREAD;
newtio[i - 1].c_iflag = IGNPAR;
newtio[i - 1].c_oflag = 0;
newtio[i - 1].c_lflag = 0;
newtio[i - 1].c_cc[VTIME] = 0;
newtio[i - 1].c_cc[VMIN] = 1;
// 应用新的串口设置
tcflush(fd[i - 1], TCIFLUSH);
tcsetattr(fd[i - 1], TCSANOW, &newtio[i - 1]);
}
// 创建epoll描述符
epfd = epoll_create(MAX_EVENTS);
if (epfd < 0) {
perror("epoll_create");
exit(1);
}
// 注册文件描述符
for (i = 0; i < argc - 1; ++i) {
ev.events = EPOLLIN | EPOLLET; // 监听读事件和边缘触发模式
ev.data.fd = fd[i];
if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd[i], &ev) < 0) {
perror("epoll_ctl");
exit(1);
}
}
while (1) {
// 设置超时时间为1秒
tv.tv_sec = 1;
tv.tv_usec = 0;
// 等待事件激活
n = epoll_wait(epfd, events, MAX_EVENTS, 1000);
if (n < 0) {
perror("epoll_wait");
break;
}
// 处理激活的事件
for (i = 0; i < n; ++i) {
if (events[i].events & EPOLLIN) { // 读事件
ret = read(events[i].data.fd, buf[i], BUF_SIZE);
if (ret < 0) {
perror("read");
exit(1);
} else if (ret == 0) {
printf("Serial port %d closed\n", events[i].data.fd);
exit(0);
} else {
printf("Received %d bytes from serial port %d: %s\n", ret, events[i].data.fd, buf[i]);
}
}
if (events[i].events & EPOLLERR) { // 错误事件
perror("epoll_wait");
exit(1);
}
if (events[i].events & EPOLLOUT) { // 写事件
ret = write(events[i].data.fd, buf[i], strlen(buf[i]));
if (ret < 0) {
perror("write");
exit(1);
} else {
printf("Sent %d bytes to serial port %d: %s\n", ret, events[i].data.fd, buf[i]);
}
}
}
// 超时重新发送
for (i = 0; i < argc - 1; ++i) {
if (strlen(buf[i]) > 0) {
timeout = 0;
while (1) {
tv.tv_sec = 1;
tv.tv_usec = 0;
ret = select(fd[i] + 1, NULL, &ev, NULL, &tv);
if (ret < 0) {
perror("select");
exit(1);
} else if (ret == 0) {
printf("Timeout on serial port %d, resend\n", fd[i]);
ret = write(fd[i], buf[i], strlen(buf[i]));
if (ret < 0) {
perror("write");
exit(1);
}
++timeout;
if (timeout >= 3) {
printf("Resend failed on serial port %d, exit\n", fd[i]);
exit(1);
}
} else {
break;
}
}
}
}
}
// 恢复原始串口设置并关闭设备
for (i = 0; i < argc - 1; ++i) {
tcsetattr(fd[i], TCSANOW, &oldtio[i]);
close(fd[i]);
}
return 0;
}
```
该例子使用epoll监听多个串口设备的读事件和错误事件,并在超时时重新发送。每个串口对应一个缓冲区,可以分别存放收到的数据和待发送的数据。在每次epoll_wait返回后,先处理激活的事件,然后对于每个串口,判断是否有待发送的数据,如果有则进行超时重发的处理。超时时间设置为1秒,如果重发3次仍然失败,则退出程序。
阅读全文