socket() возвращает 0 в клиентском серверном приложении C

Я работаю над приложением, которое содержит несколько серверных сокетов, каждый из которых запускается в уникальном потоке.
Внешняя утилита (скрипт) вызывается одним из потоков. Этот скрипт вызывает утилиту (клиент), которая отправляет сообщение на один из серверных сокетов.

Первоначально я использовал system() для выполнения этого внешнего сценария, но мы не могли его использовать, потому что нам нужно было убедиться, что сокеты сервера закрыты в дочернем элементе, который был разветвлен для выполнения внешнего сценария.
Теперь я вызываю fork() и execvp() себя. Я fork(), а затем в дочернем я закрываю все сокеты сервера, а затем вызываю execvp() для выполнения скрипта.

Теперь все это работает нормально. Проблема в том, что иногда скрипт сообщает об ошибках серверному приложению. Сценарий отправляет эти ошибки, вызывая другое приложение (клиент), которое открывает сокет TCP и отправляет соответствующие данные. Моя проблема в том, что клиентское приложение получает значение 0, возвращаемое системным вызовом socket().

ПРИМЕЧАНИЕ. Это происходит ТОЛЬКО при вызове скрипта/клиентского приложения с использованием моей функции forkExec(). Если скрипт/клиентское приложение вызывается вручную, вызов socket() выполняется надлежащим образом, и все работает нормально.

Основываясь на этой информации, я подозреваю, что это что-то в моем коде fork() execvp() ниже... Есть идеи?

void forkExec()
{    
    int stat;

    stat = fork();
    if (stat < 0)
    {
        printf("Error forking child: %s", strerror(errno));
    }
    else if (stat == 0)
    {
        char *progArgs[3];

        /*
         * First, close the file descriptors that the child 
         * shouldn't keep open
         */
        close(ServerFd);
        close(XMLSocket);
        close(ClientFd);
        close(EventSocket);
        close(monitorSocket);

        /* build the arguments for script */
        progArgs[0] = calloc(1, strlen("/path_to_script")+1);
        strcpy(progArgs[0], "/path_to_script");
        progArgs[1] = calloc(1, strlen(arg)+1);
        strcpy(progArgs[1], arg);
        progArgs[2] = NULL; /* Array of args must be NULL terminated for execvp() */

        /* launch the script */
        stat = execvp(progArgs[0], progArgs);
        if (stat != 0)
        {
            printf("Error executing script: '%s' '%s' : %s", progArgs[0], progArgs[1], strerror(errno));
        }
        free(progArgs[0]);
        free(progArgs[1]);
        exit(0);
    }

    return;
}

Код клиентского приложения:

static int connectToServer(void)
{
int socketFD = 0;
int status;
struct sockaddr_in address;
struct hostent* hostAddr = gethostbyname("localhost");

socketFD = socket(PF_INET, SOCK_STREAM, 0);

Приведенный выше вызов возвращает 0.

if (socketFD < 0)
{
    fprintf(stderr, "%s-%d: Failed to create socket: %s", 
                                __func__, __LINE__, strerror(errno));
    return (-1);
}

memset(&address, 0, sizeof(struct sockaddr));
address.sin_family = AF_INET;
memcpy(&(address.sin_addr.s_addr), hostAddr->h_addr, hostAddr->h_length);
address.sin_port = htons(POLLING_SERVER_PORT);

status = connect(socketFD, (struct sockaddr *)&address, sizeof(address));
if (status < 0)
{
    if (errno != ECONNREFUSED)
    {
        fprintf(stderr, "%s-%d: Failed to connect to server socket: %s",
                   __func__, __LINE__, strerror(errno));
    }
    else
    {
        fprintf(stderr, "%s-%d: Server not yet available...%s",
                   __func__, __LINE__, strerror(errno));
        close(socketFD);
        socketFD = 0;
    }
}

return socketFD;
}

К вашему сведению
ОС: Linux
Arch: ARM32
Ядро: 2.6.26


person Steve Lazaridis    schedule 25.01.2010    source источник
comment
Не могли бы вы опубликовать код, который вызывает socket(), пожалуйста?   -  person abc    schedule 25.01.2010
comment
Пока не знаю, что происходит с вашей розеткой. Возможно, вам будет полезно, если я укажу, что вы можете объявить свои аргументы намного проще: const char *progArgs[] = {/path_to_script, arg, NULL}; - Не нужно выделять и копировать, все, что вам нужно, это массив с правильными указателями на момент вызова execvp.   -  person VoidPointer    schedule 25.01.2010


