如何在使用 boost::asio 发送数据时丢弃数据?
我正在编写一些使用 boost::asio
类读写串行设备的代码.但是,在程序之间发送多个字符串时,我注意到在接收程序上,数据是按照写入串行端口的顺序读取的,而不是从其他程序发送的数据 - 如果我开始阅读几秒钟后的数据,我没有得到我现在发送的值,而是那些以前发送的值.我假设这是由我设置 boost::asio::serial_port
:
I'm writing some code that reads and writes to serial device using boost::asio
class. However, when sending several strings between programs, I've noticed that on the receiving program the data is read in the order as it was written to the serial port, and not as the data is sent from the other program - If I start reading data some seconds later, I don't get the values that I am sending at the moment but those that were sent previously. I'm assuming this is caused by how I am setting up my 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
",pty.data());
printf("Baud rate: %d
",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+='
';
std::cout << data;
write(port,boost::asio::buffer(data.c_str(),data.size()));
sleep(1);
val++;
data.clear();
}
port.close();
return 0;
}
有没有办法强制在新值发送到串行端口后立即丢弃过去的数据(我认为应该在代码的 write() 部分完成)?
Is there a way to force past data to be discarded as soon as a new value is sent to the serial port (which I assume should be done on the write() part of the code)?
推荐答案
Boost.Asio 没有为刷新串行端口的缓冲区提供更高级别的抽象.但是,这通常可以通过特定平台的调用来完成,例如 tcflush()
或 PurgeComm()
,在串口的native_handle().
Boost.Asio does not provide a higher-level abstraction for flushing a serial port's buffers. However, this can often be accomplished by having platform specific calls, such as tcflush()
or PurgeComm()
, operate on a serial port's native_handle().
每个串行端口都有一个接收和发送缓冲区,刷新操作在一个或两个缓冲区上进行.例如,如果连接了两个串口(/dev/pts/3
和/dev/pts/4
),程序A
打开并写入 /dev/pts/3
,然后它只能刷新与 /dev/pts/3
关联的缓冲区(在 /dev/pts 上接收的数据/3
但未读取,数据写入 /dev/pts/3
但未传输).因此,如果程序 B
启动,打开 /dev/pts/4
,并想读取 non-stale 数据,那么程序 B
需要打开串口后刷新/dev/pts/4
的接收缓冲区.
Each serial port has a receive and transmit buffer, and flushing operates on one or both of the buffers. For example, if two serial ports are connected (/dev/pts/3
and /dev/pts/4
), and program A
opens and writes to /dev/pts/3
, then it can only flush the buffers associated with /dev/pts/3
(data received on /dev/pts/3
but not read, and data written to /dev/pts/3
but not transmitted). Therefore, if program B
starts, opens /dev/pts/4
, and wants to read non-stale data, then program B
needs to flush the receive buffer for /dev/pts/4
after opening the serial port.
这是一个在 CentO 上运行的完整示例.当示例作为写入器运行时,它将每秒向串行端口写入一个顺序递增的数字.当示例作为写入器运行时,它将读取五个数字,休眠 5 秒并每隔一次迭代刷新其读取缓冲区:
Here is a complete example running on CentOs. When the example runs as a writer, it will write a sequentially increasing number to the serial port once a second. When the example runs as a writer, it will read five numbers, sleep for 5 seconds and flush its read buffer every other iteration:
#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
.
As demonstrating in the output, numbers are only skipped in the sequence when the reader flushes its read buffer: 3 4 noflush 5 6 7 8 9 flush 14 15
.
相关文章