主程序先建立一个全局Socket连接句柄后,fork()出一子进程,子进程获得了父进程数据空间、堆和栈的复制品。然后主子进程共同使用该连接句柄。这时此socket的引用计数为2, 任一进程关闭后对引用计数会-1,直到引用计数=0时,socket关闭。在某些情况下主进程需断开该socket并重新连接,此时此socket会无法断开。


通过如下程序测试

#include <sys/types.h>
#include <sys/uio.h>
#include <sys/signal.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/wait.h>

int sock;

void
catch_signal(int sig)
{
    if (sig == SIGUSR1)
    {
        printf("close socket\n");
        close(sock);

    }
}

int runapp()
{
    pid_t pid, pidwait;
    int waitcnt;
    int status;
    if ((pid = fork()) == 0 ) {
       
            printf("This is child process....\n");
            int itt = 5;
            //close(sock);

               execl("/bin/testsock", "testsock", "-a", NULL);
            exit(3);
        }
        printf("pid : %d\n", pid);
        waitcnt = 0;
        do{
        pid1=waitpid(pid, &status, WNOHANG);    /* 使用了WNOHANG参数,waitpid不会在这里等待 */
        if(pid==0){                     /* 如果没有收集到子进程 */       
            sleep(1);
            ++waitcnt;
        }
    }while((pidwait==0) && (waitcnt < 2));           
    if (pidwait == pid)
    {
        printf("stustus form pid %d\n", WEXITSTATUS(status));
    }

    return 0;

}

int main(int argc, char * argv[])
{

    signal(SIGUSR1, catch_signal);


    sock = socket(AF_INET, SOCK_DGRAM, 0);

    if (sock <= 0)
    {
        //
        // Failed to create the socket
        //
        printf("failed to create socket\n");
        return 0;

    }


    struct linger ling;
    ling.l_onoff = 0;
    ling.l_linger = 0;

   /* if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (char*) &ling, sizeof (ling)) != 0)
    {

    }*/

   
    // setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) NULL, 0);
    //
    /*int on = 1;
   if( setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)) !=0) {


   }*/
   
    //
    // Bind to any local address
    //
    struct sockaddr_in sockAdd;
    memset((char*) &sockAdd, 0, sizeof (sockAdd));

    sockAdd.sin_family      = AF_INET;
    inet_aton("10.3.24.100", (in_addr*)&(sockAdd.sin_addr));
   
    sockAdd.sin_port        = htons(5999);
       
    int result = bind(sock, (struct sockaddr*)&sockAdd, sizeof(sockAdd));
    if (result != 0)
    {

        close(sock);
        sock = -1;
        return 0;

    }


    //
    // Retrieve the port
    //
    /*int localPort = 0;
    socklen_t addrLen = sizeof(sockAdd);
    if (getsockname(sock, (struct sockaddr*)&sockAdd, &addrLen) == 0)
    {
        localPort = ntohs(sockAdd.sin_port);
    }
    else
    {
        return 0;
    }*/
     int i = runapp();
     printf("i : %d\n", i);
   
    while(1)
    {
        sleep(10);
    }
    close (sock);
 
    return true;
}

在函数runapp中如果在fork后没有close掉该socket。我们通过SIGUSR1来让主进程关闭socket
AN# ps -aux | grep testapp
15        3409  0.0  0.0  1244  784  p1  S+   11:49AM   0:00.08 ./testapp
AN# kill -30 3409
收到SIGUSR1后,主进程会关闭socket,但该socket依然存在
udp4       0      0  10.3.24.100.5999       *.* 
在关闭完/bin/testsock进程后, 此socket才会被关闭。


避免这个问题需要在fork完之后在子进程不再用到该socket的话在子进程中先close掉该socket。如果子进程根本就不会用的该socket可以用fcntl(sock,F_SETFD,1)来设置运行exec是关闭该socket句柄;
需要注意的一点,如果是使用system来运行别的程序,也会有这种情况。