问题
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