C++ Ubuntu select() 如果串行接口有异步读取的数据

C++ Ubuntu select() if serial interface has data on asynchronous read

我正在使用 C++ 和 termios 为 Ubuntu 编写异步串行数据 reader class,但我在检查是否有可用数据时遇到困难。

这是我的代码:

#include <iostream>
#include <string>
#include <sstream>
#include <vector>

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>

class MySerialClass {

    public:
        MySerialClass(std::string port);
        virtual ~MySerialClass();

        void openSerial();
        void closeSerial();
        void configureSerial();
        void writeSerial(std::vector<char> data);
        void readSerial(std::vector<char> &data, unsigned int numBytes);

    private:

        int fd = 0; // The serial file descriptor
        fd_set fdset; // The set to check on select
        std::string portName = "";
};


MySerialClass::MySerialClass(std::string port) : portName(port) {}
MySerialClass::~MySerialClass() {}

void MySerialClass::openSerial()
{
    fd = open(portName.c_str(), O_RDWR | O_NOCTTY | O_NDELAY);

    FD_ZERO(&fdset); 
    FD_SET(fd, &fdset); 
}

void MySerialClass::closeSerial()
{
    close(fd);
}

void MySerialClass::configureSerial()
{
    struct termios config = { 0 };

    tcgetattr(fd, &config);

    config.c_iflag = IGNPAR | ICRNL;
    config.c_oflag = 0;
    config.c_lflag = ICANON;

    config.c_cc[VINTR]    = 0;     /* Ctrl-c */
    config.c_cc[VQUIT]    = 0;     /* Ctrl-\ */
    config.c_cc[VERASE]   = 0;     /* del */
    config.c_cc[VKILL]    = 0;     /* @ */
    config.c_cc[VEOF]     = 4;     /* Ctrl-d */
    config.c_cc[VTIME]    = 0;     /* inter-character timer unused */
    config.c_cc[VMIN]     = 1;     /* blocking read until 1 character arrives */
    config.c_cc[VSWTC]    = 0;     /* '[=12=]' */
    config.c_cc[VSTART]   = 0;     /* Ctrl-q */
    config.c_cc[VSTOP]    = 0;     /* Ctrl-s */
    config.c_cc[VSUSP]    = 0;     /* Ctrl-z */
    config.c_cc[VEOL]     = 0;     /* '[=12=]' */
    config.c_cc[VREPRINT] = 0;     /* Ctrl-r */
    config.c_cc[VDISCARD] = 0;     /* Ctrl-u */
    config.c_cc[VWERASE]  = 0;     /* Ctrl-w */
    config.c_cc[VLNEXT]   = 0;     /* Ctrl-v */
    config.c_cc[VEOL2]    = 0;     /* '[=12=]' */

    speed_t sp = B9600;
    config.c_cflag |= CSIZE;
    config.c_cflag |= CS8;

    cfsetispeed(&config, sp);
    cfsetospeed(&config, sp);

    tcsetattr(fd, TCSAFLUSH, &config);
}

void MySerialClass::writeSerial(std::vector<char> data)
{
    char buffer[1024];

    if (data.size() > 1024)
        return;

    int index = 0;
    for (char &item : data)
        buffer[index++] = item;

    unsigned int size = data.size();
    write(fd, &buffer[0], size);
}

void MySerialClass::readSerial(std::vector<char> &data, unsigned int numBytes)
{
    char buffer[1024];
    data.clear();

    if (numBytes > 1024)
        return;

    struct timeval tv;
    tv.tv_sec = 0;
    tv.tv_usec = 0;

    int ret = select(fd + 1, 0, 0, 0, &tv);

    std::cout << "Select returns: " << ret << std::endl;

    if (!ret)
        return;

    read(fd, &buffer[0], numBytes);

    for (unsigned int i = 0; i < numBytes; i++)
        data.push_back(buffer[i]);
}

int main()
{
    MySerialClass serial("/dev/ttyS1");
    serial.openSerial();
    serial.configureSerial();

    while(1)
    {
        std::vector<char> retData;
        serial.readSerial(retData, 100);

        std::string retString(retData.begin(), retData.end());

        if (retString == "END")
        {
            serial.closeSerial();
            break;
        }
    }
}

它编译得很好,但它从不接收数据,因为 select() 语句总是 returns 零。带有阻塞选项但没有 select() 的代码工作正常(只需注释 select() 行并从 open() 中删除 O_NODELAY)。

我很确定这个问题与 select() 的使用方式有关(这是我第一次使用 select())。

有人可以帮我解决吗?该代码可在 Coliru here

顺便说一句:我对 select() 的另一个疑问是这个 class 将在多线程环境中使用。我需要确保每个 class 实例只检查其端口繁忙(它自己的 fd),没有其他线程端口繁忙。

没有指定要阅读的fd_set。试试这个:

fd_set readfs;    /* file descriptor set */

FD_ZERO(&readfs); /* clear the set */
FD_SET(fd, &readfs); /* put the fd in the set */
int ret = select(fd + 1, &readfs, 0, 0, &tv);

编辑:这也应该可以解决您的其他问题。每个 select 只查看您告诉它查看的文件描述符。

哎呀。英语语法不好,但更正后看起来更糟。