2016-08-22 105 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); 
     } 

     } 
    } 
相关问题