无法在 C 代码中模仿 `echo` 命令的动作

Cannot mimic the action of `echo` command in C code

我写了几个 bash 脚本文件,它们通过两个串行端口进行通信。可以将一个脚本视为接收器,将另一个脚本视为发送器。接收脚本逐行读取并显示数据,直到读取到字符序列<break>。然后它停止监听,并将 onetwo 的字符序列发送回发送器。因此脚本是:

#!/bin/bash

# Read and reply bash script.

exec 3<> /dev/ttyS4
while true; do
    #cat -v /dev/ttyS4 | while read -r input; do
    while read -r -u 3 input; do
        if [ "$input" = "<break>" ]
        then
            echo "break command received."
            break
        else
            echo -e "${input}"
        fi
    done 

    echo "Sending selection"
    if [ "$selection" = "one" ]
    then
        selection="two"
    else
        selection="one"
    fi
    echo "$selection" >&3

done

发送端脚本发送一些字符数据,然后等待上面接收端脚本onetwo的回复:

exec 4<> /dev/ttyS0
selection="one"
while true; do
    echo "************************************" > /dev/ttyS0
    echo "       Selection: ${selection}" > /dev/ttyS0
    echo "************************************" > /dev/ttyS0
    echo "<break>" > /dev/ttyS0
    read -r -t 3 -u 4 input
    if [ -z "$input" ]
    then
        echo "Response from remote timed out."
    elif [ "$input" = "one" ]
    then
        selection=$input
    elif [ "$input" = "two" ]
    then
        selection=$input
        colour=$RED
    else
        echo "Unknown selection: $input"
    fi

    sleep 1 
done

以上两个脚本工作正常,接收脚本正确识别 <break> 字符序列。

我想用一个小的 C 程序替换 'transmitter' 脚本,但是我发现当我发送字符序列 <break> 时,接收器脚本这次 NOT 标识为 'end-of-transmission',是对标准输出的简单回显 <break>,而不是 break command received。我已经尝试了几种方法,例如添加转义字符,但是 Bash echo 的工作方式以及我在 C 代码中发送数据的方式显然有所不同:

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>

#define TRUE 1
#define FALSE 0

int main(int argc, char *argv[]) {
    int selected_tool = DEFAULT_TOOL_SELECTION;
    FILE *ser2_fd_write, *ser2_fdrw;
    struct termios tios, tios_w;        // ser 2 termios structure
    int ser2_fd, ser2_fdw;
    char read_buffer[BUFFER_SIZE];
    struct timespec tdelay;

    bzero(&tdelay, sizeof(tdelay));
    tdelay.tv_sec = 1;
    tdelay.tv_nsec = 5000;

    if ((ser2_fd = open("/dev/ttyS0", O_RDWR)) == -1){
        printf("Unable to open ttyS0 as read-write only.\n");
        return EXIT_FAILURE;
    }

    bzero(&tios, sizeof(tios));
    cfsetispeed(&tios, B38400);
    cfsetospeed(&tios, B38400);


    tios.c_cflag = B38400 | CS8 | CLOCAL | CREAD | CRTSCTS;
    tios.c_iflag = IGNPAR;
    tios.c_oflag = 0;
    tios.c_lflag = ICANON; //0
    tios.c_cc[VTIME] = 0;
    tios.c_cc[VMIN] = 10;

    tcflush(ser2_fd, TCIFLUSH);
    if (tcsetattr(ser2_fd, TCSANOW, &tios) == -1){
        printf("Could not set ser 2 attributes.\n");
        return -1;
    }

    if ((ser2_fdrw = fdopen(ser2_fd, "awr")) == NULL){
        printf("Unable to open file descriptor.\n");
        return EXIT_FAILURE;
    }

while(1){

    push_to_ser2(ser2_fdrw, selected_tool);
    /*
     * This is where sending <break> differs from echo
     */

    //fputs("break", ser2_fdrw);
    fprintf(ser2_fdrw, "break\r\n");
    //fprintf(ser2_fdrw, "\r\n");
    //write(ser2_fd,"break\r\n", 9);
    fflush(ser2_fdrw);
    int c = 0;

    nanosleep(&tdelay, NULL);
    tcflush(ser2_fd, TCIOFLUSH);
    tcdrain(ser2_fd);
    fcntl(ser2_fd, F_SETFL, 0);

    if ( (c = read(ser2_fd, read_buffer, BUFFER_SIZE)) > 0){
        read_buffer[c] = 0;
        if (strcmp(read_buffer, "one\r\n") == 0){
            selected_tool = 1;
        } else if (strcmp(read_buffer, "two\r\n") == 0){
            selected_tool = 2;
        }
    }else{

    }
}
    return EXIT_SUCCESS;
}


/*
 * Convenience function to push data to ser 2
 * *c_data      pointer to the card data
 * *t_data      pointer to the tool data
 */
void push_to_ser2(FILE * fd, int tool){

    fprintf(fd, "**********************************************************\n");
    fprintf(fd, "*                                                        *\n");
    fprintf(fd, "*                     Tool %d Data                       *\n", tool);
    fprintf(fd, "*                                                        *\n");
    fprintf(fd, "**********************************************************\n");


    fprintf(fd,"\r\n");
    fflush(fd);
}

我也尝试过对 termios 结构进行各种更改,但没有任何区别。有什么想法吗?

看起来好像 bash 脚本正在发送文字字符串 <break>,包括尖括号。同时,C 程序只发送 break。将尖括号添加到 C 程序中的文字。 (这些括号对 echofprintf 没有任何特殊意义,因此它们按原样发送。)

您的代码有几个问题需要更正:

  • 而不是 bzero(&tios, sizeof(tios)) 代码应该调用 tcgetattr() 来正确初始化结构。对于规范输入来说,这可能是一个严重的问题,因为现有代码会将所有控制代码规范归零,而不是具有正确的定义。
  • 代码应该执行按位运算而不是直接赋值(以保留现有设置)。参见 Setting Terminal Modes Properly
  • 您在指定非规范输出和规范输入,这是一种非典型组合。似乎你想要输入和输出的规范模式。
  • VMIN 和 VTIME 仅对非规范输入有意义,不应为规范输入指定(因为它破坏了 VEOF 和 VEOL 字符规范)。
  • read(ser2_fd,...) 正在静默忽略错误。
  • 不使用 fdopen()fprintf(),只需使用 write(ser2_fd , ...) 来简化代码和开销。缺点是您必须指定字节数并使用 sprintf() 执行整数到字符串的转换。

参见Serial Programming Guide for POSIX Operating Systems