LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Why does my server cannot respond to multiple clients simultaneously even after using fork function? (https://www.linuxquestions.org/questions/programming-9/why-does-my-server-cannot-respond-to-multiple-clients-simultaneously-even-after-using-fork-function-4175668155/)

intstudent123 01-21-2020 03:02 PM

Why does my server cannot respond to multiple clients simultaneously even after using fork function?
 
Hi, I'm a newbie here. I'm trying to create an SMTP server that can respond to multiple clients simultaneously. I use the fork function in socket programming. Below is the server program (not including the variables).

smtp.c

Code:

  ...
        system("clear");
        // create socket
        if((sock = socket(AF_INET, SOCK_STREAM, 0)) == ERR){
                perror("socket: ");
                exit(-1);
        }

        server.sin_family = AF_INET;
        server.sin_port = htons(SMTP);
        server.sin_addr.s_addr = INADDR_ANY;

        // bind
        len = sizeof(struct sockaddr_in);
        if(bind(sock, (struct sockaddr *)&server, sizeof(server)) == ERR){
                perror("bind");
                exit(-1);
        }

        // listen
        if((listen(sock, 10)) == ERR){
                perror("listen");
                exit(-1);
        }

        while(1){
                memset(buff, 0, sizeof(buff));
                memset(msg, 0, sizeof(msg));
                memset(to, 0, sizeof(to));
                memset(Cc, 0, sizeof(Cc));

                printf("(SMTP) Waiting for connection\n");
                if((cli = accept(sock, (struct sockaddr *)&client, &len)) == ERR){
                        perror("accept");
                        exit(-1);
                }

                // create child process
                child_pid = fork();

                if (child_pid < 0) {       
                        perror("fork");
                        exit(-1);
                }
                // child
                else if (child_pid == 0) {
                        close(sock);                // close child copy
                        // receive from client
                        recv(cli, to, sizeof(msg), 0);
                        printf("Connection request to %s\n",to);

                        for(k=0;;k++){
                                sprintf(file_dir,"%s/mail%d.txt", to, k+1);
                                if ((fp = fopen(file_dir, "r")) == NULL) break;
                        }
                                    fp = fopen(file_dir, "w");
                                while(strcmp(msg,".") != 0){
                                        recv(cli, msg, sizeof(msg),0);
                                        if(strcmp(msg,".") == 0){
                                                strcpy(buff, "1");
                                        }
                                        else{
                                                strcpy(buff, "0");
                                        }
                                fprintf(fp, "%s\n", msg);
                                send(cli, buff, sizeof(buff), 0);
                                }

                        fclose(fp);
                        // Cc
                        // init buff and msg
                        memset(buff, 0, sizeof(buff));
                        memset(msg, 0, sizeof(msg));
                        // process
                        recv(cli, Cc, sizeof(msg), 0);
                        printf("Connection request to %s\n\n",Cc);
       
                        for(k=0;;k++){
                                sprintf(file_dir,"%s/mail%d.txt", Cc, k+1);
                                if ((fp = fopen(file_dir, "r")) == NULL) break;
                        }
                                fp = fopen(file_dir, "w");
                                while(strcmp(msg,".") != 0){
                                        recv(cli, msg, sizeof(msg),0);
                                        if(strcmp(msg,".") == 0){
                                                strcpy(buff, "1");
                                        }
                                        else{
                                                strcpy(buff, "0");
                                        }
                                        fprintf(fp, "%s\n", msg);
                                        send(cli, buff, sizeof(buff), 0);
                                }

                        fclose(fp);
                        close(cli);
                        exit(0);                // child exits here
                }
                // parent
                else{
                        close(cli);                // close parent copy
                        waitpid(child_pid, NULL, 0);        // waits for child to exit
                }
        }
        close(sock);
        return 0;
        ...

To test it, I open two terminals, for example, A and B. At A's terminal, I write emails to C and B. At the same time, I write emails at B's terminal to A and C. After entering '.' at A's terminal to send the emails, I tried to send B's emails without exiting from A's terminal.

The result turns out like this:

Quote:

(SMTP)Waiting for connection
Connection request to C
Connection request to B

(SMTP)Waiting for connection
Connection request to
(SMTP)Waiting for connection
But I want the result to be like this:

Quote:

(SMTP) Waiting for connection
Connection request to C
Connection request to B

(SMTP) Waiting for connection
Connection request to A
Connection request to C

(SMTP) Waiting for connection
Are there any mistakes in the flow of the coding? It would be great if there's anyone that can help so that I can improve my understanding of socket and fork. Thank you in advance.

astrogeek 01-21-2020 04:21 PM

Hi, and welcome to LQ!

Although the rules here ask that you not post homework or school assignments verbatim, members are happy to help if you are having difficulty with specific problems.

Posting whole code and asking others to debug it for you is not a good way to get help. Everyone volunteers their time, so working up a simplest example which reproduces the problem for others is always appreciated! And it is the best learning exercise for you as well - very often in the process of explaining the problem to others in simplest terms you will understand it well enough to fix it yourself!

What have you done to troubleshoot the problem? Are any error messages produced when the connection fails, and if so, what are they?

Please review the Site FAQ for guidance in posting your questions and general forum usage. Especially, read the link in that page, How To Ask Questions The Smart Way. The more effort you put into understanding your problem and framing your questions, the better others can help!

Geist 01-21-2020 07:59 PM

Why not wrap some of that code into functions so you don't have one huge main one?
The benefit of it that if a smaller function works as intended, it's less noise, less to think about vs it being expanded.

This also helps with debugging, especially variable scope problems since it's easy to overlook one of the many variables required for a huge spaghetti function.
That is because inside the then encapsulated function, the variable it needs must be there and set correctly, which is easier to manage if the scope of the variable is, well, constrained to only the function.

So if you had a variable like "sock", and it were worked with in 10 places in the code, then it would be easy for that to mess up in one huge spaghetti function, vs 1 or 2 times per small function, which you feed the sock variable as an argument, and have it returned.

Code:

int rock(int sock)
{
  return sock + 5;
}

int rockem_sockem(int sock){
return rock(sock);
}

int mockem_mockem(int sock){
return sock + 10;
}

int trocken_bocken(int sock){
return sock + 20;
}

int main(){
int sock = 0;
sock = rockem_sockem(sock);
sock = mockem_mockem(sock);
sock = trocken_bocken(sock);
//etc
return 0;
}

Now you have several encased places where working with sock might fail instead of sock being strewn all over the place like actual socks, having 10 different loops, functions and whatnots all meddle with it in one big place.

GazL 01-22-2020 06:50 AM

Quote:

Originally Posted by intstudent123 (Post 6081251)
Are there any mistakes in the flow of the coding? It would be great if there's anyone that can help so that I can improve my understanding of socket and fork. Thank you in advance.

Unless I'm reading it wrong, you have a waitpid(child_pid,...) call within your accept() main loop. That's going to serialise client processing. Your server won't accept() a new client until the last one has been processed fully, effectively negating the purpose of forking the client processing to the background.

You need to remove that waitpid from the main loop, and handle child reaping with a SIGCHLD signal handler.


Beej's excellent guide may be of interest to you: http://beej.us/guide/bgnet/html/

intstudent123 01-22-2020 03:26 PM

Thank you for the advice! I tried to use SIGCHLD, but it did not work. Maybe there's something wrong at the client program. I'll just do my best.

NevemTeve 01-22-2020 07:07 PM

Then periodically try some non-blocking version of wait:
Code:

pid_t exitedchild= waitpid(-1, &wstatus, WNOHANG);

intstudent123 01-23-2020 02:04 AM

I understand that the flow of the socket programming and fork function is correct, but where should I put the
Code:

pid_t exitedchild= waitpid(-1, &wstatus, WNOHANG);
? Should I put in the main loop during declaration of the variables?

NevemTeve 01-23-2020 02:12 AM

An option is calling it right after (or before) 'accept'.


All times are GMT -5. The time now is 11:58 AM.