Search code examples
csocketsnetwork-programmingtelnet

What is wrong with my handle_clients function


Fairly new to socket programming, so here goes my question. What is wrong? What my function does, is take input from the telnet session and then when you press 'enter' it should go break the while loop. But it doesn't for some reason. I can't figure out why either, I've tried various ways but, nothing I've tried thus far worked yet.

void handle_clients(socket,address)
    int *socket;
    const char *address;
{
    char msg[256];
    char cmd[128];
    int bytes;

    memset(msg,0,sizeof(msg));
    memset(cmd,0,sizeof(cmd));
    while(1) {
        send(*socket,"CMD >> ",7,0);
        bytes = 0;
        while((bytes = recv(*socket,cmd,sizeof(cmd),0)) > 0) {
            if(bytes < 0) {
                sprintf(msg,"Error: receiving from %s.\r\n",
                    address);
                send(*socket,msg,strlen(msg),0);
                break;
            }
            if(cmd[bytes] == 10 || cmd[bytes] == 13) {
                break;
            }
        }

        if(strcmp(cmd,"exit") == 0) {
            break;
        } else if(strcmp(cmd,"help") == 0) {
            sprintf(msg,"Commands: [exit,cmd,help]\r\n");
            send(*socket,msg,strlen(msg),0);
        } else if(strcmp(cmd,"cmd") == 0) {
            memset(cmd,0,sizeof(cmd));
            send(*socket,"Enter command: ",15,0);
            bytes = 0;
            while((bytes = recv(*socket,cmd,sizeof(cmd),0)) > 0) {
                if(bytes < 0) {
                    sprintf(msg,"Error: receiving from %s.\r\n",
                        address);
                    send(*socket,msg,strlen(msg),0);
                    break;
                }
                if(cmd[bytes] == 10 || cmd[bytes] == 13) {
                    break;
                }
            }
            system(cmd);
        } else {
            sprintf(msg,"Unknown command.\r\n");
            send(*socket,msg,strlen(msg),0);
        }
        memset(msg,0,sizeof(msg));
        memset(cmd,0,sizeof(cmd));
    }
}


Solution

  • it should go break the while loop. But it doesn't for some reason.

    In addition to the issues pointed out by my peers, I think the answer to your specific inquiry lies in the nested while loops and the missing \r\n chars from the strcmp.

    Since the while loops are nested, than break might break the nested loop while the container loop remains - so you're back in the next nested loop in your next iteration.

    For example:

    while(1) {
        send(*socket,"CMD >> ",7,0);
        bytes = 0;
        while((bytes = recv(*socket,cmd,sizeof(cmd),0))) {
            if(bytes < 0) {
                sprintf(msg,"Error: receiving from %s.\r\n",
                    address);
                send(*socket,msg,strlen(msg),0);
                break; /* <= WHICH LOOP ARE WE BREAKING? */
            }
            if(cmd[bytes] == 10 || cmd[bytes] == 13) {
                break; /* <= WHICH LOOP ARE WE BREAKING? */
            }
        }
        // ...
     }
    

    This is one of those cases where goto might be your friend...

    ...although I do think there are better and more elegant solutions and changes that might be required to your code before you go there.

    Also, What's the point of re-reading the data, if you're overwriting the existing buffer?

    I find it almost (in this case) more convenient and likely to ignore the possible case of incomplete TCP/IP packets/commands.

    In this specific case, it seems okay to assume that if the command doesn't end with a new line, something is wrong and we can disconnect. Such short commands should pass in a single TCP/IP packet. i.e.:

    while(1) {
        send(*socket,"CMD >> ",7,0);
        bytes = 0;
        bytes = recv(*socket,cmd,sizeof(cmd),0);
        if(!bytes) { // closed by peer
           close(*socket);
           return;
        }
        if(bytes < 0 || cmd[bytes-1] == 10 || cmd[bytes-1] == 13) {
           if(errno == EINTR)
             continue;
           sprintf(msg,"Error: receiving from %s.\r\n",
                       address);
           close(*socket);
           return;
        }
        if(strcmp(cmd,"exit\r\n") == 0) {
            break;
        } else if(strcmp(cmd,"help\r\n") == 0) {}
        // ...
     }
    

    This code obviously ignores the possibility of multiple commands being read in a single recv (i.e., the buffer being "help\r\ncmd\r\nX\r\nexit\r\n")... but you'll solve that at some point.