在做一个简单的socket程序时,遇到当client退出时,server状态为defunct情况。过程如下:
(1) Server <-- Client
(2) Server (accept) <-X- Client
(fork) |
New Server <-----+
(3) Server 杀掉Client后
New Server <defunct>
原因:在子进程退出时,发送了SIGCHLD信号。
解决方法在父进程中waitpid(); 可以获得子进程的退出状态
void sig_chld (int signo) { pid_t pid; int stat; while ((pid = waitpid(-1,&stat, WNOHANG)) > 0) printf("child %d terminated\n",pid); return; }
int main()
{
signal(SIGCHLD, sig_chld);
//...
fork();
}
也可以在父进程中忽略该信号
signal(SIGCHLD,SIG_IGN);
下面是几个socket用到的属性介绍.
int socket(int domain, int type, int protocol);
/* int type SOCK_CLOEXEC: 这个属性对应open函数的O_CLOEXEC 该标志作用为,当执行fork并执行exec的时候,在子进程中,自动关闭此描述符
参考:http://blog.csdn.net/chrisniu1984/article/details/7050663 */ /* 也可以通过如下代码设置 */
int v;
v = fcntl(fd, F_GETFD, 0);
fcntl(fd, F_SETFD, v|FD_CLOEXEC);
/* SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组还是唯一确定的。
* 所以,重启后的服务程序有可能收到非期望数据。必须慎重使用 SO_REUSEADDR 选项。
* 如果你的服务程序停止后想立即重启,而新套接字依旧使用同一地址和端口,此时 SO_REUSEADDR 选项非常有用。
*/
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
/* fcntl(fd, F_SETFL, O_NONBLOCK);
* 如果socket设置了如上属性,那么accept也将是非阻塞的。accept()函数会立即返回。
* 注:没有数据时,会返回错误。
*/
accept(s->fd, (struct sockaddr*)&cliaddr, &clilen);
read 收包,判断是否一个接收已经完成了。
/* 文件,socket 同理,read返回0时,才表示文件尾/数据包尾 */ while(read(sock, buf, sizeof(buf)-1)) { //数据放入缓冲区 } /*********************************** 同时有如下内部逻辑 TCP协议是面向流的,read和write调用的返回值往往小于参数指定的字节数。对于read调用, 如果接收缓冲区中有20字节,请求读100个字节,就会返回20。对于write调用,如果请求写100个字节, 而发送缓冲区中只有20个字节的空闲位置,那么write会阻塞,直到把100个字节全部交给发送缓冲区才返回, 但如果socket文件描述符有O_NONBLOCK标志,则write不阻塞,直接返回20 ***********************************/