在新终端中执行子进程

时间:2013-02-15 10:01:03

标签: c sockets unix multiprocessing unix-socket

我想为unix制作一个简单的聊天应用程序。 我创建了一个支持多个客户端的服务器。当新客户端连接到服务器时,使用fork命令创建新进程。现在的问题是所有子进程在服务器上共享相同的stdin,因此为了向第二个clien发送消息,第一个子进程必须终止。为了解决这个问题,我想在一个新终端中运行每个子进程。 这可以通过在新文件中编写子进程代码的代码并像xterm -e sh -c那样执行来实现(虽然我没试过)。

我真正想要的是没有两个文件只是为了启动一个新终端并运行其中的其余代码。

int say(int socket)
{
    char *s;
    fscanf(stdin,"%79s",s);
    int result=send(socket,s,strlen(s),0);
    return result;
}

int main()
{
    int listener_d;
    struct sockaddr_in name;
    listener_d=socket(PF_INET,SOCK_STREAM,0);
    name.sin_family=PF_INET;
    name.sin_port=(in_port_t)htons(30000);
    name.sin_addr.s_addr=htonl(INADDR_ANY);
    int c = bind(listener_d,(struct sockaddr *)&name,sizeof(name)); //Bind
    if(c== -1)
    {
        printf("\nCan't bind to socket\n");
    }

    if(listen(listener_d,10) == -1) // Listen
    {
        printf("\nCan't listen\n");
    }
    puts("\nWait for connection\n");
    while(1)
    {
        struct sockaddr_storage client_addr;
        unsigned int address_size = sizeof(client_addr);
        int connect_d = accept(listener_d, 
              (struct sockaddr*)&client_addr,&address_size); //Accept
        if(connect_d== -1)
        {
            printf("\nCan't open secondary socket\n");
        }

        if(!fork())
        {
            close(listener_d);
            char *msg = "welcome Sweetone\n";
            if(send(connect_d,msg,strlen(msg),0))
            {
                printf("send");
            }
            int k=0;
            while(k<5)
            {
                say(connect_d);
                ++k;
            }
            close(connect_d);
            exit(0);
        }
            close(connect_d);
    }
    close(listener_d);
    return 0;
}

2 个答案:

答案 0 :(得分:1)

我认为客户端和服务器之间发送的消息有点不寻常。更常见的是,在这个简单的“只是测试它是如何工作的”场景中让客户端向服务器发送消息。作为一个例子,我可以提到一个简单的echo服务,它将客户端发送的所有内容镜像回客户端。这种设计是否受到某些要求的限制?

除了批评之外,我有两个单独的更改可以使您当前的设计工作。它们都涉及更改子服务器中输入的读数。

备选方案1: 而不是从stdin读取,创建命名管道(请参阅man 3 mkfifo),fex / tmp / childpipe“pid_of_subserver_here”。您可以在say()中创建管道并打开它以供阅读。然后使用echo(man echo)写入管道回声“我的消息”&gt;的/ tmp / childpipe “NNNN”。在退出孩子之前,请记得使用unlink()

删除管道

备选方案2: 在服务器和每个子服务器之间创建一个未命名的管道。这使代码更加混乱,但避免创建命名管道和使用echo。示例代码包含在下面。它没有足够的错误处理(像大多数示例代码一样),并且无法正确处理断开客户端。

示例用法:1)启动服务器./a.out 2)(在外部窗口中连接客户端(例如nc localhost 30000)3)通过键入“1Hello client one”4来写入客户端1(在第三个客户端连接第二个客户端)窗口等)4)键入“2Hello second client”

写入第二个客户端
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

enum max_childeren{
    MAX_CHILDEREN = 50
};

int say(int socket)
{
    char buf[513] = {0};
    fgets(buf, sizeof(buf), stdin);
    int result=send(socket, buf, strlen(buf),0);
    return result;
}

int main()
{
    int listener_d;
    struct sockaddr_in name;
    listener_d=socket(PF_INET,SOCK_STREAM,0);
    name.sin_family=PF_INET;
    name.sin_port=(in_port_t)htons(30000);
    name.sin_addr.s_addr=htonl(INADDR_ANY);

    int on = 1;
    if (setsockopt(listener_d, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){
        perror("setsockopt()");
    }

    int c = bind(listener_d,(struct sockaddr *)&name,sizeof(name)); //Bind

    if(c== -1)
    {
        printf("\nCan't bind to socket\n");
    }

    if(listen(listener_d,10) == -1) // Listen
    {
        printf("\nCan't listen\n");
    }

    // Edited here
    int number_of_childeren = 0;
    int pipes[2] = {0};
    int child_pipe_write_ends[MAX_CHILDEREN] = {0};

    fd_set select_fds;
    FD_ZERO(&select_fds);

    puts("\nWait for connection\n");
    while(1)
    {
        struct sockaddr_storage client_addr;
        unsigned int address_size = sizeof(client_addr);

        // Edited here, to multiplex IO
        FD_SET(listener_d, &select_fds);
        FD_SET(STDIN_FILENO, &select_fds);
        int maxfd = listener_d + 1;

        int create_new_child = 0;
        int connect_d = -1; // moved here

        select(maxfd, &select_fds, NULL, NULL, NULL);

        if (FD_ISSET(listener_d, &select_fds)){
            connect_d = accept(listener_d, 
                                   (struct sockaddr*)&client_addr,&address_size); //Accept
            if(connect_d== -1)
                {
                    printf("\nCan't open secondary socket\n");
                    exit(EXIT_FAILURE);
                }

            create_new_child = 1;
        }

        char buf[512] ={0};
        char *endptr = NULL;
        if (FD_ISSET(STDIN_FILENO, &select_fds)){
            fgets(buf, sizeof(buf), stdin);
             long int child_num = strtol(buf, &endptr, 10);

             if (child_num > 0 && child_num <= number_of_childeren) {
                 write(child_pipe_write_ends[child_num - 1], endptr, strnlen(buf, sizeof(buf)) - (endptr - buf));
             }
             else {
                 printf("Skipping invalid input: %s\n", buf);
             }
        }

        if (create_new_child != 1)
            continue;

        number_of_childeren++; // Edited here

        int error = pipe(pipes);
        if (error != 0){
            //handle errors
            perror("pipe():");
            exit(EXIT_FAILURE);
        }

        child_pipe_write_ends[number_of_childeren - 1] = pipes[1];

        if(!fork())
        {

            error = dup2(pipes[0], STDIN_FILENO);
            if (error < 0){ // could also test != STDIN_FILENO but thats confusing
                //handle errors
                perror("dup2");
                exit(EXIT_FAILURE);
            }
            close(pipes[0]);

            close(listener_d);
            char *msg = "welcome Sweetone\n";
            if(send(connect_d,msg,strlen(msg),0))
            {
                printf("send\n");
            }
            int k=0;
            while(k<5)
            {
                say(connect_d);
                ++k;
            }
            close(connect_d);
            exit(0);
        }
            close(connect_d);
            close(pipes[0]);
    }
    close(listener_d);
    return 0;
}

代码需要重构为函数。太长了。我试图尽可能少地进行更改,因此我将重组作为练习。

答案 1 :(得分:0)

    fscanf(stdin,"%79s",s);

为什么呢?是tcp-chat吗?你有一个每个客户端的套接字,如果你想“说”某些东西,那么你必须使用客户端。这是真正的logick。

服务器通常只发送服务消息。这也是真正的logick。

但是如果你想要新终端,那么你可以尝试使用unistd.h中的exec家族。