不采用else分支的多线程代码

Multithreaded code not taking else branch

我目前正在用 C 为 Linux 编写一个原始的客户端-服务器程序。服务器是多线程的。我分别为客户端和服务器编写了以下代码:

服务器(使用gcc -o -lpthreads server server.c编译):

#include <stdio.h>                      
#include <string.h>                     
#include <arpa/inet.h>                  
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>


void lsfunc(char *path, char *buffer) {
    int pointer = sprintf(buffer, "User ID\tGroup ID\tFilename\n");
    int cx = 0; 
    DIR *mydir;
    struct dirent *myfile;
    struct stat mystat;
    mydir = opendir(path);
    myfile = readdir(mydir);
    while ((myfile = readdir(mydir)) != NULL) {    
        stat(myfile->d_name, &mystat);    
        cx = sprintf(buffer+pointer, "%lu\t%lu\t%s\n", (unsigned long int) mystat.st_uid, (unsigned long int) mystat.st_gid, myfile->d_name);
        pointer += cx;
    }
    closedir(mydir);
}

void makedir(char *path, char *prompt) {
    struct stat st = {0};
    if (stat(path, &st) == -1) {
        mkdir(path, 0700);
        sprintf(prompt, "Directory successfully created");
    }
    else {
        sprintf(prompt, "A folder with the same name already exists");
    }
}

void fgetsfunc(char *path, char *buffer) {
    FILE *fp = fopen(path, "r");
    char ch;
    int pointer = 0;
    while ((ch = getc(fp)) != EOF) {
        sprintf(buffer+pointer, "%c", ch);
        pointer += 1;
    }
    fclose(fp);
}   

void fputsfunc(char *path, char *buffer) {
    int resultCode = 0;
    FILE *fp = fopen(path, "w");
    resultCode = fputs(buffer, fp);
    if (resultCode == EOF) {
        fprintf(stderr, "Failed to write.\n");
    }
    fclose(fp);
}

int authenticate(char *username) {
    int error = 0;
    if (*username != 'u') {
        error = -1;
    }
    *username++;
    while (*username != '[=10=]') {
        if (!isdigit(*username)) {
            error = -1;
        }
        *username++;
    }
    return error;
}


void *connectionHandler(void *socket_descriptor) {
    int rcv_retcode, wrt_retcode, error_code;
    int sock = *(int*)socket_descriptor;
    char message_received[100], message_sent[10000];
    char user[5];
    char *greetings;
    char *FAIL = "FAIL";

    greetings = "Welcome user. Enter your username: ";
    write(sock, greetings, strlen(greetings));

    if((rcv_retcode = recv(sock, user, 5, 0)) == -1) {
        perror("Receive failed");
    }

    error_code = authenticate(user);

    if (error_code < 0) {
        fprintf(stderr, "Failed to authenticate user.\n");
        write(sock, FAIL, 5);
    }

    while(1) {
        if((rcv_retcode = recv(sock, message_received, 100, 0)) == -1) {
            perror("Receive failed");
        }

        fprintf(stdout, "Client says: %s\n",message_received);
        const char s[2] = " ";
        const char n[2] = "\n";
        char *token, *command, *path, *input;
        //char writeBuff[1000];
        token = strtok(message_received, s);
        command = token;

        if (strcmp(command, "exit") == 0) {
            close(sock);
            strncpy(message_sent, "Client logged out.", 10000);
        }

        else if (strcmp(command,"ls") == 0) {
            token = strtok(NULL, s);
            path = token;
            lsfunc(path, message_sent);
        }

        else if(strcmp(command,"mkdir") == 0) {
            token = strtok(NULL, s);
            path = token;
            makedir(path, message_sent);
        }

        else if(strcmp(command,"fgets") == 0) {
            token = strtok(NULL, s);
            path = token;
            fgetsfunc(path, message_sent);
        }

        else if(strcmp(command, "fputs") == 0) {
            token = strtok(NULL, s);
            path = token;
            token = strtok(NULL, n);
            input = token;
            printf("%s\n", input);
            fputsfunc(path, input);
            strncpy(message_sent, "Written to file", 10000);
        }
        if ((wrt_retcode = write(sock, message_sent, 10000)) == -1) {
            perror("Write failed");
        }
        message_received[0] = '[=10=]';
        message_sent[0] = '[=10=]';
        //sleep(1);
    }
    close(sock);
}


