2016-08-22 103 views
1

我一直在找出爲什麼下面的代碼現在整天都不好描述符。以下是服務器代碼,其中大部分引用了Beej的指南。C服務器客戶端錯誤的文件描述符

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <errno.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <netdb.h> 
#include <arpa/inet.h> 
#include <sys/wait.h> 
#include <signal.h> 

#include "CountryData.c" 

//This function determine if address is IPv4 or IPv6 IP address 
void *getAddr_Type(struct sockaddr *sa) { 
    if (sa->sa_family == AF_INET) { //If IPv4 
     return &(((struct sockaddr_in*)sa)->sin_addr); 
    } 
    else //if IPv6 
    return &(((struct sockaddr_in6*)sa)->sin6_addr); 
} 

void sigchld_handler(int s) 
{ 
    // waitpid() might overwrite errno, so it is stored in a variable first: 
    int saved_errno = errno; 

    while(waitpid(-1, NULL, WNOHANG) > 0); 

    errno = saved_errno; 
} 


int main(void){ 

    readData(); //Read from CountryData.c 

    int status, sockfd, client_sockfd; 
    int pid; //fork return value 

    char buffer[1000]; 
    int bytecount; 

    struct addrinfo hints, *res, *serverInfo; //res points to linked list of "struct addrinfo"; serverInfo also points to linked list of "struct addrinfo" for use in for loop. 
    struct sockaddr_storage client_addr; //Address information of the client 
    struct sigaction sa; 

    socklen_t address_size; //Initialize size of address 

    char i[INET6_ADDRSTRLEN]; //INET6_ADDRSTRLEN macro is used to store maximum length of IPv6. Since IPv4 is definitely shorter than IPv6, "INET_ADDRSTRLEN" is not used. 


    memset(&hints, 0, sizeof(hints)); //emptying the structure 

    //Pass in value into "addrinfo" struct 
    hints.ai_family = AF_INET; //Using IPv4 
    hints.ai_socktype = SOCK_STREAM; //Using TCP 
    hints.ai_flags = AI_PASSIVE; //AI_PASSIVE = Own IP address 

    status = getaddrinfo(NULL, "8888", &hints, &serverInfo); //Initialising status return value and also passing in values to getaddrinfo(). 
//IP address is set to null. This will be filled in automatically by AI_PASSIVE. 

    if (status != 0) { 
     fprintf(stderr, "Error: %s\n", gai_strerror(status));//gai_strerror to print human readable error 
     exit(1); 
    } 


    //Loop through all results and bind to the first 
    for (res = serverInfo; res != NULL; res = res->ai_next) { 

     //(1)Initializing socket 
     if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1) { //Show error message if initializing socket file descriptor fails 
      perror("Socket"); 
      continue; 
     } 

     int optValue=1; 

     if ((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &optValue, sizeof(int))) == -1) { 
      perror("Socket options"); 
      exit(1); 
     } //(2)setting socket options. "SO_REUSEADDR" to prevent "Address already in use" and to allow reuse of the port 

     if ((bind(sockfd, res->ai_addr, res->ai_addrlen)) == -1) { //(3) Binding to local address and port 
      close(sockfd); 
      perror("Bind"); 
      continue; 
     } 

    break; 
    } 
    freeaddrinfo(serverInfo); //Freeing "serverInfo" linked list 


    //However, if linked list is still empty, print error. 
    if (res == NULL) { 
     fprintf(stderr, "Error! Server is unable to bind.\n"); 
     exit(1); 
    } 

    //If unable to listen, print error. 
    if ((listen(sockfd, 8)) == -1) { //(4) Listen for client connections, maximum of 8 waiting in queue 
     perror("Listen"); 
    } 

    sa.sa_handler = sigchld_handler; // reap all dead processes 
    sigemptyset(&sa.sa_mask); 
     sa.sa_flags = SA_RESTART; 
     if (sigaction(SIGCHLD, &sa, NULL) == -1) { 
      perror("sigaction"); 
      exit(1); 
    } 


    printf("Running server program 'server' ... \n\n\nCountry Directory Server Started! PID: %d\n", getpid()); 



    for(;;) //infinite loop for server to wait for client requests 
    { 
     memset(buffer, 0, 1000); 
     address_size = sizeof(client_addr); 
     client_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &address_size); 

     if (client_sockfd == -1) { 
      perror("Accept"); 
      close(client_sockfd); 
      exit(1); 
     } 

     inet_ntop(client_addr.ss_family, getAddr_Type((struct sockaddr *)&client_addr), i, sizeof(i));//retrieving IP address. "inet_ntop" is used for IPv6 compatibility. 


     printf("-------------------------------------------------------\n"); 
     printf("Connection received from:  %s\n\n", i); 


     if ((pid = fork()) == -1){ //Starts forking 
      perror("Failed to fork"); 
      close(sockfd); 
     } 

     else if (pid == 0){ //child process 

      close(sockfd);//Child doesn't need this socket 
      memset(buffer, 0, 1000); //clear the buffer 

      if ((bytecount = recv(client_sockfd, buffer, 1000, 0)) == -1){//Receiving Client's input 
       perror("Server unable to receive"); 
       close(client_sockfd); 
       exit(0); 
      } 
      else if ((strcasecmp(buffer, "END")) == 0){ //Nested If-statement; If client sends "end" 
       close(client_sockfd); 
       exit(0); 
       break; 
      } 

      else if (bytecount == 0) { //If "recv" returns 0, client has closed the connection 
       printf("Client (%d) has closed the connection.\n", getpid()); 
       close(client_sockfd); 
       exit(0); 
       break; 
      }else { 
      printf("%s", buffer); 
      printf("%d", client_sockfd); 
      } 
     } 

    close(client_sockfd); 

    } //end of infinite while loop 

}//End of main function 

