Почему моя длина буфера игнорируется?
Я разрабатываю приемник для небольшого аппаратного проекта. Я работаю на небольшой плате, которая использует UART для передачи данных.
Код приемника показан полностью ниже, я кратко объясню неисправные бит отдельно.
#define TTY "/dev/ttys002"
#include <stdio.h>
#include <string.h>
#include <unistd.h> //Unix standard functions
#include <fcntl.h> //File controls
#include <errno.h> //Error numbers
#include <assert.h>
#include <termios.h> //Posix terminal
int open_port(const char * tty) {
int fd;
fd = open(tty, (O_RDWR | O_NOCTTY | O_NDELAY));
assert("__failed to open port__" && fd != -1);
//Block until read
fcntl(fd, F_SETFL, 0);
return fd;
}
void configure_port(int fd) {
struct termios options;
//Get current options
tcgetattr(fd, &options);
//9600 Baud
cfsetispeed(&options, B9600);
cfsetospeed(&options, B9600);
//Receive & local mode
options.c_cflag |= (CLOCAL | CREAD);
//Raw output
options.c_oflag &= ~OPOST;
//No hardware flow control
options.c_cflag &= ~CRTSCTS;
//No parity, 1 stop bit
options.c_cflag &= ~PARENB;
options.c_cflag &= ~CSTOPB;
//8 data bits
options.c_cflag &= ~CSIZE;
options.c_cflag |= CS8;
//Write options back, set them immediately
tcsetattr(fd, TCSANOW, &options);
}
int main(int argc, const char * argv[]) {
int fd = open_port(TTY);
const size_t count = 8;
char buf[count + 1];
ssize_t n;
configure_port(fd);
while (1) {
n = read(fd, buf, count);
buf[count] = '\0';
if (n > 0) {
printf("%s\n", buf);
}
}
return 0;
}
Поскольку в настоящее время у меня нет моего оборудования, я решил проверить свой приемник на регулярной tty (#define TTY "/dev/ttys002"
). Чтобы протестировать его, я просто скомпилировал и выполнил вышеуказанный код, а затем открыл отдельный терминал и:
echo "text" >> /dev/ttys002
Все это прекрасно работает, и я получаю все данные, которые я повторяю в tty.
Однако проблема возникает при вводе длинного сообщения в tty:
echo "this is a longer test message" >> /dev/ttys002
Я получаю все сообщение в виде одной строки в своем программном выпуске. Почему это так? Я бы ожидал, что текст будет разбит на блоки из 8 символов (const size_t count = 8;
).
Если это важно, я использую это руководство в качестве моего варианта конфигурации.
Изменить: Пожалуйста, просмотрите комментарии для дальнейшего обсуждения проблемы.
Ответы
Ответ 1
IMHO, ваше сообщение разделено на блоки из восьми символов: n = read(fd, buf, count);
не может выдавать больше, чем считать байты за раз.
Но поскольку вы не настраиваете строку tty в режиме RAW, все еще находится в режиме буферизации в строке. Таким образом, основной драйвер блокирует первое чтение до тех пор, пока он не завершит полную строку с помощью \n
(или превысит емкость буфера).
Затем чтение возвращается с первыми 8 байтами, а последующие чтения сразу возвращаются с 8 байтами, так как данные доступны в буфере драйвера.
Вы должны смотреть на неканонический режим в человеческих терминах, если вы хотите использовать режим сырого ввода:
options.c_cflags &= ~ICANON;
/* optionally if defaults are not appropriates */
options.c_cc[VMIN] = vmin; /* default is 1 */
options.c_cc[VTIME] = vtime; /* default is 0 */
Но в любом случае вы читаете chararacters и никогда не добавляете завершающий null, поэтому buf не имеет причины для отказа от null. Вы должны только напечатать количество фактически прочитанных символов:
const size_t count = 8;
char buf[count];
ssize_t n;
configure_port(fd);
while (1) {
n = read(fd, buf, count);
if (n > 0) {
printf("%.*s\n", n, buf); /* only print the n characters */
}
}
Ответ 2
Кажется, что ваша проблема имеет то же имя, что и на этом сайте:)
Вы выделяете 8 байт данных в стек для вашей переменной buf
. Затем в функции read
вы пишете строку "это более длинное тестовое сообщение", длина которой составляет более 8 байтов. Когда вы используете функцию printf
с "% s", printf
перестанет печатать, когда она достигнет нулевого символа (\0
), так как ожидает, что ваша строка будет завершена с нулевой отметкой.
В вашем случае вы должны проверить, не изменена ли ваша переменная buf
. Если это не так, вы должны сделать последний байт buf
равным null символу
Ответ 3
То, что вы ищете, - это псевдо-tty (PTY).
Простым способом создания PTY является использование socat(1)
следующим образом:
socat pty,link=/tmp/testpty stdio
Это создаст PTY и прикрепит текущий терминал stdio к его главной стороне. Независимо от того, какие данные вы ввели здесь, будут отправлены на подчиненную сторону PTY.
Теперь подключитесь к подчиненной стороне из вашей программы, и она будет работать так, как вы ожидаете:
#define TTY "/tmp/testpty"