I have a pool of socket connection that share in mu开发者_如何学Pythonltiple threads, a dead connection should be removed from pool, the problem is I don't know which is dead from SIGPIPE sighandler, Any advice in this situation?
One way to deal with this is to ignore SIGPIPE. This means that your writing operation (write
, sendmsg
, whatever) will return with an error, and as long as you pay attention to the error returns, you'll know which file descriptor fails - because everything is synchronous.
The following programs have nothing to do with sockets, instead they use named pipes. Just putting up the program here to demonstrate how I went about handling the above issue. They are a pair of programs I wrote up to better understand named pipes. All error conditions are handled. There are two executables which must be compiled separately. Run each executable in two separate terminals on the same PC. The order in which you run them is not important. Whichever is run first, will create the named pipe and the other one when started will notice that its already created (mkfifo error handling). The writer write to the pipe once every second and the reader reads from the pipe whenever read() returns. On the writer side, SIGPIPE is ignored and instead EPIPE is handled. Press 'q' on the keyboard in any of the two terminal windows (reader or writer) and both will quit.
If SIGPIPE is not ignored, what would happen is that when I quit the reader before the writer (by pressing q in reader's terminal) the write() would fail and trigger the SIGPIPE signal to be received by the writer application. The default behaviour of this is to exit the application immediately. This of course is not what I need in the multithreaded application that was going to be using this code. So I ignored the signal and checked for the errno and handled it appropriately.
writer.c:
#include <stdio.h> //for printf()
#include <stdlib.h> //for malloc()
#include <stdint.h> //for definitions of uint8_t etc..
#include <unistd.h> //for sleep()
#include <sys/stat.h> //for S_IRUSR, S_IWUSR, mkfifo()
#include <fcntl.h> //for O_WRONLY
#include <errno.h> //for errno, perror()
#include "kbhit.h"
#include <sys/types.h>
#include <signal.h> //for signal()
/* Named pipe considerations (http://www.unixguide.net/unix/programming/2.10.3.shtml):
*
* To use the pipe, you open it like a normal file, and use read()
* and write() just as though it was a plain pipe.
* However, the open() of the pipe may block. The following rules apply:
*
* If you open for both reading and writing (O_RDWR), then the open will not block.
*
* If you open for reading (O_RDONLY), the open will block until
* another process opens the FIFO for writing, unless O_NONBLOCK is
* specified, in which case the open succeeds.
*
* If you open for writing O_WRONLY, the open will block until
* another process opens the FIFO for reading, unless O_NONBLOCK is
* specified, in which case the open fails.
*
* When reading and writing the FIFO, the same considerations apply as for
* regular pipes and sockets, i.e. read() will return EOF when all
* writers have closed, and write() will raise SIGPIPE when
* there are no readers. If SIGPIPE is blocked or ignored, the call
* fails with EPIPE.
*
*/
static const char file_path[] = "/tmp/anurag";
static const char message[] = "Hello from writer!";
int main(void) {
int ret;
int fd=0;
char keypress=0;
/*
* (http://stackoverflow.com/questions/4351989/cleanup-in-sigpipe)
* EPIPE is returned if fd is connected to a pipe or socket whose reading end is closed.
* When this happens the writing process will also receive a SIGPIPE signal.
* (Thus, the write return value is seen only if the program catches, blocks or ignores this signal.)
*/
signal(SIGPIPE, SIG_IGN);
//Create the FIFO (named pipe)
ret = mkfifo(file_path, S_IRUSR | S_IWUSR);
if(ret == 0) {
printf("mkfifo(): Named pipe created.\n");
} else {
if ((ret == -1) && (errno == EEXIST)) {
perror("mkfifo()");
} else {
perror("mkfifo()");
}
}
printf("Will now begin waiting on open()...\n");
fd = open(file_path, O_WRONLY);
if(fd == -1) {
perror("open()");
} else if (fd > 0) {
printf("open(): Named pipe file descriptor opened for writing.\n");
}
while(keypress != 'q') {
if (kbhit()) {
keypress = getchar();
printf("Exiting...\n");
} else {
ret = write(fd, message, sizeof(message));
if(ret > 0) {
printf("write(): %d bytes to pipe: %s\n",ret,message);
} else if (ret <=0) {
if(errno == EPIPE) {
printf("write(): got EPIPE, reader closed the pipe, exiting...\n");
break;
} else {
perror("write()");
break;
}
}
}
sleep(1);
};
ret = close(fd);
if(ret == 0) {
printf("close(): Named pipe file descriptor closed.\n");
} else {
perror("close()");
}
ret = remove(file_path);
if(ret == 0) {
printf("remove(): Named pipe deleted.\n");
} else {
perror("remove()");
}
fflush(stdout);
fflush(stderr);
return EXIT_SUCCESS;
}
reader.c:
#include <stdio.h> //for printf()
#include <stdlib.h> //for malloc()
#include <stdint.h> //for definitions of uint8_t etc..
#include <unistd.h> //for sleep()
#include <sys/stat.h> //for S_IRUSR, S_IWUSR, mkfifo()
#include <fcntl.h> //for O_WRONLY
#include <errno.h> //for errno, perror()
#include <string.h> //for memset()
#include "kbhit.h"
/* Named pipe considerations (http://www.unixguide.net/unix/programming/2.10.3.shtml):
*
* To use the pipe, you open it like a normal file, and use read()
* and write() just as though it was a plain pipe.
* However, the open() of the pipe may block. The following rules apply:
*
* If you open for both reading and writing (O_RDWR), then the open will not block.
*
* If you open for reading (O_RDONLY), the open will block until
* another process opens the FIFO for writing, unless O_NONBLOCK is
* specified, in which case the open succeeds.
*
* If you open for writing O_WRONLY, the open will block until
* another process opens the FIFO for reading, unless O_NONBLOCK is
* specified, in which case the open fails.
*
* When reading and writing the FIFO, the same considerations apply as for
* regular pipes and sockets, i.e. read() will return EOF when all
* writers have closed, and write() will raise SIGPIPE when
* there are no readers. If SIGPIPE is blocked or ignored, the call
* fails with EPIPE.
*
*/
static const char file_path[] = "/tmp/anurag";
static char message[100] = {0};
int main(void) {
int ret;
int fd=0;
char keypress=0;
//Create the FIFO (named pipe)
ret = mkfifo(file_path, S_IRUSR | S_IWUSR);
if(ret == 0) {
printf("mkfifo(): Named pipe created.\n");
} else {
if ((ret == -1) && (errno == EEXIST)) {
perror("mkfifo()");
} else {
perror("mkfifo()");
}
}
printf("Will now begin waiting on open()...\n");
fd = open(file_path, O_RDONLY);
if(fd == -1) {
perror("open()");
} else if (fd > 0) {
printf("open(): Named pipe file descriptor opened for reading.\n");
}
while(keypress != 'q') {
if (kbhit()) {
keypress = getchar();
printf("Exiting...\n");
} else {
memset(message,0,100);
ret = read(fd, message, 100);
if(ret > 0) {
printf("read(): %d bytes from pipe: %s\n",ret, message);
} else if (ret == 0){
printf("read(): got EOF, writer closed the pipe, exiting...\n");
break;
} else if (ret < 0){
perror("read()");
break;
}
}
sleep(1);
};
ret = close(fd);
if(ret == 0) {
printf("close(): Named pipe file descriptor closed.\n");
} else {
perror("close()");
}
ret = remove(file_path);
if(ret == 0) {
printf("remove(): Named pipe deleted.\n");
} else {
perror("remove()");
}
fflush(stdout);
fflush(stderr);
return EXIT_SUCCESS;
}
Both the above c files make use of kbhit() to poll for character received from the keyboard. Here is the code for that:
kbhit.c:
//Taken from: http://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include "kbhit.h"
int kbhit(void) {
struct termios oldt, newt;
int ch;
int oldf;
tcgetattr(STDIN_FILENO, &oldt);
newt = oldt;
newt.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSANOW, &newt);
oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
ch = getchar();
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
fcntl(STDIN_FILENO, F_SETFL, oldf);
if (ch != EOF) {
ungetc(ch, stdin);
return 1;
}
return 0;
}
kbhit.h:
#ifndef KBHIT_H_
#define KBHIT_H_
//Console related variables and functions
int kbhit(void);
#endif /* KBHIT_H_ */
精彩评论