我对这个简单的服务器代码有疑问,它会按预期工作,直到收到信号。 对于调试I,在使用以下行调用select之前打印服务器和客户端文件描述符:
fprintf(stderr,"server_socket_fd=%d client_socket_fd=%d fd_max=%d\n", server_socket_fd, client_socket_fd, fd_max);
正常运行时会继续打印
server_socket_fd=3 client_socket_fd=4 fd_max=4
但是当它收到信号时,它会打印一行
server_socket_fd=3 client_socket_fd=-1 fd_max=3
然后程序停止。
使用GDB我在signal_handler中放了一个断点,当它断开时我无法看到client_socket_fd变量,gdb说
No symbol "client_socket_fd" in current context.
它没有从signal_handler函数正确返回..如果我看后面的跟踪:
(gdb) bt
#0 0xb7fdccf9 in ?? ()
#1 0xb7e26af3 in __libc_start_main (main=0x8048bdd <main>, argc=1, argv=0xbfffef24, init=0x8049a00 <__libc_csu_init>,
fini=0x8049a70 <__libc_csu_fini>, rtld_fini=0xb7fed160 <_dl_fini>, stack_end=0xbfffef1c) at libc-start.c:287
#2 0x08048b01 in _start ()
我不知道如何更深入地调试。
这是主要代码:
char receive_buf[2048];
int main(int argc, char *argv[]){
int server_socket_fd;
int client_socket_fd = -1;
int fd_max;
struct sockaddr_in s_in;
int one = 1;
int status;
fd_set readfds;
int port;
int next_option;
const char* short_options = "hp:d:";
const struct option long_options[] = {
{ "help", 0, NULL, 'h'},
{ "port", 1, NULL, 'p'},
{ "debug", 1, NULL, 'd'},
{ NULL, 0, NULL, 0}
};
program_name = argv[0];
port = DEFAULT_PORT;
debug = 0;
do{
next_option = getopt_long(argc, argv, short_options, long_options, NULL);
switch(next_option){
case 'h':
print_usage(stdout, 0);
break;
case 'p':
port = atoi(optarg);
if((port < 0)||(port > 65535)){
fprintf(stderr, "Invalid port number (%d), using default: %d", port, DEFAULT_PORT);
port = DEFAULT_PORT;
}
break;
case 'd':
debug = atoi(optarg);
if(debug < 0 || debug > 3)
debug = 0;
break;
case '?':
print_usage(stderr, 1);
break;
case -1:
break;
default:
abort();
}
}while(next_option != -1);
/************************* SIGNAL DEFINITIONS ***************************/
signal_action.sa_handler = (void *)signal_handler;
sigemptyset(&signal_action.sa_mask);
signal_action.sa_flags = SA_RESTART; // | SA_NOCLDSTOP;
if(sigaction(SIGINT, &signal_action, NULL) == -1){
fprintf(stderr, "Error setting SIGINT signal handler\n");
exit(1);
}
if(sigaction(SIGTERM, &signal_action, NULL) == -1){
fprintf(stderr, "Error setting SIGTERM signal handler\n");
exit(1);
}
if(sigaction(SIGWINCH, &signal_action, NULL) == -1){
fprintf(stderr, "Error setting SIGWINCH signal handler\n");
exit(1);
}
/* // ALSO TRIED WITH SIGNAL WITH SAME RESULT
if(signal(SIGWINCH, signal_handler) == SIG_ERR){
fprintf(stderr, "signal error\n");
return 1;
}
*/
s_in.sin_family = PF_INET;
s_in.sin_port = htons(port);
s_in.sin_addr.s_addr = INADDR_ANY;
if ((server_socket_fd = socket(s_in.sin_family, SOCK_STREAM, IPPROTO_TCP)) == -1){
perror("Error creating socket");
return 1;
}
if(setsockopt(server_socket_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1){
perror("Error setting socket parameters");
return 1;
}
////////////////////////////////////////////
int x;
x=fcntl(server_socket_fd,F_GETFL,0); // Get socket flags
fcntl(server_socket_fd,F_SETFL,x | O_NONBLOCK); // Add non-blocking flag
////////////////////////////////////////////
if(bind(server_socket_fd, (struct sockaddr*) &s_in, sizeof(s_in)) == -1){
perror("Error binding socket");
return 1;
}
if(listen(server_socket_fd, 1) == -1){
perror("Error creating listening socket");
return 1;
}else{
printf("Server (%d) listening on port %d\n", server_socket_fd, port);
}
memset(receive_buf, '\0', sizeof(receive_buf));
gettimeofday(&t_print, NULL);
while(1){
// SERVER
FD_ZERO(&readfds);
FD_SET(server_socket_fd, &readfds);
fd_max = server_socket_fd;
// ADD CLIENT IF CONNECTED
if(client_socket_fd > 0){
FD_SET(client_socket_fd, &readfds);
if(client_socket_fd > server_socket_fd)
fd_max = client_socket_fd;
}
// ADDED THIS FPRINTF TO CHECK VARIABLES <----------------------------------
fprintf(stderr,"server_socket_fd=%d client_socket_fd=%d fd_max=%d\n", server_socket_fd, client_socket_fd, fd_max);
if(select(fd_max+1, &readfds, NULL, NULL, NULL) == -1){
if(errno != EINTR){
perror("select failed");
}
}
// ACCEPT CLIENT
if(FD_ISSET(server_socket_fd, &readfds)){
struct sockaddr_in s_in;
socklen_t len;
len = sizeof(s_in);
if((client_socket_fd = accept(server_socket_fd, (struct sockaddr*) &s_in, &len)) < 0){
if(errno != EWOULDBLOCK){
perror("En accept");
}
}else
printf("New client connected from %s\n", inet_ntoa(s_in.sin_addr));
}
// RECEIVE FROM CLIENT
if(client_socket_fd > 0){
if(FD_ISSET(client_socket_fd, &readfds)){
handle_client(client_socket_fd);
}
}
}
return 0;
}
int handle_client(int cl_fd){
int n;
n = recv(cl_fd, receive_buf, sizeof(receive_buf) - 1, MSG_DONTWAIT);
if(n == 0){
fprintf(stderr,"--------------> DEBUG: handle_client:client %d closed connection\n", cl_fd);
}else if(n < 0){
if(errno == EAGAIN){
return 0;
}else{
fprintf(stderr,"--------------> DEBUG: handle_client: recv ERROR: client %d closed connection (errno: %d : %s)\n", cl_fd, errno, strerror(errno));
memset(receive_buf, 0, sizeof(receive_buf));
return -1;
}
}else{
receive_buf[n] = '\0';
fprintf(stderr, "%s\n", receive_buf);
}
return 0;
}
void signal_handler(int sig){
switch(sig){
case SIGINT:
exit_properly(0);
break;
case SIGTERM:
exit_properly(1);
break;
case SIGABRT:
fprintf(stderr, "SIGABRT signal received\n");
break;
case SIGWINCH:
fprintf(stderr, "\33[2J");
fflush(stdout);
break;
default:
fprintf(stderr, "Unhandled signal %d received\n",sig);
break;
}
}
我不知道我还能做些什么来调试这个问题而且我被困住了。任何帮助将非常感谢!
编辑:
这是失败时的strace输出,因为你可以看到它打印(并选择使用)正确的文件描述符然后,在信号发生后,client_socket_fd是错误的,因为接受失败了EAGAIN。我已经评论了exit_properly调用以及SIGTERM和SIGINT的信号处理。对于SIGWINH信号,我什么都不做,只需返回。
STRACE输出:
write(2, "server_socket_fd=3 client_socket"..., 47server_socket_fd=3 client_socket_fd=4 fd_max=4
) = 47
select(5, [3 4], NULL, NULL, NULL) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
sigreturn() (mask []) = -1 EINTR (Interrupted system call)
accept(3, 0xbf981e7c, [16]) = -1 EAGAIN (Resource temporarily unavailable)
write(2, "server_socket_fd=3 client_socket"..., 48server_socket_fd=3 client_socket_fd=-1 fd_max=3
) = 48
select(4, [3], NULL, NULL, NULL) = ? ERESTARTNOHAND (To be restarted if no handler)
--- SIGWINCH {si_signo=SIGWINCH, si_code=SI_KERNEL} ---
sigreturn() (mask []) = -1 EINTR (Interrupted system call)
accept(3, 0xbf981e7c, [16]) = -1 EAGAIN (Resource temporarily unavailable)
write(2, "server_socket_fd=3 client_socket"..., 48server_socket_fd=3 client_socket_fd=-1 fd_max=3
) = 48
select(4, [3], NULL, NULL, NULL
现在是signal_handler:
void signal_handler(int sig){
switch(sig){
/*
case SIGINT:
exit_properly(0); //sigint_flag = 1;
break;
case SIGTERM:
exit_properly(1); //sigterm_flag = 1;
break;
*/
case SIGWINCH:
//sigwinch_flag = 1;
/*
fprintf(stderr, "\33[2J");
fflush(stdout);
*/
break;
default:
//fprintf(stderr, "Unhandled signal %d received\n",sig);
break;
}
}
在没有SA_RESTART
标志的情况下尝试了......同样的结果......?:/
答案 0 :(得分:2)
select()
将被信号中断,返回-1,并将errno设置为EINTR,但您的代码无法处理此问题。
即使您使用SA_RESTART
安装信号处理程序,仍有许多系统调用将被中断,返回错误条件并将errno设置为EINTR
。
请参阅“信号处理程序中断系统调用和库函数” http://man7.org/linux/man-pages/man7/signal.7.html/
部分如果选择失败,您的代码会继续检查readfds
,如下所示:
if(FD_ISSET(server_socket_fd, &readfds)){
但是,如果select()失败,则传递给它的fd_set
变量处于不确定状态,您不应该依赖它们的值。
相反,如果选择失败,您应该重新启动循环,例如像这样的继续声明:
if(select(fd_max+1, &readfds, NULL, NULL, NULL) == -1){
if(errno != EINTR){
perror("select failed");
//Might be severe enough to quit your program...
}
continue;
}
答案 1 :(得分:0)
我发现解决方案仅使用时间变量。
我不知道为什么,但是信号使得选择标志server_socket_fd被读取(如果有人知道为什么要分享),就像客户端会尝试连接,并接受返回的EAGAIN错误,所以client_socket_fd变量写到-1。
我只是使用临时变量(ret)解决了这个问题,而不是将接受结果直接分配给client_socket_fd:
// ACCEPT CLIENT
if(FD_ISSET(server_socket_fd, &readfds)){
struct sockaddr_in s_in;
socklen_t len;
len = sizeof(s_in);
if((ret = accept(server_socket_fd, (struct sockaddr*) &s_in, &len)) < 0){
if(errno != EWOULDBLOCK){
perror("En accept");
}
}else{
client_socket_fd = ret;
printf("New client connected from %s\n", inet_ntoa(s_in.sin_addr));
}
}
只有在你确定它们没问题时才更新变量......经验教训! :)