Terminating C program on command line, but make sure writer is finished writing

情到浓时终转凉″ 提交于 2019-12-12 02:43:12

问题


I have a C server that writes packet data to an output file. The problem is I terminate the server in terminal using control c, and I've noticed that my output file is never finished writing to. Is there a way to make sure that my output file is completely written to before quitting?

(I know I could just let the server run longer so I'm sure the data I want has been written, but I'm looking for an alternative method)

Thanks

Here's my code for reference.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.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 <sys/time.h>

int PORT_NUM = 0; 
int RecordRate = 3000; 

typedef struct timeval timeval;
timeval time_;


void error(const char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno;
     socklen_t clilen;
     char buffer[1000000];
     struct sockaddr_in serv_addr, cli_addr;
     int n;
     FILE *fp;

     PORT_NUM = atoi(argv[1]);  
     fp = fopen(argv[2],"w");   

     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
            error("ERROR opening socket");
     bzero((char *) &serv_addr, sizeof(serv_addr));
     //portno = atoi(argv[1]);
     portno = PORT_NUM;
     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) 
        error("ERROR on binding");
     listen(sockfd,10);
     clilen = sizeof(cli_addr);

     newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
     if (newsockfd < 0) 
            error("ERROR on accept");

    int counter = 0;    
    int total_bytes_sent = 0;

     while(1){  

         bzero(buffer,1000000);
         n = read(newsockfd,buffer,999999);

         if (n < 0) {
             error("ERROR reading from socket");
         }
         else if (n != 0) {
             total_bytes_sent += n;
             gettimeofday(&time_, NULL);

             if(counter%RecordRate==0){
             printf("counter  %d \n", counter); 
             fprintf(fp,"\"%d\",\"%ld\",\"%d\",\"%d\",\"%d\"\n", counter, time_.tv_sec, time_.tv_usec, n,total_bytes_sent);
             }

             counter++; 

             //print format: packet number, time Sec, time milli Sec, bytes in packet, total bytes sent 
            // example: "11182","1465921447","196422","3100","26821100"

         }
     }
     fclose(fp);
     close(newsockfd);
     close(sockfd);
     return 0; 
     }

回答1:


You could set a custom handler for SIGINT in which you set a flag, and then exit gracefully whenever that flag is set.

#include <stdlib.h>
#include <signal.h>

volatile sig_atomic_t flag = 0;

void handle_int(int sig)
{
  flag = 1;
}

int main()
{
  signal(SIGINT, handle_int); // intercept SIGINT

  while (!flag)
  {
    // do work ...
  }
  // cleanup, fflush, etc...
  return 0;
}

Note that calling fflush directly in the signal handler is unsafe.

The // do work part obviously should be relatively short, so that the flag is checked regularly throughout the lifetime of the application.




回答2:


In Linux you can use the EVIOCGKEY ioctl to check if a key is pressed and then break out of the while (1) loop.

#include <fcntl.h>
#include <linux/input.h>
#include <sys/ioctl.h>

int rdKey(int fd, int key) {//faster not to open file every time the function is called
//  const char *keyboard = "/dev/input/by-path/platform-i8042-serio-0-event-kbd";
//  int fd = open(keyboard, O_RDONLY);
    char arr[(KEY_MAX+7)/8];
    memset(arr, 0, sizeof(arr));
    ioctl(fd, EVIOCGKEY(sizeof(arr)), arr);
    return !!(arr[key/8] & (1<<(key % 8)));
}

You would pass a file descriptor for an input device like a keyboard and then either the key number or a macro from input.h like KEY_A for 'a' and it would return 1 if the key is pressed and 0 if not.




回答3:


Another approach to consider would be have the application fork() with the child process becoming the server and the parent remain attached to the terminal waiting for input with the two communicating through a socket or pipe.

Before doing the fork() the parent would create a socket or pipe which it will use to communicate with the child process. The user could then type commands into the parent process still attached to the terminal and the parent process would then echo commands to the child process through the socket or pipe.

The child would use select() and include the communication socket/pipe in the list of descriptors. So where as your code above uses only a single socket with the read() you would instead use a select() with both the socket shared by with the parent as well as the socket from the accept with a check to determine if there is a command from the parent waiting to be read or more server work to do. The child can then do any tidying up needed and exit cleanly with the parent doing a wait() for it to finish up and then the parent exits itself.

Sorry I don't have any code example handy. See the following stackoverflow for details about sockets and select().

Is it possible (and safe) to make an accepting socket non-blocking?

c++ select async programming

Asynchronous C client for a multiclient C server

How do I change a TCP socket to be non-blocking?

working of fork in c language



来源:https://stackoverflow.com/questions/38751319/terminating-c-program-on-command-line-but-make-sure-writer-is-finished-writing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!