2015-01-24 1 views
1

В настоящее время я пишу примитивную клиент-серверную программу в 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 != '\0') { 
     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] = '\0'; 
     message_sent[0] = '\0'; 
     //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; 
} 

Client:

#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] = '\0'; 
    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] = '\0'; 
      rcv_message[0] = '\0';          
     } 
     close(client_retcode); 
    } 
    return 0; 
} 

Как может быть ясно, из кода я не очень хороший программист на C. При выполнении программы я добираюсь до того момента, когда клиент предоставляет имя пользователя, а сервер проверяет его достоверность. Проблема в том, что после этого она останавливается. Если имя пользователя действительно, ветвь else на стороне клиента никогда не берется, и после этого ничего не происходит в соответствующих оболочках. Он работает очень хорошо, если я вынимаю все if-else и не проверяю правильные имена пользователей, но это важная часть моей реализации, и я не могу обойтись без этого. Что я делаю не так?

+0

@ JS1 Исправлено это, но до сих пор не решает проблему. –

+0

_ «Если имя пользователя является допустимым, ветвь' else' на стороне клиента никогда не берется »_ Если' '' было достигнуто, и имя пользователя было бы действительным, будет выведена ветка 'else'. Таким образом, либо ваш компилятор не может получить простую форму if-else, либо 'if' никогда не будет достигнута. Я оставлю вас, чтобы выяснить, какая из этих возможностей более вероятна. Подсказка: много очень хороших программистов на C тоже используют ваш компилятор и заметили бы, если бы он не поддерживал if-else. –

ответ

2

Там, ряд критических ошибок, но я думаю, что причина, почему вы видите Описанное поведение является:

В 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"); 
    } 
    ... 
} 

Если имя пользователя является допустимым, сервер не вернуть любой ответ и просто ждать. Ваш сервер должен ответить, если имя пользователя является действительным, или же ваш клиент сидит здесь ждет навсегда в 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"); 
    } 
    ... 
} 
+0

Спасибо, что исправляет проблему. Однако сейчас я не могу обрабатывать несколько соединений. Если какой-либо клиент выдает команду «exit», код на стороне сервера автоматически умирает и дает «Ошибка сегментации». Я знаю, что меня беспокоит, но я относительно новичок в программировании. Я был бы признателен, если бы вы могли указать мне в правильном направлении, как я должен работать с несколькими соединениями. –

+0

Хорошо, что вы готовы учиться. Я бы рекомендовал вам создать новые вопросы, чтобы получить самые быстрые ответы для проектирования многопоточного сервера. Если вы это сделаете, я обязательно посмотрю на это позже. – mauzel

+0

Что касается SegFault в 'exit', это потому, что вы не убиваете поток' connectionHandler' после вызова 'close (sock)' в блоке обработки 'exit'. Он просто продолжается и вызывает 'write()' в закрытом сокете, затем цикл и вызывает 'recv' снова, даже если сокет закрыт. И затем вы пытаетесь вызвать 'strtok' на' message_received [0] = '\ 0'; ', так что это seg faults. – mauzel

Смежные вопросы