2013-10-11 83 views
2

我有一個在tcp套接字5060上偵聽的SIP服務器(守護進程)。現在,在此父進程中,我創建了一個子進程並在子進程中執行了一些操作。 現在,當我在父進程中關閉此tcp套接字並嘗試再次創建(比如說我正在禁用並在此服務器上啓用SIP)時,會發生什麼情況,創建此套接字會給我提供錯誤。我已調試此問題並找到根本原因。根本原因是當孩子被創建時它從父代繼承(從中獲得副本)所有打開的fd /套接字。當父母關閉tcp套接字時,它仍然在child(ref_counter!= 0)中打開,因此我不能再次在父級中打開套接字!在子進程中關閉打開的套接字

現在,我想要的通用解決方案是 - 一旦子進程啓動,它將檢查任何打開的fd(IPv4/TCP類型)並關閉它們,以便此子進程對父進程沒有副作用。這怎麼能在C-unix中完成? 我已經考慮過在系統的方向(lsof | grep | awk)並獲取文件描述符,但是如何關閉它們? 任何其他解決方案關閉兒童插座?有沒有一種方法,我可以通過端口號,它給了我已經創建了FD?

解決方案,我不希望是(該不會是對我有幫助) -
1.在父進程,初步同時創造了一些標誌的TCP套接字,使它們不被孩子複製。 (我不能修改父進程中的套接字創建)! 2.在創建子進程時將文件描述符從父項傳遞給子項。我不能這樣做,因爲我沒有那個fd。解決方案必須是需要放在子進程中的東西!

謝謝

+0

我不確定這是可能的,而不需要編輯父進程代碼。每當我遇到這個問題時,我都使用了你不需要的解決方案2。 –

+1

一種可能的方法,如果使用Linux:檢查/ proc//fd /中的鏈接。關閉鏈接到「socket:[inode]」的描述符。 –

+2

我懷疑你錯誤診斷了根本原因,現在正在考慮解決你自己製造問題的兩種不良解決方案。對於孩子來說,關閉監聽套接字並讓父母在分叉之後立即關閉數據套接字應該是微不足道的。 – Duck

回答

1

這裏有一種方法來確定你的孩子是否你的文件描述符是套接字。

由於孩子要繼承fd表,只需遍歷FD來測試每個表。以下程序中的孩子通過getrlimit獲取fd表的最大大小,並通過表遍歷表來確定每個文件描述符是否(a)打開,(b)套接字,如果是的話(c)是否是偵聽套接字。父母只是在分叉之前打開一個監聽和常規套接字(用於測試目的),然後等待孩子。

你應該能夠使用這個大綱來實現你的目標,而不訴諸於awk等。

#define _BSD_SOURCE 
#include <stdio.h> 
#include <stdlib.h> 
#include <sys/wait.h> 
#include <unistd.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#include <errno.h> 
#include <sys/stat.h> 
#include <string.h> 
#include <netdb.h> 


int isListeningSocket(int fd) 
{ 
    int retval; 
    socklen_t len = sizeof(retval); 

    if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &retval, &len) != -1) 
     if (retval) 
      return(1); 

    return(0); 
} 

int main (int argc, char *argv[]) 
{ 
    //create a listening socket 
    int lsock = socket(AF_INET,SOCK_STREAM, 0); 

    struct sockaddr_in serverinfo; 
    memset(&serverinfo, '0', sizeof(serverinfo)); 
    serverinfo.sin_family=AF_INET; 
    serverinfo.sin_port=htons(atoi("9999")); 
    serverinfo.sin_addr.s_addr=INADDR_ANY; 

    int ret; 

    if ((ret = bind(lsock,(struct sockaddr *) &serverinfo, sizeof(serverinfo))) == -1) 
    { 
     perror("bind"); 
     exit(1); 
    } 

    if ((ret = listen(lsock,1000)) == -1) 
    { 
     perror("listen"); 
     exit(1); 
    } 

    //create a regular socket 
    int rsock = socket(AF_INET,SOCK_STREAM, 0); 

    int pid = fork(); 

    if (pid == -1) 
    { 
     perror("fork"); 
     exit(1); 
    } 

    if (pid) //parent 
    { 
     wait(NULL); 
     exit(0); 
    } 

    //child ---------- 

    struct rlimit rlim; 

    if ((ret = getrlimit(RLIMIT_NOFILE, &rlim)) == -1) 
    { 
     perror("getrlimit"); 
     exit(1); 
    } 

    int maxFD = rlim.rlim_cur; 

    for (int i = 0; i < maxFD; ++i) 
    { 
     struct stat statbuf; 

     if (fstat(i, &statbuf) == -1) 
      if (errno == EBADF) 
      { 
       printf("file descriptor %d is not open\n", i); 
       continue; 
      } 
      else 
      { 
       perror("fstat"); 
       exit(1); 
      } 

     if (S_ISSOCK(statbuf.st_mode)) 
      if (isListeningSocket(i)) 
       printf("file descriptor %d is a LISTENING socket\n", i); 
      else 
       printf("file descriptor %d is a REGULAR socket\n", i); 
     else 
      printf("file descriptor %d is NOT a socket\n", i); 
    } 

    return 0; 
} 
+0

完全沒有必要。這個孩子有相同的數據。和fork()之後的父輩有相同的變量。所有孩子所要做的就是關閉'lsock'或代碼中調用的任何東西,父母所要做的就是關閉'csock'或者代碼中調用的任何東西。你的'rsock'是完全逃脫我的。 – EJP

+0

@EJP - 我知道。我前兩天說過,但這是OP堅持要他做的。 – Duck

+0

@EJP - 如果你在做出毫無根據的粗暴決定之前花了10秒鐘來考慮它,它不會逃脫你。 – Duck

4

你有文件描述符。只需關閉你不需要的那些!

在孩子你應該關閉收聽插座。

在父項中,您應關閉接受的套接字(=新連接)。

3

孩子們繼承父母的打開文件描述符(這也包括套接字)。在你的情況下,子進程也有套接字打開並綁定到上面的端口。因此,沒有其他進程(在正常情況下)可以在此端口上監聽。你需要做的是關閉小孩的插座(或者,如果你不需要,在fork()之前)在分岔之前關閉它。

相關問題