Селект возвращает сразу на fd, возвращаемом функцией принятия - PullRequest
0 голосов
/ 20 сентября 2019

Мой вопрос точно такой же, как в заголовке.

Это традиционная серверная программа, где в основном потоке я создаю, связываю и слушаю сокет.После создания сокета я жду вызова select, чтобы определить любой запрос клиентского соединения.При получении нового соединения вызов select сбрасывается, я принимаю соединение с помощью функции accept и передаю fd , возвращенную им, клиентскому потоку, где он ожидает выберите вызовите снова для доступности данных.

while(1)
  {
    select() call waiting on fd
      recv() on select release
  }

Как и ожидалось, вызов select немедленно освобождается, и я могу получить данные, используя recv функция.

За этим следует интересная часть.После извлечения данных с использованием recv они снова и переходят к функции select , как указано в приведенном выше коде sudo.Здесь я ожидаю, что функция select должна дождаться получения новых данных в сокете fd.Но, к моему удивлению, он немедленно освобождается и переходит к функции recv . (Которая возвращает ошибку EINVAL)

Мой вопрос: почему функция select освобождается?сразу во второй раз.Я немного знаком с сокетами, поэтому любая помощь приветствуется.

Редактировать: проблема, о которой я упоминал выше, заключается в том, что я сталкиваюсь с одним из проектов, над которым я работаю, чтобы изолировать, отладить и понять проблему.Лучше я создал образец приложения, которое имитирует исходную проблему.Таким образом, добавление примера приложения здесь.

#include "stdio.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> 
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>


/* waits on select call for getting connection request */
int waitForData(int sock_fd)
{
    int                 result = 0;
    fd_set              rfds;
    struct timeval      tv;

    FD_ZERO ( &rfds );
    FD_SET ( sock_fd, &rfds );

    tv.tv_sec = 10;
    tv.tv_usec = 0;

    while(1)
    {
        result = select ( sock_fd+1, &rfds, NULL, NULL, &tv);
        if ( -1 == result )
        {
            if ( errno == EINTR )
            {
                /*
                ** Select interrupted by a signal.
                ** tv is updated for time remaining, restart select()
                */
                continue;
            }

            printf("select fails\n");
            return -1;
        }
        else if ( 0 == result )
        {
            printf("select timeouts\n");
            return -2;
        }
        else
        {
            if(FD_ISSET(sock_fd, &rfds))
            {
                printf("COM_Select - Fd is set\n");
                return 0;
            }
            else
            {
                printf("COM_Select - Fd is NOT set\n");
                return -1;
            }
        }
    }//while
}//End of waitForData



void* client_thread(void* ptr_argument)
{
    if(ptr_argument == 0)
    {
        printf("Invalid client_sockfd passed to client thread\n");
        return 0;
    }

    int client_sockfd = *((int*)ptr_argument);
    printf("Client: client_sockfd: %d\n", client_sockfd);

    char data[501];

    printf("Client is launched\n");
    while(1)
    {
        printf("Waiting for incoming data...\n");
        int waitResult = waitForData(client_sockfd);
        if(waitResult == 0)
        {
            int data_len = 0;
            (void)memset(data, 0, sizeof(data));
            data_len = recv(client_sockfd, data, 500, 0);
            if (data_len == -1)
            {
                if ( (errno == EWOULDBLOCK) || (errno == EAGAIN) )
                    printf("Data is being blocked, should try again..\n");

                printf("\r\nrecv Error (client_sockfd %d, Error %d)", client_sockfd, errno);
                // break;
            }
            else if(data_len == 0)
            {
                // Disconnect received
                printf("\r\nrecv Disconnect (client_sockfd %d)", client_sockfd);
                // break;
            }
            else
            {
                printf("Data received successfully\n");
            }
            printf("data: %s\n", data);
        }
        else if(waitResult != -2)
        {
            printf("Error in client wait: %d\n", waitResult);
        }

        char ch;
        scanf("%c", &ch);
    }
}

class Server
{
    public:
        /* Constructor */
        Server()
        {
            mListenFd = 0;
        }

        /* Destructor */
        ~Server()
        {}

        /* Creates and initializes socket */
        bool createSocket()
        {
            mListenFd = socket(AF_INET, SOCK_STREAM, 0);
            if (mListenFd == -1)
            {
                printf("\r\nsocket Error (Error %d)", errno);
                return false;
            }

            int optval = 1;
            socklen_t optlen = sizeof(optval);
            setsockopt(mListenFd, SOL_SOCKET, SO_REUSEADDR, &optval, optlen);

            int fcntl_flags = 0;
            // Switch the server socket to non-blocking mode
            fcntl_flags = fcntl(mListenFd, F_GETFL, 0);
            fcntl(mListenFd, F_SETFL, fcntl_flags | O_NONBLOCK);

            memset(&mSockAddr, 0, sizeof(struct sockaddr_in));
            mSockAddr.sin_family = AF_INET;
            mSockAddr.sin_port   = htons(502);
            mSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);

            if(bind(mListenFd, (struct sockaddr *)(&mSockAddr), sizeof(struct sockaddr_in) ) == -1)
            {
                printf("\r\nbind Error (iFd %d, Error %s)", mListenFd, strerror(errno));
                close(mListenFd);
                return false;
            }

