客户端完成传递消息后,服务器无法退出接受循环

时间:2019-06-15 10:58:48

标签: c sockets tcp

使用fork创建的客户端程序读取多个文件并创建单独的套接字。然后,每个套接字将其读取的文件中的消息发送到服务器,该服务器使用fork处理多个客户端。但是,服务器永远不会退出accept循环-因此,即使关闭了客户端上的所有套接字,它也不会终止。

服务器:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>
#include "uthash.h" //Used for building hash map

#define PORT "3400"
#define HOST "localhost"
#define MAXDATASIZE 20
#define DEPARTMENT_LEN 2
#define BACKLOG 5



int main(void){
  int sockfd, rv, child, numBytes;
  int opt = 1;
  struct addrinfo hints, *servinfo, *p;
  struct sockaddr_storage their_addr; //connector's address information
  socklen_t sin_size;
  struct sigaction sa;

  char dept[MAXDATASIZE];
  double gpa;
  char dept_name[DEPARTMENT_LEN + 1];

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE;

  if((rv = getaddrinfo(HOST, PORT, &hints, &servinfo)) != 0){
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
    return 1;
  }

  //loop though all the results and bind to the first we can
  for(p = servinfo; p != NULL; p = p->ai_next){
    if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
      perror("server: socket");
      continue; //move to next available socket
    }

    //reuse port and supress address already in use warnings
    if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)) == -1){
      perror("server: setsockopt");
      exit(1);
    }

    //Bind socket and local address
    if(bind(sockfd, p->ai_addr, p->ai_addrlen) == -1){
      close(sockfd);
      perror("server: bind");
      continue;
    }
    break;
  }

  if(p == NULL){
    fprintf(stderr, "server: failed to bind\n");
    return 1;
  }

  freeaddrinfo(servinfo); //free list structure

  //Listen to client
  if(listen(sockfd, BACKLOG) == -1){
    perror("server: listen");
    exit(1);
  }

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

  while(1){//accept() main loop
    sin_size = sizeof(their_addr);
    if((child = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1){
      perror("server: accept");
      continue;
    }

    if(!fork()){//this is the child process
      close(sockfd);
      while(1){
        if((numBytes = recv(child, dept, MAXDATASIZE, 0)) == -1){
          perror("server: recv");
          exit(1);
        }
        dept[numBytes] = '\0';
        if(strcmp(dept, ":exit") == 0){
          printf("%s\n", dept);
          break;
        }
        else{
          //printf("%s\n", dept);
          _parse_dept(dept, dept_name, &gpa);
          //printf("%s: %.1f\n", dept_name, gpa);
          _add_dept(dept_name, gpa);
          // _print_dept();
          bzero(dept_name, (int)strlen(dept_name));
          bzero(dept, (int)strlen(dept));
        }
      }//end while
      //_print_dept();
      printf("%d\n", 2);
      close(child);
      exit(0);
    }// end fork
    printf("%d\n", 3);
    close(child); //parent doesn't need this
  }
  printf("%d\n", 4);
  //_print_dept();
  // _delete_all();
  return 0;
}

客户端:

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

#define PORT "3400"
#define NO_DEPARTMENTS 3
#define LINE_SIZE 7
#define HOST "localhost"

//Global variable containing respective departments file name extensions
char * filenames[] = {"DepartmentA.txt", "DepartmentB.txt", "DepartmentC.txt"};
char * department_names[] = {"DepartmentA", "DepartmentB", "DepartmentC"};

int main(void){
  pid_t child_pid, wpid;
  int status = 0;
  for(int ii = 0; ii < NO_DEPARTMENTS; ii++){
    if((child_pid = fork()) == 0){
      int sockfd, rv;
      char dept_ip[INET6_ADDRSTRLEN]; //Department IP address
      unsigned int dept_port; //Department port
      struct addrinfo hints, *servinfo, *p;
      struct sockaddr_in my_addr;

      memset(&hints, 0, sizeof(hints));
      hints.ai_family = AF_UNSPEC;
      hints.ai_socktype = SOCK_STREAM;
      if((rv = getaddrinfo(HOST, PORT, &hints, &servinfo)) != 0){
        fprintf(stderr, "\ngetaddrinfo: %s\n", gai_strerror(rv));
        return 1;
      }

      //loop through all the results and connect to the first that we can find
      for(p = servinfo; p != NULL; p = p->ai_next){
        if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
          close(sockfd);
          perror("client: socket");
          continue;
        }

        if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1){
          close(sockfd);
          perror("client: connect");
          continue;
        }
        break;
      }
      if(p == NULL){
        fprintf(stderr, "client: failed to connect\n");
        return 1;
      }
      //1) Upon startup of Phase 1
      socklen_t len = sizeof(my_addr);
      getsockname(sockfd, (struct sockaddr *)&my_addr, &len);
      inet_ntop(AF_INET, &my_addr.sin_addr, dept_ip, sizeof(dept_ip));
      dept_port = ntohs(my_addr.sin_port);
      printf("<%s> has TCP port %d ", filenames[ii], dept_port);
      printf("and IP address %s for Phase 1\n", dept_ip);

      //2) Upon establishing a TCP connection to the admission office
      printf("<%s> is now connected to the admission office\n", filenames[ii]);


      //readfile and send contents to Addmissions office
      struct Node * fileContent = NULL;
      _readFile(&fileContent, filenames[ii]);

      struct Node * fileIter = fileContent;
      while(fileIter != NULL){
        sleep(3);
        send(sockfd, fileIter->dept, (int)strlen(fileIter->dept), 0);
        fileIter = fileIter->next;
      }
      sleep(3);
      char *ex = ":exit";
      send(sockfd, ex, (int)strlen(ex), 0);
      _freeFile(&fileContent);
      freeaddrinfo(servinfo); // free up list structure
      close(sockfd);
      exit(0); //exit for fork
    }
  }

  while ((wpid = wait(&status)) > 0);
  return 0;
}

1 个答案:

答案 0 :(得分:0)

解决了!我必须使要接受的队列(BACKLOG)与部门数相同,在这种情况下为3。然后,我将计数器初始化为队列的大小,并在每次执行接受时将其递减。一旦计数器为零,我就让父进程等待所有子进程,然后手动中断accept循环。

RichText(
  text: TextSpan(
    text: 'Hello ',
    style: TextStyle(color: Colors.black, fontWeight: FontWeight.w900)),
    children: <TextSpan>[
      TextSpan(text: 'bold', style: TextStyle(fontWeight: FontWeight.bold)),
      TextSpan(text: ' world!'),
    ],
  ),
)