Paul M.
Paul M.

Reputation: 51

Redirecting stdout to socket in client-server situation

I'm new to this forum, so I'm sorry if my question is not correctly asked. I'll try to be as clear as possible.

I'm trying to code two programs (client.c and server.c, using TCP sockets) in Linux, with the following behavior:

So far, I have this:

server.c:

/*After creating socket with socket(), binding to address and port, 
putting in listening mode and accepting connection*/
dup2(sock_client,1);    //redirect stdout
while(1){
    recv(sock_client,buf,MAX_MSG_LENGTH,0);
    /*If the message recieved was END_STRING, exit this loop*/
    if (strncmp(buf, END_STRING, MAX_MSG_LENGTH) == 0)
        break;
    system(buf);
}

client.c:

/*After creating socket and connecting*/
while(fgets(buf,MAX_MSG_LENGTH,stdin)){
    send(sock,buf,MAX_MSG_LENGTH,0);
    if (!strncmp(buf,END_STRING,MAX_MSG_LENGTH)){
            /*If the message recieved was END_STRING, exit this loop*/
            break;
    }
    read(sock,buf,MAX_MSG_LENGTH);  //read stdout from program 
    printf("%s\n",buf);
}

My problem is that, if a command has a long output, there's some "garbage" left from it when showing the output of the next commands, so I was wondering if there was a way to flush the socket (apparently not, based on my google research), or maybe to accomplish the expected server-client behavior in some other way.

Thank you!

EDIT:

Ok, I've finished the client. Here's the code:

client.c:

/* After creating socket and connecting*/
while(fgets(buf,MAX_MSG_LENGTH,stdin)){
    send(sock,buf,MAX_MSG_LENGTH,0);
    if (!strncmp(buf,END_STRING,MAX_MSG_LENGTH)){
        /*If the message recieved was END_STRING, exit this loop*/
        break;
    }
    while(1){
        read_size = read(sock,buf,MAX_MSG_LENGTH);
        /*Print read_size characters from buf*/
        printf("%.*s\n", read_size, buf);
        if (read_size < MAX_MSG_LENGTH){
            /*Keep on reading until read_size < MAX_MSG_LENGTH, then exit this loop*/
            break;
        }
    }
    /*Clear buffer, just in case*/
    for (i = 0; i < MAX_MSG_LENGTH; i++){
        buf[i] = 0;
    }

Just as a comment, this program will not work properly if the command sent to the server doesn't have any standard output (for example, mkdir new_directory), since, in this case, read() will leave the client permanently blocked, causing the server to never recieve the next command to be run or the END_STRING message to leave the program from the client. You could probably fix this by using a non-blocking socket and using select() to read from socket, just like synther suggested. Additionally, in the server, after the system(buf); line, you should add fflush(0), which will flush all the buffers (including the socket, which could be useful if the command send by the client has a really short output).

Thanks a lot!

Upvotes: 4

Views: 4163

Answers (2)

Paul M.
Paul M.

Reputation: 51

Thank you for your answers!

I tried adding this to my client.c code:

/*After creating socket and connecting*/
while(fgets(buf,MAX_MSG_LENGTH,stdin)){
    /*Send command to server*/
    send(sock,buf,MAX_MSG_LENGTH,0);
    if (!strncmp(buf,END_STRING,MAX_MSG_LENGTH)){
        /*If the message recieved was END_STRING, exit this loop*/
        break;
    }
    while(1){
        read_size = read(sock,buf,MAX_MSG_LENGTH);  //read stdout from program 
        printf("%.*s\n", read_size, buf);
        if (read_size < MAX_MSG_LENGTH){
            /*Exit this loop when reading less that MAX_MSG_LENGTH*/    
            break;
        }
    }
    /*Clear the 'buf' array. I don't know if this is really necessary*/
    for (i = 0; i < MAX_MSG_LENGTH; i++){
        buf[i] = 0;
    }
}

And now, after every command, the client only prints the output of the last command sent. I will test it more thoroughly and edit my original post if this solution is correct, so thanks a lot!

Upvotes: 1

synther
synther

Reputation: 336

Perhaps, you get "garbage" in client when your command's output exceeds MAX_MSG_LENGTH. read(sock,buf,MAX_MSG_LENGTH); reads just MAX_MSG_LENGTH bytes from socket, remaining chars in socket are read the next time when you except it from the next command.

You can fix it in multiple ways in client.

  1. read returns the actual number of bytes read. You can compare it with MAX_MSG_LENGTH and decide to read one more time or not. But if your actual data is exactly MAX_MSG_LENGTH bytes then you decide to read again and read blocks waiting data that is not available at the moment (and stdin blocks too, user can't send new command).

  2. Use non-blocking socket to fix issue in 1. read will return immediately when no available data.

  3. Add end-of-command marker to your server's output and client will know when to stop reading and switch to stdin reading.

  4. Use select() mechanism to read from socket and user input "simultaneously". It allows to read from multiple file descriptors (socket and stdio) when data available on any of them.

Additionally, you use the same buffer for user commands and server responses. Typically, user commands are shorter then server output and buf could contain parts of last server outputs. Then you send this mix of user command and last server output to server.

And, as stated above, read returns the actual number of bytes read. You should print the exactly received number of bytes from buf, not all the data.

    int ret = read(sock,buf,MAX_MSG_LENGTH);  //read stdout from program 
    printf("%.*s\n", ret, buf);

Upvotes: 0

Related Questions