            if(listen(mListenFd, 5) == -1)
            {
                printf("\r\nlisten Error (mListenFd %d, Error %d)", mListenFd, errno);
                close(mListenFd);
                return false;
            }

            return true;
        }// createSocket

        /* waits on select call for getting connection request */
        int waitForConnRequest()
        {
            int                 result = 0;
            fd_set              rfds;
            // struct timeval       tv;

            FD_ZERO ( &rfds );
            FD_SET ( mListenFd, &rfds );

            // tv.tv_sec = 1;
            // tv.tv_usec = 0;

            printf("Waiting for incoming connections...\n");
            while(1)
            {
                result = select ( mListenFd+1, &rfds, NULL, NULL, 0);

                if ( -1 == result )
                {
                    if ( errno == EINTR )
                    {
                        /*
                        ** Select interrupted by a signal.
                        ** tv is updated for time remaining, restart select()
                        */
                        continue;
                    }

                    return -1;
                }
                else if ( 0 == result )
                {
                    return -1;
                }
                else
                {
                    if(FD_ISSET(mListenFd, &rfds))
                    {
                        printf("COM_Select - Fd is set\n");
                        return 0;
                    }
                    else
                    {
                        printf("COM_Select - Fd is NOT set\n");
                        return -1;
                    }
                }
            }//while
        }//End of waitForConnRequest

        /* accepts the incoming connection request and creates a client thread */
        int acceptConnection()
        {
            int accept_fd = -1;
            struct linger server_linger ={0};
            int fcntl_flags = 0;
            socklen_t addr_len = sizeof(struct sockaddr_in);

            if (mListenFd <= 0)
            {
                printf("\r\nAccept on INVALID_SOCKET\n");
                return -1;
            }

            accept_fd = accept(mListenFd, (struct sockaddr *)(&mSockAddr), &addr_len);
            if (accept_fd == -1)
            {
                if ((errno == EWOULDBLOCK)||(errno == EAGAIN)||(errno == ECONNABORTED))
                    return -1;
                else
                {
                    printf("\r\nconnect Error (accept_fd %d, Error %d)", accept_fd, errno);
                    return -1;
                }
            }

            // Set linger timeout to 0, so connections do not remain in TIME_WAIT
            server_linger.l_onoff  = 1;
            server_linger.l_linger = 0;
            setsockopt(accept_fd, SOL_SOCKET, SO_LINGER, (char*)&server_linger, sizeof(server_linger));

            // Set to non-blocking
            fcntl_flags = fcntl(accept_fd, F_GETFL, 0);
            fcntl(accept_fd, F_SETFL, fcntl_flags | O_NONBLOCK);

            return accept_fd;
        }

        int createClient(int client_sockfd)
        {
            int                 result = 0;
            pthread_t           thread_id;
            pthread_attr_t attr;
            int rc;

            rc = pthread_attr_init(&attr);
            if (rc == -1)
            {
                printf("error in pthread_attr_init");
                close(client_sockfd);
                exit(1);
            }

            rc = pthread_attr_setstacksize(&attr, 4*1024*1024);
            if (rc == -1)
            {
                close(client_sockfd);
                printf("error in pthread_attr_setstacksize");
                exit(2);
            }

            /**
             * Start thread
             */
            int* p_fd = new int;
            *p_fd = client_sockfd;
            printf("createClient: client_sockfd: %d\n", client_sockfd);
            result = pthread_create(&thread_id, &attr, client_thread, p_fd);
            if(0 != result)
            {
                //printf("main: ERROR: pthread_create for %d thread failed with error = %d\n", thread, result);
                printf("thread creation failed\n");
                return -1;
            }

            return 0;
        }

        void startServer()
        {
            if(createSocket())
            {
                while(1)
                {
                    if(waitForConnRequest() == 0)
                    {
                        int client_sockfd = acceptConnection();
                        if(client_sockfd != -1 && client_sockfd != 0)
                        {
                            printf("Server: creating client: client_sockfd: %d\n", client_sockfd);
                            if(-1 == createClient(client_sockfd))
                            {
                                close(client_sockfd);
                                printf("Error creating client thread.\nQuitting..\n");
                                break;
                            }
                        }
                    }
                }// while(1)
            }//createSocket

            printf("\n\nUn expected error in the startServer method\n");
        }

    private:
        int                     mListenFd;
        struct sockaddr_in      mSockAddr;
};

int main()
{
    Server inst_srvr;
    inst_srvr.startServer();

    printf("Bye!!\n");
}

Пожалуйста, игнорируйте отладочные сообщения, комментарии, любые утечки памяти или ошибки.

Таким образом, в основном клиентский поток после запуска сервером ожидаетданные в client_sockfd , а затем извлекают данные с помощью функции recv .Затем немедленно переходит к функции select , здесь, так как я получил данные в предыдущем вызове функции recv , я ожидаю, что функция select будет ждать, покавремя ожидания (в этом случае будет возвращено -2).Но функция select всегда выполняется успешно и возвращает 0. Итак, для повторения вопроса, почему select всегда выполняется успешно.

Пожалуйста, предоставьте некоторые предложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...