TCP 客户端不处理损坏的服务器在 C 中正确连接

TCP client not handling broken server connect correctly in C

我使用 TCP 创建了自己的 ftp server/client(一个非常简单的版本)。有 5 个可能的命令:ls-remote、ls-local、get、put 和 exit。此外,我在我的服务器代码中使用多处理,以便能够通过使用 fork() 同时处理多个客户端。除了我注意到的一件事外,一切都运行良好:我在正确处理损坏的服务器连接时遇到问题。例如,如果服务器发送消息时出现问题,我会检查发送调用中的 return 值是否小于 0,然后关闭套接字,并调用 exit(-1) 来终止进程;但是,这会导致我的客户端挂起...

我的客户:

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

void readDirectory();

void syserr(char* msg) 
{ perror(msg); 
  exit(-1); 
}

int main(int argc, char* argv[])
{
  int sockfd, portno, n;
  struct hostent* server;
  struct sockaddr_in serv_addr;
  char buffer[256], temp[256];

  if(argc < 3) {
    fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]);
    return 1;
  }

    portno = atoi(argv[2]);


  server = gethostbyname(argv[1]);
  if(!server) {
    fprintf(stderr, "ERROR: no such host: %s\n", argv[1]);
    return 2;
  }

  sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(sockfd < 0) syserr("can't open socket");

  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr = *((struct in_addr*)server->h_addr);
  serv_addr.sin_port = htons(portno);

  if(connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
    syserr("can't connect to server");

  printf("Connection to %s:%s established. Now awaiting commands...\n\n\n", argv[1], argv[2]);


  do{
     printf("%s:%s> ", argv[1], argv[2]); //prompt user for command
     fgets(buffer, 255, stdin);
     n = strlen(buffer);

    if(n>0 && buffer[n-1] == '\n')
      buffer[n-1] = '[=10=]';


    if(strcmp(buffer, "ls-remote") == 0){ //display files from server directory
      uint32_t size;
      uint32_t commandSize = strlen(buffer);

       //convert to network form
       commandSize = htonl(commandSize);

       n = send(sockfd, &commandSize, sizeof(commandSize), 0); // send size of command to server 
       if(n < 0) syserr("can't send to server");

       n = send(sockfd, buffer, strlen(buffer), 0); // send command to server 
       if(n < 0) syserr("can't send to server");

       n = recv(sockfd, &size, sizeof(size), 0); // recieve the size of the directory
       if(n < 0) syserr("can't receive from server");

       size = ntohl(size);
       int currentSize = 0;
       printf("Files at the server: %s\n", argv[1]);

        while(currentSize < size){
            memset(&buffer[0], 0, sizeof(buffer));
            n = recv(sockfd, buffer, 255, 0); // recieve directory from server 
            if(n < 0) syserr("can't recieve server");

            currentSize = currentSize + n;

            printf("%s", buffer);
        }
    }
    else if(strcmp(buffer, "ls-local") == 0){ //display files from local directory
        printf("Files at the client: \n");
       readDirectory();
    }
    else if(strncmp(buffer, "get ", 4) == 0){ //downlaod file from server


       if(strlen(buffer) < 5){ // no file was entered
          printf("%s\n", "ERROR...missing filename: get <filename>");
        }
       else{
          uint32_t fileSize;
          uint32_t commandSize = strlen(buffer);


          //convert to network form
          commandSize = htonl(commandSize);

          n = send(sockfd, &commandSize, sizeof(commandSize), 0); // send size of command to server
          if(n < 0) syserr("can't send to server");

          n = send(sockfd, buffer, strlen(buffer), 0); // send command to server
          if(n < 0) syserr("can't send to server");

           n = recv(sockfd, &fileSize, sizeof(fileSize), 0); // get size of file 
                  if(n < 0) syserr("can't receive from server");

          fileSize = ntohl(fileSize);

          if(fileSize == -1){
              printf("%s\n", "File does not exist");
           }
           else{   // file exists
               int totalBytes = 0;
               int bytesWritten = 0;

                // get file name
                char *fileName = strtok(buffer, " ");
                fileName = strtok(NULL, " ");

                memcpy(temp, fileName, strlen(fileName)); //copy filename into temp
                temp[strlen(fileName)] = '[=10=]';


              //create new file with given name
              FILE *fpNew = fopen(fileName, "w");

              if(fpNew){

                  while(totalBytes < fileSize){
                      //receieve the bytes
                      n = recv(sockfd, buffer, sizeof(buffer), 0);
                      if(n < 0) syserr("can't receive from server");

                      //write the bytes
                      int b = fwrite(buffer, 1, n, fpNew);

                      if (b < 0)
                      syserr("error writing file");

                      if(n == 0){ // error reading on server side 
                        break;
                      }

                      totalBytes = n + totalBytes;
                      bytesWritten = b + bytesWritten;

                    }

                    fclose(fpNew);
                     if(bytesWritten == fileSize) // all bytes read/written to file successfully
                       printf("Retrieval of file %s: successful.\n", temp);
                     else
                       printf("Retrieval of file %s: unsuccessful.\n", temp);

                 }
                 else{
                       syserr("couldnt open file for writing.");
                 }
           }
        }
     }
     else if(strncmp(buffer, "put ", 4) == 0){ // upload file to server
          if(strlen(buffer) < 5){
          printf("%s\n", "ERROR...missing filename: get <filename>");
        }
        else{
            uint32_t commandSize = strlen(buffer);
            uint32_t  status;

            memcpy(temp, buffer, strlen(buffer)); //copy buffer into temp
            temp[strlen(buffer)] = '[=10=]';

            // get name of file
            char *fileName = strtok(temp, " ");
            fileName = strtok(NULL, " ");
            int bytes;

            FILE *fp = fopen(fileName, "r"); // open the file

            if(fp){  // file exists and opened
                int totalBytes = 0;
                  //convert to network form
                commandSize = htonl(commandSize);

                n = send(sockfd, &commandSize, sizeof(commandSize), 0); // send size of command to server
                if(n < 0) syserr("can't send to server");

                n = send(sockfd, buffer, strlen(buffer), 0); // send command to server
                if(n < 0) syserr("can't send to server");

                // get file size
                fseek(fp, 0L, SEEK_END);
                int fileSize = ftell(fp);

               // send the file size
               uint32_t size = htonl(fileSize);

               n = send(sockfd, &size, sizeof(size), 0);
               if(n < 0) syserr("can't send to server");

               //go back to beginning of file
               fseek(fp, 0, SEEK_SET);

               while(totalBytes < fileSize){  // while there are more bytes...

                  bytes = fread(buffer, 1, sizeof(buffer), fp); // read bytes fromt he file

                  if(bytes < 0){
                    syserr("Error reading the file.");
                  }

                  totalBytes = totalBytes + bytes;

                   //send the bytes
                   n = send(sockfd, buffer, bytes, 0);
                   if(n < 0) syserr("can't send to server");


                  if(bytes == 0){ //error reading
                   break;
                  }

                }

               fclose(fp);

               //recieve the final status
                n = recv(sockfd, &status, sizeof(status), 0);
                  if(n < 0) syserr("can't receive from server");

                 status = ntohl(status); 

              if(totalBytes == fileSize && status == 1){ // successful on both ends
               printf("Upload of file %s: successful.\n", fileName);
              }
              else{
                 printf("Upload of file %s: unsuccessful.\n", fileName);
              }

          }
           else{
              printf("%s\n", "File does not exist");
           }
        }
     }else if(strcmp(buffer, "exit") == 0){
      uint32_t commandSize = strlen(buffer);
          //convert to network form
          commandSize = htonl(commandSize);

          n = send(sockfd, &commandSize, sizeof(commandSize), 0); // send size of command to server
          if(n < 0) syserr("can't send to server");

          n = send(sockfd, buffer, strlen(buffer), 0); // send command to server
          if(n < 0) syserr("can't send to server");

     }
     else{
       if(strcmp(buffer, "exit") != 0)
         printf("Error...invalid command.\nValid commands: ls-remote, ls-local, get <filename>, put <filename>, exit\n");
     }
   }while (strcmp(buffer, "exit") != 0);

   printf("Connection to server %s:%s terminated, BYE now!\n", argv[1], argv[2]);
   close(sockfd);
   return 0;
 }

