Caro
Caro

Reputation: 11

How to execute a command only after the previous one has finished in a Server/Client program using sockets

I'm making a client/server program in C using sockets. The server must be able to handle many clients.

After the server has been properly set up, the client connects to it, and sends either "Ready" or "Kill". The server receives the message, and reply back "Go" or "Destroy".

If the reply received by the client is "Go", then it executes a command passed in parameter. That command is executed like this : system(command)

If the reply received by the client is "Destroy", then the Client is closed, and the server is destroyed.

The Server must be able to handle many clients, BUT it must allow for only one client to execute a command at a time. Also, if a Client connects to the Server and sends "Kill", then all the Clients in the queue must be closed, and the Server must be Destroyed right away.

So, to achieve that, I'm using select()

So I'm having issues with this specification : it must allow for only one client to execute a command at a time.

Basically, the command is executed on the client side. And I don't want two Clients to be able to execute a command at the same time.

Here's an example (this is not code) :

Client1 : system(echosleep hello1 goodbye1);

Client2 : system(echosleep hello2 goodbye2);

So the way my program works right now, it prints :

hello1
hello2
goodbye1
goodbye2

But what I want is :

hello1
goodbye1
hello2
goodbye2

--> And I don't understand how the server could know if the command is finished or not, since it's executed on the Client side.

So here's my Server code structure (I am not posting the full code here, because my issue is not about writing the code itself, but more about the structure, and where exactly I should send "Go" to the Client... so I have left out irrelevant code) :

//initialize all client_socket[] to 0
//Create master_socket
//bind() master_socket
//listen() master_socket

//accept the incoming connection 
addrlen = sizeof(address);

while(TRUE) {
    //clear the socket set
    FD_ZERO(&readfds);

    //add master socket to set
    FD_SET(master_socket, &readfds);
    max_sd = master_socket;

    //add child sockets to set
    for ( i = 0 ; i < max_clients ; i++) {
        //socket descriptor
        sd = client_socket[i];

        //if valid socket descriptor then add to read list
        if(sd > 0) {
            FD_SET( sd , &readfds);
        }

        //highest file descriptor number, need it for the select function
        if(sd > max_sd) {
            max_sd = sd;
        }
    }

    //wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely
    activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL);

    if (activity < 0) {
        printf("select()");
    }

    //If something happened on the master socket , then its an incoming connection
    if (FD_ISSET(master_socket, &readfds)) {
        if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) {
            perror("accept()");
            return -1;
        }

        //Check message received from the Client
        //if message == Kill then destroy everything   (this is working)

        //if message == Ready, then send Go
        //When the Client receives Go, it will proceed with the execution of his command

        //add Client to the queue
        for (i = 0; i < max_clients; i++) {
            //if position is empty
            if( client_socket[i] == 0 ) {
                client_socket[i] = new_socket;              
                break;
            }
        }
    }
}

Upvotes: 1

Views: 112

Answers (1)

Jeremy Friesner
Jeremy Friesner

Reputation: 73071

I don't understand how the server could know if the command is finished or not

The simple answer is, it can't, for exactly the reason you mentioned. The server has no way of knowing what is going on inside the client unless the client tells it.

Therefore, one solution to the problem would be to have the client send another message (e.g. "Done") back to the server after the client has finished executing the command. Upon receiving the "Done" message, the server would know that the client was done executing and could then authorize another command on another client, if it wants to.

(btw having a client receive a string from the network and pass that string to system() is a big potential security hole -- an 'evil' server could pass a string like "rm -rf *" and cause your client to delete all its files! So be careful when running your client software outside of an academic context)

Upvotes: 1

Related Questions