在编写一个仿QQ软件,C/S模式。出现的问题:当客户机关闭时,服务器也随着关闭,纠结很久之后,我gdb了下,出现下面提示信息:
Program received signal SIGPIPE, Broken pipe.
0x0012e416 in __kernel_vsyscall ()
在 网上查了一下出现SIGPIPE的原因:如果尝试send到一个已关闭的
socket上两次,就会出现此信号,也就是用协议TCP的socket编程,服务器是不能知道客户机什么时候已经关闭了socket,导致还在向该已关
闭的socket上send,导致SIGPIPE。
而系统默认产生SIGPIPE信号的措施是关闭进程,所以出现了服务器也退出。
下面分析TCP协议的缺陷以至于服务器无法及时判断对方socket已关闭:
具 体的分析可以结合TCP的"四次握手"关闭. TCP是全双工的信道, 可以看作两条单工信道,
TCP连接两端的两个端点各负责一条. 当对端调用close时, 虽然本意是关闭整个两条信道, 但本端只是收到FIN包.
按照TCP协议的语义, 表示对端只是关闭了其所负责的那一条单工信道, 仍然可以继续接收数据. 也就是说, 因为TCP协议的限制,
一个端点无法获知对端的socket是调用了close还是shutdown.(此段网上抄来的)
解决方法:
重新定义遇到SIGPIPE的措施,signal(SIGPIPE, SIG_IGN);具体措施在函数SIG_IGN里面写。
摘自:
当服务器close一个连接时,若client端接着发数据。根据TCP协议的规定,会收到一个RST响应,client再往这个服务器发送数据时,系统会发出一个SIGPIPE信号给进程,告诉进程这个连接已经断开了,不要再写了。
又或者当一个进程向某个已经收到RST的socket执行写操作是,内核向该进程发送一个SIGPIPE信号。该信号的缺省学位是终止进程,因此进程必须捕获它以免不情愿的被终止。
根据信号的默认处理规则SIGPIPE信号的默认执行动作是terminate(终止、退出),所以client会退出。若不想客户端退出可以把
SIGPIPE设为SIG_IGN
如:signal(SIGPIPE, SIG_IGN);
这时SIGPIPE交给了系统处理。
服务器采用了fork的话,要收集垃圾进程,防止僵尸进程的产生,可以这样处理:
signal(SIGCHLD,SIG_IGN);
交给系统init去回收。
这里子进程就不会产生僵尸进程了。
在linux下写socket的程序的时候,如果尝试send到一个disconnected
socket上,就会让底层抛出一个SIGPIPE信号。
这个信号的缺省处理方法是退出进程