它成功讀取客戶端的輸入並在屏幕上爲第一個for(;;)循環打印出來。在第二次迭代之後,它顯示Bad file descriptor 以下是在客戶端中鍵入Hi之後服務器終端中的輸出。

Johnny$ server 
Running server program 'server' ... 


Country Directory Server Started! PID: 18386 
------------------------------------------------------- 
Connection received from:  127.0.0.1 

Accept: Bad file descriptor 
Hi4 

4被印刷出來的孩子文件描述符的返回值。這意味着循環運行一次,然後返回一個錯誤。我的預期輸出只是爲了不斷聽取客戶的輸入,服務器應該不斷地吐出客戶輸入的內容。 我是新來的這臺服務器的東西,真的有一個令人頭痛的權利,讓這個工作。任何幫助將不勝感激。

下面是客戶端的代碼,如果你有興趣。

#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <string.h> 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <netdb.h> 
#include <arpa/inet.h> 
#include <errno.h> 

void welcome() 
{ 
    printf("\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); 
    printf("  Welcome to the Country Info Directory Service!   \n"); 
    printf("  ----------------------------------------------   \n"); 
    printf("Usage :\n\n"); 
    printf("1) At the '>' prompt, type in the name of the country\n"); 
    printf(" you wish to search\n\n"); 
    printf("2) To end program, type in 'end'\n"); 
    printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n\n"); 
} 

//This function determine if address is IPv4 or IPv6 IP address 
void *getAddr_Type(struct sockaddr *sa) { 
    if (sa->sa_family == AF_INET) { //If IPv4 
     return &(((struct sockaddr_in*)sa)->sin_addr); 
    } 
    else //if IPv6 
    return &(((struct sockaddr_in6*)sa)->sin6_addr); 
} 

int main(int argc, char *argv[]) { 
    int sockfd, numOfBytes; 
    int retrieveInfo; 
    char buf[100]; 
    struct addrinfo hints, *res, *serverInfo; 

    char i[INET6_ADDRSTRLEN]; //INET6_ADDRSTRLEN macro is used to store maximum length of IPv6. Since IPv4 is definitely shorter than IPv6, "INET_ADDRSTRLEN" is not used. 

    if (argc != 2){ 
     printf("Please enter in this format:\n'client':server-host-name\nFor example, if your hostname is vmwubuntu, please type:\nclient vmwubuntu [Enter]\n\n"); 
     exit(1); 
    } 

    welcome(); 

    memset(&hints, 0, sizeof(hints)); //emptying the structure 

    //Pass in value into "addrinfo" struct 
    hints.ai_family = AF_INET; //Using IPv4 
    hints.ai_socktype = SOCK_STREAM; //Using TCP 

    retrieveInfo = getaddrinfo(argv[1], "8888", &hints, &serverInfo); 

    if(retrieveInfo != 0) { 
     printf("Fail to retrieve address!"); 
    } 

    //Loop through all results and bind to the first 
    for (res = serverInfo; res != NULL; res = res->ai_next) { 

     sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); //(1)Initializing socket 

     if (sockfd == -1) { //Show error message if initializing socket file descriptor fails 
      perror("Socket"); 
      continue; 
     } 

     if (connect(sockfd, res->ai_addr, res->ai_addrlen) == -1) { //Retrieving values from "struct addrinfo" through "res" pointer 
      //close(sockfd); 
      perror("Connection"); 
      continue; 
     } 
     break; 
    } 

    if (res == NULL) { 
    printf("Failed to connect to server\n"); 
    exit(1); 
    } 

    char input[1000]; 
    char receive[1000]; 

    for(;;) 
    { 
     memset(input, '\0', 1000); //Initialize buffer size to store user input 
     printf("Enter Country > "); 
     fgets(input, 1000, stdin); //Take in user input with 1000 as the buffer size 
     input[strlen(input) - 1] = '\0'; //Stripping the null terminator away 

     if (strcasecmp(input, "END") == 0){ //If user enters "end"(case is ignored), close the file descriptor and exit 
      close(sockfd); 
      exit(0); 
     } 

     else {//SEND 
      if((numOfBytes = send(sockfd, input, strlen(input), 0)) == -1){ //start of nested if statement 
       perror("Unable to send"); 
       exit(1); 
      } 
      else if (numOfBytes != strlen(input)){ //If string is not sent in full 
       perror("Send"); 
       close(sockfd); 
       exit(1); 
      }else{//for testing purposes 
      printf("%d\n",strlen(input));//for testing purpose 
      printf("%d\n", numOfBytes); //for testing purpose 
      }//End of nested if statement 

     } 

    }//End of for infinite loop 
} //End of main() 

