Executing child process in new terminal

痴心易碎 提交于 2019-12-24 11:41:17

问题


I want to make a simple chat application for unix. I have created one server which supports multiple clients. When ever a new client connects to the server a new process is created using fork command. Now the problem is all the child processes share the same stdin on the server, cause of this in order to send a message to 2nd clien 1st child prosess has to terminte. In order to resolve this I would like to run each child process in a new terminal. This can be achieved by writing the code for the child process code in a new file and executing it like xterm -e sh -c .(i have not tried this though).

What i really want is not to have two file just to fireup a new terminal and run rest of the code in it.

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;
}

回答1:


I think the message sending between your client and servers is a bit unusual. It is more common, in this simple "just test how it works" scenario to have the clients sending messages to the server. As an example I could mention a simple echo service, which mirrors everything a client sends, back to the client. Is this design forced by some requirements?

Critique aside, I have two separate changes that could make your current design work. They both involve changing the reading of input in the subservers.

Alternative 1: Instead of reading from stdin, create a named pipe ( see man 3 mkfifo), fex /tmp/childpipe"pid_of_subserver_here". You could create the pipe in say() and open it for reading. Then use echo (man echo) to write to the pipe echo "My message" > /tmp/childpipe"NNNN". Before exiting the child, remember to remove the pipe with unlink()

Alternative 2: Create an unnamed pipe between server and each subserver. This makes the code much more messy, but avoids creating named pipes and using echo. Example code is included below. It has insufficient error handling (like most example code) and does not handle disconnecting client properly.

Example usage: 1) start server ./a.out 2) (connect client in external window (e.g. nc localhost 30000) 3) write to client 1 by typing "1Hello client one" 4) (connect second client in third window etc) 4) Write to second client by typing "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;
}

The code needs refactoring into functions. It is too long. I tried to do the least possible amount of changes, so I left the restructuring as an exercise.




回答2:


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

Why? Is it tcp-chat? You have some socket for each client and if yoy want to "say" something then you must to use client. It's true logick.

The server usually sends a service messages only. It's true logick too.

But if you want new terminal then you can try to use a exec's family from unistd.h .



来源:https://stackoverflow.com/questions/14892182/executing-child-process-in-new-terminal

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!