2016-09-29 48 views
0

所以我有一個非常簡單的多服務器客戶端應用程序(Linux)。我試圖做的初學者是連接到服務器接收來自它的消息,並寫入消息到服務器,然後是回聲。客戶端在新形成的連接上沒有收到消息

問題是,在連接而不是來自服務器的問候消息時,客戶端已準備好將消息鍵入終端 - 沒有問候消息。在輸入任何我想要的信息之後,我會回覆問候信息!我在這裏做錯了什麼?下面的代碼。

server.c

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/socket.h> 
#include <sys/un.h> 
#include <unistd.h> 
#include <pthread.h> 

#include <netdb.h> 
#include <netinet/in.h> 

int server(int client_socket); 
void* handle_connection(void* inc); 

int main(int argc, char* argv[]) 
{ 
    int socket_fd; 
    int client_socket_fd; 
    int port_number; 
    int client_length; 
    int * new_sock; 
    struct sockaddr_in serv_addr; 
    struct sockaddr_in cli_addr; 
    char buffer[256]; 

    socket_fd = socket(AF_INET, SOCK_STREAM, 0); 

    if(socket_fd < 0) { 
     perror("Error opening socket!"); 
     exit(1); 
    } 

    /* Initialize socket structure */ 
    port_number = 1234; 
    bzero((char *) &serv_addr, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_addr.s_addr= INADDR_ANY; 
    serv_addr.sin_port = htons(port_number); 

    /* Binding */ 
    if(bind(socket_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 
     perror("Error on binding!"); 
     exit(1); 
    } 

    listen(socket_fd, 10); 
    client_length = sizeof(cli_addr); 

    while(client_socket_fd = accept(socket_fd, (struct sockaddr*) &cli_addr, &client_length)) { 
    pthread_t thread_id; 
    new_sock = malloc(sizeof(client_socket_fd)); 
    *new_sock = client_socket_fd; 

    if(pthread_create(&thread_id, NULL, handle_connection, (void*) new_sock)) { 
     perror("could not create thread"); 
     exit(1); 
    } 
    pthread_join(thread_id, NULL); 
} 

if(client_socket_fd < 0) { 
    perror("Error on accept client"); 
    exit(1); 
} 

    return 0; 
} 

void* handle_connection(void* inc) 
{ 
    int socket_fd = *(int *) inc; 
    int message_size; 
    char *message; 
    char buffer[256]; 

    message = "You have been connected\n"; 
    write(socket_fd, message, strlen(message)); 
    message = "I will repeat what you type\n"; 
    write(socket_fd, message, strlen(message)); 

    while((message_size = recv(socket_fd, buffer, sizeof(buffer)/sizeof(buffer[0]), 0)) > 0) { 
    write(socket_fd, buffer, strlen(buffer)); 
    } 

    if(message_size == 0) { 
    puts("Client disconnected"); 
    fflush(stdout); 
    } 
    else if(message_size == -1) { 
    perror("Reading back from client failed"); 
    } 

    free(inc); 

    return 0; 

} 

client.c

#include <stdio.h> 
#include <stdlib.h> 
#include <netdb.h> 
#include <netinet/in.h> 
#include <string.h> 
#include <pthread.h> 

int main(int argc, char* argv[]) 
{ 
    int socket_fd, port_number; 
    struct sockaddr_in serv_addr; 
    struct hostent *server; 
    char buffer[256]; 

    if(argc<2) { 
    fprintf(stderr,"Incorrect arguments input\n"); 
    exit(0); 
    } 

    //port_number = atoi(argv[2]); 
    port_number = 1234; 

    server = gethostbyname(argv[1]); 

    socket_fd = socket(AF_INET, SOCK_STREAM, 0); 
    if(socket_fd < 0) { 
    perror("Error opening socket"); 
    exit(1); 
    } 

    bzero((char*) &serv_addr, sizeof(serv_addr)); 
    serv_addr.sin_family = AF_INET; 
    serv_addr.sin_port = htons(port_number); 
    bcopy((char*) server->h_addr,(char *) &serv_addr.sin_addr.s_addr,sizeof(server->h_length)); 

    if(connect(socket_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { 
     perror("Error connecting"); 
     exit(1); 
    } 
    while(1) { 
    bzero(buffer, sizeof(buffer)/sizeof(buffer[0])); 
    printf("Enter the message: "); 
    bzero(buffer, sizeof(buffer)/sizeof(buffer[0])); 
    fgets(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, stdin); 

    if(send(socket_fd, buffer, strlen(buffer), 0) < 0) { 
     perror("Error sending message"); 
     exit(1); 
    } 

    bzero(buffer, sizeof(buffer)/sizeof(buffer[0])); 
    if(recv(socket_fd, buffer, sizeof(buffer), 0) < 0) { 
     perror("Error reading back from server"); 
     exit(1); 
    } 

    printf("Server reply: "); 
    printf("%s",buffer); 
    } 
    close(socket_fd); 
    return 0; 
} 

下面是該程序的事與願違的結果: unwatned result

服務器回覆應在例如:HEJ。

+0

客戶端連接後,也有2個消息從服務器。在開始輸入循環之前,您必須閱讀這些內容。 –

+0

@JohnnyMopp我該怎麼做?我不知道從服務器得到多少條消息。這裏有2個,但最近可能會有所不同。 – Lisek

+1

printf打印到標準輸出。標準輸出是行緩衝。後綴全部你想用printf用新行打印。它將被立即打印。 – alk

回答

2

問題是,在連接而不是來自服務器的問候消息時,客戶機已準備好將消息鍵入終端 - 沒有問候消息。

這是因爲您在讀取recv()問候語並向用戶顯示問候語之前正在閱讀用戶輸入內容。

輸入任何我想要的信息後,我會得到回覆與問候消息!

這是因爲你獲取/顯示問候後調用 fgets()

代碼中有許多錯誤。我會指出最大的。

  1. 你打電話pthread_join()之後pthread_create()。這是 阻止新的連接(即進一步accept() s)。這應該在循環之外完成 。在這種情況下,你需要一些存儲線程ID。對於 例如:
const int thread_pool_step = 10; 
int thread_pool_size = thread_pool_step; 
pthread_t *thread_ids = malloc(thread_pool_size * sizeof(pthread_t)); 
memset(thread_ids, 0, thread_pool_size * sizeof(pthread_t)); 
int thread_index = -1; 

while (!do_shutdown) 
    { 
    /* ... */ 
    if (++thread_index >= thread_pool_size) 
     { 
     thread_pool_size += thread_pool_step; 
     thread_ids = realloc(thread_ids, thread_pool_size * sizeof(pthread_t)); 
     if (thread_ids == NULL) 
      { 
      fprintf(stderr, "failed to realloc thread pool: %s\n", 
       strerror(errno)); 
      abort(); 
      } 
     } 

    pthread_create(&thread_ids[thread_index++], NULL, 
        handle_connection, (void*) new_sock); 
    } 

while (--thread_index >= 0) 
    { 
    if (thread_ids[thread_index]) 
     pthread_join(thread_ids[thread_index], NULL); 
    } 

if (thread_ids != NULL) 
    free(thread_ids); 
  • 正如註釋到問題中提到,則需要某種 協議。例如,您可能會讀取問候語,直到服務器發送從「READY」字符串開始的 行。客戶可能還記得,服務器狀態,然後用聊天邏輯進行:
  • enum { 
        INIT_STATE, 
        READY_STATE 
    } serv_state = INIT_STATE; 
    
    #define check_serv_state(str, st) \ 
        (strncmp(str, #st, sizeof(#st) - 1) == 0) 
    
    for (;;) 
    { 
        bzero(buffer, sizeof(buffer)/sizeof(buffer[0])); 
        rc = recv(socket_fd, buffer, sizeof(buffer), 0); 
        /* ... */ 
    
        if (serv_state == INIT_STATE) 
        { 
         char *part = strtok(buffer, "\n"); 
         do 
         { 
          if (check_serv_state(part, READY)) 
          { 
           printf("The server is ready for the chat\n"); 
           serv_state = READY_STATE; 
          } 
          else 
          printf("Server: [[[%s]]]\n", part); 
         } 
         while ((part = strtok(NULL, "\n"))); 
    
         if (serv_state != READY_STATE) 
         continue; 
        } 
        else 
        printf("Server reply: %s", buffer); 
    
        /* ... */ 
        printf("Enter the message: "); 
        /* ... */ 
    } 
    
  • 我也建議實施一些信號處理爲正常關機。至少,對於服務器:
  • volatile sig_atomic_t do_shutdown = 0; /* global */ 
    
    static void 
    sigterm_handler(int sig) 
    { 
        printf("Caught signal %d. Leaving...\n", sig); 
        do_shutdown = 1; 
    } 
    
    /* ... */ 
    
    struct sigaction a; 
    a.sa_handler = sigterm_handler; 
    a.sa_flags = 0; 
    sigemptyset(&a.sa_mask); 
    sigaction(SIGINT, &a, NULL); 
    sigaction(SIGQUIT, &a, NULL); 
    sigaction(SIGTERM, &a, NULL); 
    
    /* Check do_shutdown in the loops */ 
    while (!do_shutdown) { /* ...recv(), accept(), etc. */ } 
    

    筆記,從異步上下文訪問全局變量(信號處理程序,處理程序線程[尤其])是通常不安全無需使用mutual exclusion

    以下是完整的代碼與所施加的修正:

    server.c

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    #include <sys/socket.h> 
    #include <sys/un.h> 
    #include <unistd.h> 
    #include <pthread.h> 
    #include <errno.h> 
    #include <signal.h> 
    #include <netdb.h> 
    #include <netinet/in.h> 
    
    volatile sig_atomic_t do_shutdown = 0; 
    
    static void 
    sigterm_handler(int sig) 
    { 
        printf("Caught signal %d. Leaving...\n", sig); 
        do_shutdown = 1; 
    } 
    
    
    static void 
    send_message(int socket_fd, const char *message) 
    { 
        int rc; 
        int message_size = strlen(message); 
    
        do 
        { 
         rc = send(socket_fd, message, message_size, 0); 
         if (rc == -1) 
         { 
          fprintf(stderr, "send() failed: %s\n", strerror(errno)); 
          abort(); 
         } 
        } 
        while (rc < message_size && !do_shutdown); 
    } 
    
    
    static void * 
    handle_connection(void *inc) 
    { 
        int socket_fd = *(int *)inc; 
        int message_size; 
        char *message; 
        char buffer[256]; 
    
        message = "You have been connected\n"; 
        send_message(socket_fd, message); 
    
        message = "I will repeat what you type\n"; 
        send_message(socket_fd, message); 
    
        message = "READY\n"; 
        send_message(socket_fd, message); 
    
        while (!do_shutdown) 
        { 
         memset(buffer, 0, sizeof(buffer)); 
         message_size = recv(socket_fd, buffer, sizeof(buffer)/sizeof(buffer[0]), 0); 
         if (message_size > 0) 
         send_message(socket_fd, buffer); 
         else if (message_size == -1) 
         { 
          fprintf(stderr, "recv() failed: %s\n", strerror(errno)); 
          break; 
         } 
         else if (message_size == 0) 
         { 
          puts("Client disconnected"); 
          fflush(stdout); 
          break; 
         } 
        } 
    
        free(inc); 
    
        return 0; 
    } 
    
    
    int 
    main(int argc, char* argv[]) 
    { 
        int socket_fd; 
        int client_socket_fd; 
        int port_number; 
        socklen_t client_length; 
        int *new_sock; 
        struct sockaddr_in serv_addr; 
        struct sockaddr_in cli_addr; 
    
        socket_fd = socket(AF_INET, SOCK_STREAM, 0); 
        if (socket_fd < 0) 
        { 
         perror("Error opening socket!"); 
         exit(1); 
        } 
    
        /* Initialize socket structure */ 
        port_number = 12345; 
        bzero((char *)&serv_addr, sizeof(serv_addr)); 
        serv_addr.sin_family = AF_INET; 
        serv_addr.sin_addr.s_addr= INADDR_ANY; 
        serv_addr.sin_port = htons(port_number); 
    
        /* Binding */ 
        if (bind(socket_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
        { 
         perror("Error on binding!"); 
         exit(1); 
        } 
    
        const int thread_pool_step = 10; 
        listen(socket_fd, thread_pool_step); 
        client_length = sizeof(cli_addr); 
    
        int thread_pool_size = thread_pool_step; 
        pthread_t *thread_ids = malloc(thread_pool_size * sizeof(pthread_t)); 
        if (thread_ids == NULL) 
        { 
         perror("Failed to allocate memory for thread ids"); 
         abort(); 
        } 
        memset(thread_ids, 0, thread_pool_size * sizeof(pthread_t)); 
        int thread_index = -1; 
    
        struct sigaction a; 
        a.sa_handler = sigterm_handler; 
        a.sa_flags = 0; 
        sigemptyset(&a.sa_mask); 
        sigaction(SIGINT, &a, NULL); 
        sigaction(SIGQUIT, &a, NULL); 
        sigaction(SIGTERM, &a, NULL); 
    
        while (!do_shutdown) 
        { 
         client_socket_fd = accept(socket_fd, (struct sockaddr *)(&cli_addr), &client_length); 
         if (client_socket_fd == -1) 
         { 
          fprintf(stderr, "accept() failed: %s\n", strerror(errno)); 
          break; 
         } 
    
         new_sock = malloc(sizeof(client_socket_fd)); 
         *new_sock = client_socket_fd; 
    
         if (++thread_index >= thread_pool_size) 
         { 
          thread_pool_size += thread_pool_step; 
          thread_ids = realloc(thread_ids, thread_pool_size * sizeof(pthread_t)); 
          if (thread_ids == NULL) 
          { 
           fprintf(stderr, "failed to realloc thread pool: %s\n", strerror(errno)); 
           abort(); 
          } 
         } 
    
         if (pthread_create(&thread_ids[thread_index++], NULL, 
             handle_connection, (void*) new_sock)) 
         { 
          fprintf(stderr, "pthread_create() failed: %s\n", strerror(errno)); 
          abort(); 
         } 
        } 
    
        puts("Waiting for threads to finish"); 
        while (--thread_index >= 0) 
        { 
         if (thread_ids[thread_index]) 
         pthread_join(thread_ids[thread_index], NULL); 
        } 
    
        if (thread_ids != NULL) 
        free(thread_ids); 
    
        return 0; 
    } 
    

    client.c

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <netdb.h> 
    #include <netinet/in.h> 
    #include <string.h> 
    #include <pthread.h> 
    #include <unistd.h> 
    
    int main(int argc, char* argv[]) 
    { 
        int socket_fd, port_number; 
        struct sockaddr_in serv_addr; 
        struct hostent *server; 
        char buffer[256]; 
        int rc; 
    
        if (argc<2) 
        { 
         fprintf(stderr,"Incorrect arguments input\n"); 
         exit(0); 
        } 
    
        port_number = 12345; 
    
        server = gethostbyname(argv[1]); 
    
        socket_fd = socket(AF_INET, SOCK_STREAM, 0); 
        if (socket_fd < 0) 
        { 
         perror("Error opening socket"); 
         exit(1); 
        } 
    
        bzero((char*) &serv_addr, sizeof(serv_addr)); 
        serv_addr.sin_family = AF_INET; 
        serv_addr.sin_port = htons(port_number); 
        bcopy((char*) server->h_addr,(char *) 
         &serv_addr.sin_addr.s_addr,sizeof(server->h_length)); 
    
        if (connect(socket_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 
        { 
         perror("Error connecting"); 
         exit(1); 
        } 
    
        enum 
        { 
         INIT_STATE, 
         READY_STATE 
        } serv_state = INIT_STATE; 
    
    #define check_serv_state(str, st) \ 
        (strncmp(str, #st, sizeof(#st) - 1) == 0) 
    
        for (;;) 
        { 
         bzero(buffer, sizeof(buffer)/sizeof(buffer[0])); 
         rc = recv(socket_fd, buffer, sizeof(buffer), 0); 
         if (rc < 0) 
         { 
          perror("Error reading back from server"); 
          exit(1); 
         } 
         else if (rc == 0) 
         { 
          printf("The server has been disconnected. Quitting.\n"); 
          exit(0); 
         } 
    
         if (serv_state == INIT_STATE) 
         { 
          char *part = strtok(buffer, "\n"); 
          do 
          { 
           if (check_serv_state(part, READY)) 
           { 
            printf("The server is ready for the chat\n"); 
            serv_state = READY_STATE; 
           } 
           else 
           printf("Server: [[[%s]]]\n", part); 
          } 
          while ((part = strtok(NULL, "\n"))); 
    
          if (serv_state != READY_STATE) 
          continue; 
         } 
         else 
         printf("Server reply: %s", buffer); 
    
         bzero(buffer, sizeof(buffer)/sizeof(buffer[0])); 
         printf("Enter the message: "); 
         bzero(buffer, sizeof(buffer)/sizeof(buffer[0])); 
         fgets(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, stdin); 
    
         if (send(socket_fd, buffer, strlen(buffer), 0) < 0) 
         { 
          perror("Error sending message"); 
          exit(1); 
         } 
        } 
        close(socket_fd); 
        return 0; 
    } 
    
    +0

    'int do_shutdown = 0;'應該定義爲'volatile sig_atomic_t do_shutdown;'。 – alk

    +0

    @alk,是的。更新了答案。謝謝。 –

    相關問題