回答

3

您的子進程似乎沒有退出,而是繼續使用與父進程相同的代碼。然後,您嘗試用關閉的文件描述符調用accept

這就是爲什麼我總是把子代碼放在它自己的函數中,並且總是緊接着調用_exit()。請注意,我使用_exit()而不是exit()來確保沒有父atexit處理程序被執行。

此外,它有助於在您的日誌消息中包含PID。嘗試使用類似這樣的東西:

#define INFO(fmt, ...) fprintf(stderr, "[%d] %s" fmt, getpid(), __FUNCTION__, __VA_ARGS__) 

... 
INFO("x=%d\n", x); 
+0

這真的很有幫助!謝謝! – JamesPoppycock

1

服務器端的子進程將嘗試接受相同的fd。 如何添加一個無限循環之前

else if (pid == 0){ //child process 

     close(sockfd);//Child doesn't need this socket 
     memset(buffer, 0, 1000); //clear the buffer 

     for (;;) { 
     if ((bytecount = recv(client_sockfd, buffer, 1000, 0)) == -1){//Receiving Client's input 
      perror("Server unable to receive"); 
      close(client_sockfd); 
      exit(0); 
     } 
     else if ((strcasecmp(buffer, "END")) == 0){ //Nested If-statement; If client sends "end" 
      close(client_sockfd); 
      exit(0); 
      break; 
     } 

     else if (bytecount == 0) { //If "recv" returns 0, client has closed the connection 
      printf("Client (%d) has closed the connection.\n", getpid()); 
      close(client_sockfd); 
      exit(0); 
      break; 
     }else { 
     printf("%s", buffer); 
     printf("%d", client_sockfd); 
     } 

     } 
    } 
相關問題