void readDirectory(){
  DIR *d = opendir(".");
  struct dirent *dir;
    if (d)
      {
        while((dir = readdir(d))!= NULL)
        {
          printf("%s\n", dir->d_name);
        }
      closedir(d);
    }
    else{
       syserr("Error...could not get files from directory.");
    }
  }

我的服务器:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <dirent.h>

void syserr(char *msg){
 perror(msg); exit(-1); 
}
void errorHandling(char *msg, int newsockfd){
 close(newsockfd);
 perror(msg); exit(-1); 
}
uint32_t directorySize();
void handle_client(int newsockfd);


int main(int argc, char *argv[])
{
  int sockfd, newsockfd, portno;
  struct sockaddr_in serv_addr, clt_addr;
  socklen_t addrlen;

  if(argc < 1) { 
    fprintf(stderr,"Usage: %s <port>\n", argv[0]);
    return 1;
  } 
  if(argc == 1){
    argv[1] = "5555";
  }

  portno = atoi(argv[1]);

  sockfd = socket(AF_INET, SOCK_STREAM, 0); 
  if(sockfd < 0) syserr("can't open socket"); 

  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  serv_addr.sin_port = htons(portno);

  if(bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) 
    syserr("can't bind");
  printf("bind socket to port %d...\n", portno);

  listen(sockfd, 5); 


for(;;){

  printf("wait on port %d...\n", portno);
  addrlen = sizeof(clt_addr); 
  newsockfd = accept(sockfd, (struct sockaddr*)&clt_addr, &addrlen);
  if(newsockfd < 0) syserr("can't accept"); 

   pid_t pid = fork();

   if(pid < 0){
    syserr("Error can't fork");
   }
   else if(pid == 0){ // child process
     close(sockfd);
     handle_client(newsockfd);
     close(newsockfd);
     break;
   }
   else{ // parent
     close(newsockfd);
     continue;
   } 

  }
  return 0;
}

