【问题标题】:How to discard data as it is sent with boost::asio?如何在使用 boost::asio 发送数据时丢弃数据?
【发布时间】:2014-03-22 17:51:14
【问题描述】:

我正在编写一些使用boost::asio 类读写串行设备的代码。但是,在程序之间发送多个字符串时,我注意到在接收程序上,数据是按照写入串行端口的顺序读取的,而不是从其他程序发送数据的顺序读取的 - 如果我开始阅读几秒钟后的数据,我没有得到我现在发送的值,而是那些以前发送的值。我假设这是由我设置 boost::asio::serial_port 的方式引起的:

int main(int argc, char const *argv[]){

    int baud=atoi(argv[1]);
    std::string pty=argv[2];

    printf("Virtual device: %s\n",pty.data());
    printf("Baud rate: %d\n",baud);

    boost::asio::io_service io;
    boost::asio::serial_port port(io, pty);
    port.set_option(boost::asio::serial_port_base::baud_rate(baud));

    // counter that writes to serial port in 1s intervals
    int val=0;
    while (1){
        std::string data=std::to_string(val);
        data+='\n';
        std::cout << data;
        write(port,boost::asio::buffer(data.c_str(),data.size()));
        sleep(1); 
        val++;
        data.clear();
    }

    port.close(); 
    return 0;
}

有没有办法在新值发送到串行端口后强制丢弃过去的数据(我认为应该在代码的 write() 部分完成)?

【问题讨论】:

  • 我对 boost asio 不熟悉,但在将其归咎于您的程序之前,我建议您使用任何终端程序(如 putty)检查是否发生了同样的事情。
  • 我一启动 putty,窗口就会显示所有发送的数据,但会在发送时同时打印。
  • @joaocandre 你可以尝试取消任何运行中的异步操作(使用port.cancel()
  • 您的描述中不清楚数据流:您如何“在程序之间发送多个字符串”?串口在这个流程中的作用是什么?你用同步吗?或异步。输入/输出?
  • 和上面的函数一样,我分别用write()read()发送和读取数据,所以我假设我使用的是同步I/O。串行端口是使用socat 创建的,所以我有一对虚拟串行设备,我用一个程序写入并用另一个程序读取。

标签: c++ boost serial-port boost-asio tty


【解决方案1】:

Boost.Asio 没有为刷新串行端口的缓冲区提供更高级别的抽象。但是,这通常可以通过在串行端口的native_handle() 上运行特定于平台的调用(例如tcflush()PurgeComm())来实现。

每个串行端口都有一个接收和发送缓冲区,刷新操作在一个或两个缓冲区上进行。例如,如果连接了两个串口(@98​​7654326@ 和/dev/pts/4),并且程序A 打开并写入/dev/pts/3,那么它只能刷新与/dev/pts/3 关联的缓冲区(在@987654331 上接收到的数据) @ 但未读取,数据写入 /dev/pts/3 但未传输)。因此,如果程序B启动,打开/dev/pts/4,想要读取非过期数据,那么程序B需要在打开串口后刷新/dev/pts/4的接收缓冲区。


这是一个在 CentO 上运行的完整示例。当示例作为写入器运行时,它将每秒向串行端口写入一个顺序递增的数字。当示例作为写入器运行时,它将读取五个数字,休眠 5 秒并每隔一次迭代刷新其读取缓冲区:

#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

/// @brief Different ways a serial port may be flushed.
enum flush_type
{
  flush_receive = TCIFLUSH,
  flush_send = TCOFLUSH,
  flush_both = TCIOFLUSH
};

/// @brief Flush a serial port's buffers.
///
/// @param serial_port Port to flush.
/// @param what Determines the buffers to flush.
/// @param error Set to indicate what error occurred, if any.
void flush_serial_port(
  boost::asio::serial_port& serial_port,
  flush_type what,
  boost::system::error_code& error)
{
  if (0 == ::tcflush(serial_port.lowest_layer().native_handle(), what))
  {
    error = boost::system::error_code();
  }
  else
  {
    error = boost::system::error_code(errno,
        boost::asio::error::get_system_category());
  }
}

/// @brief Reads 5 numbers from the serial port, then sleeps for 5 seconds,
///        flushing its read buffer every other iteration.
void read_main(boost::asio::serial_port& serial_port)
{
  std::vector<unsigned char> buffer(5);
  for (bool flush = false;; flush = !flush)
  {
    std::size_t bytes_transferred =
        read(serial_port, boost::asio::buffer(buffer));
    for (std::size_t i = 0; i < bytes_transferred; ++i)
      std::cout << static_cast<unsigned int>(buffer[i]) << " ";
    boost::this_thread::sleep_for(boost::chrono::seconds(5));
    if (flush)
    {
      boost::system::error_code error;
      flush_serial_port(serial_port, flush_receive, error);
      std::cout << "flush: " << error.message() << std::endl;
    }
    else
    {
      std::cout << "noflush" << std::endl;
    }
  }
}

/// @brief Write a sequentially increasing number to the serial port
///        every second.
void write_main(boost::asio::serial_port& serial_port)
{
  for (unsigned char i = 0; ; ++i)
  {
    write(serial_port, boost::asio::buffer(&i, sizeof i));
    boost::this_thread::sleep_for(boost::chrono::seconds(1));
  }
}

int main(int argc, char* argv[])
{
  boost::asio::io_service io_service;
  boost::asio::serial_port serial_port(io_service, argv[2]);
  if (!strcmp(argv[1], "read"))
    read_main(serial_port);
  else if (!strcmp(argv[1], "write"))
    write_main(serial_port);
}

socat创建虚拟串口:

$ socat -d -d PTY: PTY:
2014/03/23 16:22:22 socat[12056] N PTY is /dev/pts/3
2014/03/23 16:22:22 socat[12056] N PTY is /dev/pts/4
2014/03/23 16:22:22 socat[12056] N starting data transfer loop with
                                 FDs [3,3] and [5,5]

同时启动读写示例:

$ ./a.out read /dev/pts/3 & ./a.out write /dev/pts/4
[1] 12238
0 1 2 3 4 noflush
5 6 7 8 9 flush: Success
14 15 16 17 18 noflush
19 20 21 22 23 flush: Success
28 29 30 31 32 noflush
33 34 35 36 37 flush: Success

如输出所示,仅当读取器刷新其读取缓冲区时,才会在序列中跳过数字:3 4 noflush 5 6 7 8 9 flush 14 15

【讨论】:

  • 在这种情况下,每次读写操作都使用相同的缓冲区。就我而言,我在循环中声明了std::string。这应该改变什么吗?
  • @joaocandre 不。它对 Boost.Asio 没有任何影响。
猜你喜欢
  • 2011-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多