int main(int argc, char* argv[]) {

    int socket_retcode, bind_retcode, listen_retcode, acc_retcode, rcv_retcode, wrt_retcode;
    char message_received[100], message_sent[10000];
    struct sockaddr_in server, client;

    if ((socket_retcode = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        perror("Failed to create socket");
        return -1;
    }

    fprintf(stdout, "Socket created successfully\n");

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(4000);
    char *server_add = inet_ntoa(server.sin_addr);
    uint16_t port_num = ntohs(server.sin_port);
    printf("The address of the server is: %s\nThe port number is: %d\n", server_add,port_num);

    if ((bind_retcode = bind(socket_retcode, (struct sockaddr*)&server, sizeof(server))) == -1) {
        perror("Bind failed");
        return -1;
    }

    if((listen_retcode = listen(socket_retcode, 20)) == -1) {
        perror("Listen failed");
        return -1;
    }
    int c = sizeof(struct sockaddr_in);
    pthread_t thread_id;

    while ((acc_retcode = accept(socket_retcode, (struct sockaddr*)&client, (socklen_t*)&c))) {
        printf("Connection accepted\n");

        if (pthread_create (&thread_id, NULL, connectionHandler, (void*)&acc_retcode) < 0) {
            perror("Thread creation failed");
        }

        printf("Handler assigned\n");
    }
    if (acc_retcode < 0) {
        perror("Accept failed");
        return -1;
    }
    return 0;
}

客户:

#include <stdio.h>              
#include <string.h>             
#include <arpa/inet.h>           

#define OK       0
#define NO_INPUT 1
#define TOO_LONG 2 

static int getLine(char *prompt, char *buff, size_t sz) {
    int ch, extra;

    if (prompt != NULL) {
        printf ("%s", prompt);
        fflush (stdout);
    }
    if (fgets (buff, sz, stdin) == NULL) {
        return NO_INPUT;
    }

    if (buff[strlen(buff)-1] != '\n') {
        extra = 0;
        while (((ch = getchar()) != '\n') && (ch != EOF))
            extra = 1;
        return (extra == 1) ? TOO_LONG : OK;
    }

    buff[strlen(buff)-1] = '[=11=]';
    return OK;
}

int main(int argc, char *argv[]) {
    char IP[20], message[100], rcv_message[10000], filewriter_buffer[10000], check;
    char greeting[100], username[5], user_auth[5];

    int port_num, flag = 1;
    int client_retcode, IP_retcode, message_retcode, cnct_retcode, send_retcode, rcv_retcode;
    int logon_rcv, send_usr, user_rcv;

    struct sockaddr_in r_server;
    client_retcode = socket(AF_INET,SOCK_STREAM,0);

    if (client_retcode == -1) {
        perror("Failed to create socket.");
        return -1;
    }

    if (getLine("Enter IP: ", IP, sizeof(IP)) != OK) {
        fprintf(stderr, "No/Invalid IP provided.\n");
        return -1;
    }

    fprintf(stdout, "Enter Port Number: ");

    if (scanf("%d%c", &port_num, &check) != 2 || check != '\n') {
        printf("Non-integer port provided.\n");
        return -1;
    }

    r_server.sin_family = AF_INET;
    r_server.sin_port = htons(port_num);
    r_server.sin_addr.s_addr = inet_addr(IP);
    cnct_retcode = connect(client_retcode, (struct sockaddr *)&r_server, sizeof(r_server));

    if (cnct_retcode == -1) {
        perror("Connect failed");
        return -1;
    }

    fprintf(stdout, "Connection to server on %s:%d successful\n", IP, port_num);

    while(1) {
        logon_rcv = recv(client_retcode, greeting, 100, 0); 
        if (logon_rcv == -1) {
            perror("Receive failed");
        }
        if (getLine(greeting, username, sizeof(username)) != OK) {
            printf("No/Invalid input\n");
            return -1;
        }

        send_usr = send(client_retcode, username, 5, 0);
        if (send_usr == -1) {
            perror("Send failed");
            return -1;
        }

        user_rcv = recv(client_retcode, user_auth, 5, 0);

        if (user_rcv == -1) {
            perror("Receive failed");
            return -1;
        }


        if (strcmp(user_auth, "FAIL") == 0) {
            fprintf(stderr, "Wrong username. Exiting...\n");
            break;
        }

        else {

            if (getLine("Enter command: ", message, sizeof(message)) != OK) {
                printf("No/Invalid command provided\n");
                return -1;
            }

            if(strcmp(message, "exit") == 0) {
                send_retcode = send(client_retcode, message, 100, 0);                           
                if(send_retcode == -1) {
                    perror("Send failed");
                    return -1;
                }
                break;
            } 

            send_retcode = send(client_retcode, message, 100, 0);                           
            if(send_retcode == -1) {
                perror("Send failed");
                return -1;
            }

            rcv_retcode = recv(client_retcode, rcv_message, 10000, 0);                      

            if (rcv_retcode == -1) {
                perror("Receive failed");
                return -1;
            }
            printf("Server says: %s\n", rcv_message);
            message[0] = '[=11=]';
            rcv_message[0] = '[=11=]';                                      
        }
        close(client_retcode);
    }
    return 0;
}

从代码中可以清楚地看出,我不是一个很好的 C 程序员。执行程序时,我到了客户端提供用户名和服务器检查其有效性的地步。问题是:在那之后它停止了。如果用户名有效,则客户端的 else 分支将永远不会被采用,之后在各自的 shell 上也不会发生任何事情。如果我取出整个 if-else 并且不检查有效的用户名,它会工作得很好,但这是我实现的一个重要部分,我不能没有它。我做错了什么?

存在一些严重错误,但我认为您看到所述行为的原因是:

server.c中:

if((rcv_retcode = recv(sock, user, 5, 0)) == -1) {
    perror("Receive failed");
}

error_code = authenticate(user);

if (error_code < 0) {
    fprintf(stderr, "Failed to authenticate user.\n");
    write(sock, FAIL, 5);
}

while(1) {
    if((rcv_retcode = recv(sock, message_received, 100, 0)) == -1) {
        perror("Receive failed");
    }
    ...
}

如果用户名有效,您的服务器不会return任何响应,只是等待。如果用户名有效,您的服务器需要响应,否则您的客户端将永远等待 client.c:

    /* Ends up waiting here forever */
    user_rcv = recv(client_retcode, user_auth, 5, 0);

    if (user_rcv == -1) {
        perror("Receive failed");
        return -1;
    }


    if (strcmp(user_auth, "FAIL") == 0) {
        fprintf(stderr, "Wrong username. Exiting...\n");
        break;
    }

要解决此问题,请更改 server.c(由注释指示):

if((rcv_retcode = recv(sock, user, 5, 0)) == -1) {
    perror("Receive failed");
}

error_code = authenticate(user);

if (error_code < 0) {
    fprintf(stderr, "Failed to authenticate user.\n");
    write(sock, FAIL, 5);
}

write(sock, "butt", 5); /* Send something that isn't FAIL */

while(1) {
    if((rcv_retcode = recv(sock, message_received, 100, 0)) == -1) {
        perror("Receive failed");
    }
    ...
}