uint32_t directorySize(int newsockfd){
    DIR *d = opendir(".");
    struct dirent *dir;
    uint32_t count = 0;

    if (d)
      {
        while((dir = readdir(d))!= NULL)
        {
         //count++;
            count = strlen(dir->d_name) + count + 1;
        }
      closedir(d);
    }
    else{
       errorHandling("Error...could not get files from directory.", newsockfd);
     }

    return count;
  }
  void handle_client(int newsockfd){

    // receieve command size from client
  uint32_t commandSize;
  int n;
  char buffer[256];

  while(1){
  n = recv(newsockfd, &commandSize, sizeof(commandSize), 0); // receive size of command
  if(n < 0) errorHandling("can't receive from client", newsockfd); 

  commandSize = ntohl(commandSize);

  //recieve command
  n = recv(newsockfd, buffer, commandSize, 0); 
  if(n < 0) errorHandling("can't receive from client", newsockfd); 

  else buffer[n] = '[=11=]';


  if(strcmp(buffer, "ls-remote") == 0){  //display files from server directory

      // get the size of the directory
      uint32_t size = htonl(directorySize(newsockfd));

      n = send(newsockfd, &size, sizeof(size), 0); // send size of directory
      if(n < 0) errorHandling("can't send to client", newsockfd); 


      DIR *d = opendir(".");
      struct dirent *dir;

      if(d){
          while((dir = readdir(d))!= NULL){
              memset(&buffer[0], 0, sizeof(buffer));
             strcpy(buffer, dir->d_name);
             buffer[strlen(buffer)] = '\n';

             // send file/folder names
             n = send(newsockfd, buffer, strlen(buffer), 0);
            if(n < 0) 
              errorHandling("can't receive from client", newsockfd); 

       }

        closedir(d);

    }
    else{
       errorHandling("Error...could not get files from directory.", newsockfd);
    }

}
 else if (strncmp(buffer, "get", 3) == 0){  // if command is get
    char *fileName = strtok(buffer, " "); // "get"
    fileName = strtok(NULL, " "); // the name of the file
    FILE *fp = fopen(fileName, "r");

    if(fp){  // if file exists
        int totalBytes = 0;
        int bytes;

        // get the size of the file
        fseek(fp, 0L, SEEK_END);
        int fileSize = ftell(fp);

        // send the file size
         uint32_t size = htonl(fileSize);

         n = send(newsockfd, &size, sizeof(size), 0);
         if(n < 0) errorHandling("can't send to client", newsockfd); 


         //go back to beginning of file
         fseek(fp, 0, SEEK_SET);

         while(totalBytes < fileSize){ // while there are more bytes to read...

           // read the bytes into the buffer
           bytes = fread(buffer, 1, sizeof(buffer), fp);
           if(bytes < 0){
            errorHandling("Eorror reading bytes on server side", newsockfd);
           }

            //send the bytes
            n = send(newsockfd, buffer, bytes, 0);
            if(n < 0) errorHandling("can't send to client", newsockfd); 

            if(bytes == 0) // error reading file; bytes should have been > 0
                break;

            totalBytes = totalBytes + bytes;  
        }

         fclose(fp);
    }
     else{
      // tell client file doesnt exist by sending -1
        uint32_t dne = htonl(-1);
        n = send(newsockfd, &dne, sizeof(dne), 0);
        if(n < 0) errorHandling("can't send to client", newsockfd); 

     }
  }
  else if (strncmp(buffer, "put", 3) == 0){ // upload a file
      int totalBytes = 0;
      int bytesWritten = 0;
      int b = 0;
      uint32_t fileSize, status;

      n = recv(newsockfd, &fileSize, sizeof(fileSize), 0);// receive the size of file 
      if(n < 0) errorHandling("can't receive from client", newsockfd); 

      fileSize = ntohl(fileSize);

       // get file name
       char *fileName = strtok(buffer, " ");
       fileName = strtok(NULL, " ");

       //create new file with given name
        FILE *fpNew = fopen(fileName, "w");

          if(fpNew){
            while(totalBytes < fileSize){
                n = recv(newsockfd, buffer, sizeof(buffer), 0);
                  if(n < 0) errorHandling("can't receive from client", newsockfd); 

                 if(n == 0){ //bad file transfer on client side
                    break;
                 }

                 //write the bytes 
                 b = fwrite(buffer, 1, n, fpNew);

                 if(b < n){ // error writing to file
                    break;
                 }
                 totalBytes = totalBytes + n; // bytes recived
                 bytesWritten = bytesWritten + b; //bytes written

             }     

                  fclose(fpNew);

                  if(bytesWritten != fileSize){ // not all bytes written
                    status = htonl(-1); 
                  }
                  else{
                    status = htonl(1);
                  }

                  // send the status
                  n = send(newsockfd, &status, sizeof(status), 0);
                   if(n < 0) errorHandling("can't send client", newsockfd); 
          }
          else{
            errorHandling("could not open file for writing.", newsockfd);
          }

      } 
       else{ // command is exit
            printf("%s\n", "closing connection");
            close(newsockfd); // close the connection
            break;

        }

      }

   }
n = recv(sockfd, &size, sizeof(size), 0); // recieve the size of the directory
if(n < 0) syserr("can't receive from server");

这还不够。如果 n == 0 对等方已关闭连接:您必须这样做并退出读取循环,或者在您的情况下实际上可能是整个过程。