4

我正在尝试使用“ Introduction to Interprocess Communication Using Named Pipes - Full-Duplex Communication Using Named Pipes ”,链接;特别是fd_server.c(包括在下面以供参考)

这是我的信息和编译行:

:~$ cat /etc/issue
Ubuntu 10.04 LTS \n \l
:~$ gcc --version
gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3
:~$ gcc fd_server.c -o fd_server

fd_server.c创建两个命名管道,一个用于读取,一个用于写入。可以做的是:在一个终端中,运行服务器并读取(通过cat)它的写入管道:

:~$ ./fd_server & 2>/dev/null
[1] 11354
:~$ 猫 /tmp/np2

在另一个中,写入(使用回声)到服务器的读取管道:

:~$ echo "heeellloooo" > /tmp/np1

回到第一个终端,可以看到:

:~$ 猫 /tmp/np2
嘻嘻嘻
0[1]+ 退出 13 ./fd_server 2> /dev/null

我想做的是进行某种“交互式”(或“壳”式)会话;也就是说,服务器像往常一样运行,但不是运行catand echo,我想使用类似于screen的东西。我的意思是,屏幕可以被称为screen /dev/ttyS0 38400,然后它进行一种交互式会话,在终端中输入的内容被传递给/dev/ttyS0,其响应被写入终端。现在,当然,我不能使用screen,因为在我的情况下,程序有两个独立的节点,据我所知,screen只能引用一个。

在这种情况下(使用两个单独的读/写管道)如何实现这种“交互式”会话?

下面的代码:

#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//#include <fullduplex.h> /* For name of the named-pipe */
#define NP1     "/tmp/np1"
#define NP2     "/tmp/np2"
#define MAX_BUF_SIZE    255
#include <stdlib.h> //exit
#include <string.h> //strlen

int main(int argc, char *argv[])
{
    int rdfd, wrfd, ret_val, count, numread;
    char buf[MAX_BUF_SIZE];

    /* Create the first named - pipe */
    ret_val = mkfifo(NP1, 0666);

    if ((ret_val == -1) && (errno != EEXIST)) {
        perror("Error creating the named pipe");
        exit (1);
    }

    ret_val = mkfifo(NP2, 0666);

    if ((ret_val == -1) && (errno != EEXIST)) {
        perror("Error creating the named pipe");
        exit (1);
    }

    /* Open the first named pipe for reading */
    rdfd = open(NP1, O_RDONLY);

    /* Open the second named pipe for writing */
    wrfd = open(NP2, O_WRONLY);

    /* Read from the first pipe */
    numread = read(rdfd, buf, MAX_BUF_SIZE);

    buf[numread] = '0';

    fprintf(stderr, "Full Duplex Server : Read From the pipe : %sn", buf);

    /* Convert to the string to upper case */
    count = 0;
    while (count < numread) {
        buf[count] = toupper(buf[count]);
        count++;
    }

    /*
     * Write the converted string back to the second
     * pipe
     */
    write(wrfd, buf, strlen(buf));
}

编辑:

对,只是为了澄清-似乎我发现了一个讨论非常相似的文档,它是-那里的脚本的修改(“例如,以下脚本配置设备并启动后台进程以复制从串行接收到的所有数据设备到标准输出... ") 用于上述程序如下:

# stty raw # 
( ./fd_server 2>/dev/null; )&
bgPidS=$!
( cat < /tmp/np2 ; )&
bgPid=$!
# Read commands from user, send them to device
echo $(kill -0 $bgPidS 2>/dev/null ; echo $?)
while [ "$(kill -0 $bgPidS 2>/dev/null ; echo $?)" -eq "0" ] && read cmd; do
   # redirect debug msgs to stderr, as here we're redirected to /tmp/np1
   echo "$? - $bgPidS - $bgPid" >&2
   echo "$cmd"
   echo -e "\nproc: $(kill -0 $bgPidS 2>/dev/null ; echo $?)" >&2
done >/tmp/np1
echo OUT
# Terminate background read process - if they still exist
if [ "$(kill -0 $bgPid 2>/dev/null ; echo $?)" -eq "0" ] ;
then
    kill $bgPid
fi
if [ "$(kill -0 $bgPidS 2>/dev/null ; echo $?)" -eq "0" ] ;
then
    kill $bgPidS
fi
# stty cooked

因此,将脚本保存为 saystarter.sh并调用它,会产生以下会话:

$ ./starter.sh 
0
i'm typing here and pressing [enter] at end
0 - 13496 - 13497
I'M TYPING HERE AND PRESSING [ENTER] AT END
0~�.N=�(�~� �����}����@������~� [garble]
proc: 0
OUT

这就是我所说的“交互式会话”(忽略调试语句) - 服务器等待我输入命令;它在收到命令后给出输出(在这种情况下,它在第一个命令后退出,启动脚本也是如此)。除此之外,我不想缓冲输入,而是一个字符一个字符地发送(这意味着上面的会话应该在第一次按键后退出,并且只打印一个字母 - 这是我期望sttyraw 会有所帮助的,但是它没有:它只会杀死对两者的反应Enter- Ctrl:) C

如果已经有一个现有的命令(类似于screen串行设备,我猜),我只是在徘徊,它将接受两个这样的命名管道作为参数,并通过它们建立一个类似“终端”或“外壳”的会话;或者我是否必须使用上述脚本和/或程序自己的“客户端”,它将充当终端..

4

1 回答 1

3

如果您只是希望能够接收多行,而不是在一行之后退出,这很简单。你只需要在你的读/写代码周围放置一个循环,就像这样(又快又脏):

while( 1 ) {
    numread = read(rdfd, buf, MAX_BUF_SIZE);

    fprintf(stderr, "Full Duplex Server : Read From the pipe : %sn", buf);

    /* Convert to the string to upper case */
    count = 0;
    while (count < numread) {
        buf[count] = toupper(buf[count]);
        count++;
    }

    /*
     * Write the converted string back to the second
     * pipe
     */
    write(wrfd, buf, strlen(buf));
}

当然,现在你有一个永远不会退出的应用程序,一旦它得到一个 EOF 等就会开始什么都不做。所以,你可以重新组织它来检查错误:

numread = read(rdfd, buf, MAX_BUF_SIZE);
while( numread > 0) {
    /* ... etc ... */
    numread = read(rdfd,buf, MAX_BUF_SIZE);
}
if( numread == 0 )  {
    /* ... handle eof ... */
}
if( numread < 0 ) {
    /* ... handle io error ... */
} 

从手册页中, read 返回 0 表示 EOF,返回 -1 表示错误(您已经阅读了手册页,对吗? http: //linux.die.net/man/2/read)。因此,它所做的就是不断从读取管道中获取字节,直到它到达 EOF 或某些错误,在这种情况下,您(可能)打印一条消息并退出。也就是说,您可能会在获得 EOF 时重新打开,以便获得更多输入。

一旦您将程序修改为连续读取,交互式输入多行就很简单了。只需执行:

cat - > /tmp/np1

'-' 明确告诉 cat 从标准输入读取(这是默认设置,因此您实际上不需要破折号)。因此 cat 会将您输入的所有内容传递给您的管道程序。您可以使用 Ctrl+D 插入 EOF,这将导致 cat 停止读取标准输入。您的管道程序会发生什么取决于您如何处理读取循环中的 EOF。

现在,如果你想要另一个没有 cat 的程序来完成所有的 io(所以你最终得到了一个 stdio echo 程序),伪代码看起来像这样:

const int stdin_fd = 0;  // known unix constant!
int readpipe_fd = open the read pipe, as before 
int writepipe_fd =  open the write pipe, as before 
read stdin into buffer
while( stdin is reading correctly ) {
     write data from stdin to read pipe
         check write is successful
     read write pipe into buffer
         check read is successful
     write buffer to stdout (fprintf is fine)
     read stdin into buffer.
}

如果您愿意,可以使用 read 系统调用来读取 stdin,但您也可以只使用 stdio。读取、写入和打开管道都应该与您的服务器程序相同,除了读/写都是相反的。

于 2010-05-06T13:36:42.987 回答