写入主pty,但无法在从pty中读取 :(

3

尝试编写基本测试。程序必须启动tcp服务器,接收连接并将接收到的数据重定向到分叉的程序中。以下是代码:

#include "TcpServer.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <termios.h>
#include <stropts.h>
#include <sys/ioctl.h>

//test
#include <pty.h>

int main() {

    if (setsid() < 1) {
        perror("cannot setsid()");
        return 1;
    }

    TcpServer tcp_server;
    int client;
    char buf[1024];

    int master, slave;
    char * slave_name;


    bzero(buf, sizeof(buf));

    tcp_server.setPort(1025);
    int server = tcp_server.startup();
    client = accept(server, NULL, NULL);

    fprintf(stderr, "forking...\n");
    pid_t pid = forkpty(&master, NULL, NULL, NULL);


    if (pid == 0) {
        setsid();


        fprintf(stderr, "slave pty is opened\n");

        fprintf(stdout, "hello, client!\n");

        char * cmd[] = {"upper.py", NULL};

        fprintf(stderr, "slave: start to login\n");
        int res = execvp("upper.py", cmd);
        if (res < 0) {
            fprintf(stderr, "trouble while running exec - '%s'\n", strerror(errno));
            return 1;
        }
        fprintf(stderr, "slave: quit\n");
        return 0;
    } else if (pid < 0) {
        perror("cannot fork off process");
        return 1;
    }

    fd_set in;
    int max_fd, n;

    struct termios ot, t;
    tcgetattr(master, &ot);
    t = ot;
    t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT );
    t.c_iflag |= IGNBRK;
    t.c_cc[VMIN] = 1;
    t.c_cc[VTIME] = 0;
    tcsetattr(master, TCSANOW, &t);

    while (1) {
        FD_ZERO(&in);

        max_fd = (master > max_fd) ? master : max_fd;
        FD_SET(master, &in);

        max_fd = (client > max_fd) ? client : max_fd;
        FD_SET(client, &in);

        fprintf(stderr, "select starts\n");
        if (select(max_fd + 1, &in, NULL, NULL, NULL) < 0) {
            fprintf(stderr, "select returned incorrectly\n");
            if (errno == EINTR) 
                continue;

            perror("error while select");
            return 1;
        }

        if (FD_ISSET(client, &in)) {
            fprintf(stderr, "client is set\n");
            n = recv(client, buf, sizeof(buf), 0);

            if (n < 0) {
                fprintf(stderr, "error while receiving data from client '%s'\n", strerror(errno));
                return 1;
            }

            if (n <= sizeof(buf)) {
                buf[n] = 0;
            }

            fprintf(stderr, "received from client: '%s'\n", buf);

            n = write(master, buf, n);
            tcflush(master, TCIOFLUSH);
        }

        if (FD_ISSET(master, &in)) {
            fprintf(stderr, "master is set ");

            n = read(master, buf, sizeof(buf));

            if (n < 0) {
                fprintf(stderr, ": %s - ", strerror(errno));
                return 1;
            }
            if (n <= sizeof(buf)) 
                buf[n] = 0;
            fprintf(stderr, "sending data - '%s'\n", buf);
            n = send(client, buf, n, 0);
        }

    }

    return 0;
}

程序输出:

[milo@milo telnetd]$ sudo ./server 
forking...
select starts
master is set sending data - 'slave pty is opened
hello, client!
slave: start to login
'
select starts
master is set sending data - 'input string: '
select starts
client is set
received from client: 'hello'
select starts

问题简述:服务器成功地从tcp客户端接收数据,将其写入主pty,但从端未收到。我看了很多例子,但是找不出错误所在。请给我提建议...
更新:我尝试使用int len = read(STDIN_FILENO, buf, sizeof(buf));代替execvp,它可以正常工作!我想我必须发送某种控制符号,比如回车... 有什么想法吗?

虚假代码。在您将ot传递给forkpty时,它不在范围内,并且似乎未初始化... - R.. GitHub STOP HELPING ICE
抱歉。已修复。请再试一次。 - milo
2个回答

1

太好了!我解决了这个问题!错误在于(如我之前在UPD中所说),输入中缺少\n符号!另一方面,程序等待输入直到出现换行符号!


哈哈。有趣的是,我刚遇到了同样的问题。你会认为这应该更明显,但却需要编写一个十六进制输出回显程序在另一端运行才能找到它。 - darron

0

你的孩子做的第一件事情就是调用setsid函数。根据POSIX文档(我强调):

如果调用进程不是进程组的组长,则setsid()函数将创建一个新会话。返回时,调用进程将成为此新会话的会话领导者,将成为新进程组的进程组领导者,并且将没有控制终端

因此,你的孩子做的第一件事情就是与其控制终端分离。


我尝试使用ioctl和TIOCNOTTY从子进程中分离从设备pty,但没有任何效果。 - milo
1
如果子进程没有tty,它如何尝试从其tty读取数据? - Nemo
每个子进程都有自己的tty,因为forkpty()函数会处理这一点。 - milo
1
在调用setsid之前,子进程拥有自己的tty。在调用setsid之后(在子进程中;即在if(pid == 0)测试之后),子进程根本没有tty。(请注意,我谈论的是您代码中的第二次调用setsid,而不是在forkpty之前执行的那个)。 - Nemo
我后来注释掉了setsid(),抱歉,这个源码已经不再实际。我已经解决了问题。谢谢你的参与! - milo
是的。抱歉,那不是问题 :-). 不过,如果您只想在文件描述符上使用读/写操作,那么管道比主/从TTY更简单、更高效。 - Nemo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接