Ответы (4)


socket() возвращает -1 при ошибке.

Возврат 0 означает, что socket() выполнился успешно и дал вам файловый дескриптор 0. Я подозреваю, что один из файловых дескрипторов, которые вы закрываете, имеет файловый дескриптор 0, и после его закрытия следующий вызов функции, которая выделила файловый дескриптор, вернет fd 0 как это доступно.

person R Samuel Klatchko    schedule 25.01.2010
comment
Да, я так и подозревал. Но нет, я ничего подобного не делал. - person Steve Lazaridis; 25.01.2010
comment
Он возвращает 0 (как указано в вашем вопросе) или возвращает отрицательное значение (как проверяет ваш образец кода)? Повторим еще раз: возврат 0 вполне допустим, если доступен fd 0. - person R Samuel Klatchko; 25.01.2010
comment
Он возвращает ноль. Я понимаю, что 0 является допустимым fd. Проблема здесь должна иметь какое-то отношение к моей функции forkExec(), потому что, если я перехожу на использование сокета system(), возвращается › 0. К сожалению, я не могу использовать вызов system(). - person Steve Lazaridis; 25.01.2010
comment
система использует оболочку для запуска новой программы. Я предполагаю, что оболочка открывает fd 0 (который затем наследуется вашей подпрограммой), поэтому он больше не доступен при вызове socket(). - person R Samuel Klatchko; 25.01.2010
comment
Вызов system() запускает /bin/sh -c , но ни вызов system(), ни /bin/sh не закроют файловые дескрипторы, как в forkExec(). - person nos; 25.01.2010
comment
Верный! Один из fds, который я закрывал, был установлен на ноль, он еще не был инициализирован. Спасибо! - person Steve Lazaridis; 26.01.2010

Сокет со значением 0 в порядке, это означает, что стандартный ввод был закрыт, что сделает fd 0 доступным для повторного использования, например, сокетом.

скорее всего, один из файловых дескрипторов, которые вы закрываете в дочернем пути forkExec() (XMLSocket/ServerFd) и т. д.), был fd 0 . Это запустит ребенка с закрытым fd 0, чего не произойдет, когда вы запустите приложение из командной строки, так как fd 0 уже будет открыт как стандартный ввод оболочки.

Если вы хотите, чтобы ваш сокет не был 0,1 или 2 (stdin/out/err), вызовите следующее в своей функции forkExec() после всех вызовов close()

void reserve_tty()
{
  int fd;

  for(fd=0; fd < 3; fd++)
    int nfd;
    nfd = open("/dev/null", O_RDWR);

    if(nfd<0) /* We're screwed. */
    continue;

    if(nfd==fd)
    continue;

    dup2(nfd, fd);
    if(nfd > 2)
     close(nfd);

}

Проверьте, не возвращает ли сокет -1, что означает, что произошла ошибка.

person nos    schedule 25.01.2010
comment
Но ПОЧЕМУ стандартный ввод закрывается? Почему кто-то хочет сделать это намеренно? - person AJ.; 25.01.2010
comment
Обычно (хотя часто они перенаправляются в /dev/null, как показано в приведенном выше коде) и рекомендуется закрывать все fd, которые не нужны, в фоновом процессе/процессе демона, которым, как я полагаю, является родительский процесс. - person nos; 25.01.2010

Не забудьте позвонить в

waitpid()

Конец «режима очевидных вопросов». Я здесь немного предполагаю, но вы ничего не делаете с pid, возвращаемым вызовом fork(). (-:

person Rob Wells    schedule 25.01.2010
comment
Да, у меня уже есть зарегистрированный обработчик SIGCHLD, который выполняет функцию waitpid() для очистки от зомби. Спасибо, в любом случае :) - person Steve Lazaridis; 25.01.2010

Как упоминается в другом комментарии, вам действительно не следует закрывать 0,1 или 2 (stdin/out/err), вы можете поставить галочку, чтобы убедиться, что вы не закрываете их, и поэтому они не будут назначены как новые fd` s когда вы запрашиваете новый сокет

person Adel    schedule 02.11.2016