当服务器向 tcp 客户端发送数据时,epoll 未收到“EPOLLIN”信号

epoll not receiving `EPOLLIN` signal when server sends data to tcp client

提问人:Habib Ayode 提问时间:11/16/2023 更新时间:11/16/2023 访问量:30

问:

当我创建一个TCP客户端并将其连接到服务器时,它能够检测到它何时连接(通过)。服务器应该向客户端发送消息,但客户端从未通过 epoll 接收到信号。我使用了 Wireshark,可以确认服务器发送了数据,所以我不知道问题出在哪里。EPOLLOUT

代码:

/** A structure representing a TCP client. */
struct tcp_client
{
    /** The socket file descriptor. */
    socket_t sockfd;
    /** The address of the server to connect to. */
    struct sockaddr sockaddr;

    /** Whether or not the client is polling. */
    int listening;

#ifndef _WIN32
    /** The polling file descriptor. */
    int pfd;
#endif 

    /** User defined data to be passed to the event callbacks. */
    void *data;

    /** The callback for when the client has connected to the server. */
    void (*on_connect)(struct tcp_client *client);
    /** The callback for when the client has received a message from the server. */
    void (*on_data)(struct tcp_client *client);
    /** The callback for when the client has disconnected from the server. */
    void (*on_disconnect)(struct tcp_client *client, int is_error);
};


int tcp_client_main_loop(struct tcp_client *client)
{
    /** The client socket should be nonblocking when listening for events. */
    socket_set_non_blocking(client->sockfd);
    client->listening = 1;

    while (client->listening)
    {
#ifdef __linux__
        int pfd = client->pfd;
        struct epoll_event events[1];
        int nev = epoll_wait(pfd, events, 1, -1);
        if (nev == -1) return netc_error(POLL_FD);
#elif _WIN32
        WSAPOLLFD events[1];
        events[0].fd = client->sockfd;
        events[0].events = POLLIN | POLLOUT | POLLERR | POLLHUP;
        int nev = WSAPoll(events, sizeof(events), -1);
        if (nev == -1) return netc_error(POLL_FD);
#elif __APPLE__
        int pfd = client->pfd;
        struct kevent events[1];
        int nev = kevent(pfd, NULL, 0, events, 1, NULL);
        if (nev == -1) return netc_error(POLL_FD);
#endif

        if (client->listening == 0) break;

#ifdef __linux__
        struct epoll_event ev = events[0];
        socket_t sockfd = ev.data.fd;

        if (ev.events & EPOLLIN && client->on_data != NULL)
            client->on_data(client);
        else if (ev.events & EPOLLOUT)
        {
            int error = 0;
            socklen_t len = sizeof(error);
            int result = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);

            if (result == -1 || error != 0)
                return netc_error(HANGUP);
            else if (client->on_connect != NULL)
                    client->on_connect(client);

            ev.events = EPOLLOUT;
            if (epoll_ctl(pfd, EPOLL_CTL_DEL, sockfd, &ev) == -1) return netc_error(POLL_FD);
        }
        else if (ev.events & EPOLLERR || ev.events & EPOLLHUP)
        {
            if (tcp_client_close(client, ev.events & EPOLLERR) != 0) return netc_error(CLOSE);
        }
#elif _WIN32
        WSAPOLLFD event = events[0];
        SOCKET sockfd = event.fd;

        if (event.revents & POLLIN && client->on_data != NULL)
                client->on_data(client);
        else if (event.revents & POLLOUT)
        {
            int error = 0;
            socklen_t len = sizeof(error);
            int result = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);

            if (result == -1 || error != 0)
                return netc_error(HANGUP);
            else if (client->on_connect != NULL)
                client->on_connect(client);
        }
        else if (event.revents & POLLERR || event.revents & POLLHUP)
        {
            if (tcp_client_close(client, event.revents & POLLERR) != 0) return netc_error(CLOSE);
        };
#elif __APPLE__
        struct kevent ev = events[0];
        socket_t sockfd = ev.ident;

        if (ev.flags & EV_ERROR || ev.flags & EV_EOF)
        {
            if (tcp_client_close(client, ev.flags & EV_ERROR) != 0) return netc_error(CLOSE);
        }
        else if (ev.filter == EVFILT_READ && client->on_data != NULL)
            client->on_data(client);
        else if (ev.filter == EVFILT_WRITE)
        {
            int error = 0;
            socklen_t len = sizeof(error);
            int result = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);

            if (result == -1 || error != 0)
                return netc_error(HANGUP);
            else if (client->on_connect != NULL) client->on_connect(client);

            // deregister event
            EV_SET(&ev, sockfd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
            if (kevent(pfd, &ev, 1, NULL, 0, NULL) == -1) return netc_error(POLL_FD);
        }
#endif
    };

    return 0;
};

int tcp_client_init(struct tcp_client *client, struct sockaddr addr, int non_blocking)
{
    if (client == NULL) return -1; 
    
    client->sockaddr = addr;
    int protocol = addr.sa_family;

    client->sockfd = socket(protocol, SOCK_STREAM, 0); // IPv4, TCP, 0
    if (client->sockfd == -1) return netc_error(SOCKET_C);

    client->listening = 0;

    if (non_blocking == 0) return 0; 
    if (socket_set_non_blocking(client->sockfd) != 0) return netc_error(FD_CTL);

    /** Register events for a nonblocking socket. */
#ifdef __linux__
    client->pfd = epoll_create1(0);
    if (client->pfd == -1) return netc_error(EVCREATE);

    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP;
    ev.data.fd = client->sockfd;

    if (epoll_ctl(client->pfd, EPOLL_CTL_ADD, client->sockfd, &ev) == -1) return netc_error(POLL_FD);
#elif _WIN32
#elif __APPLE__
    client->pfd = kqueue();
    if (client->pfd == -1) return netc_error(EVCREATE);

    struct kevent ev[2];
    int events = 0;
    EV_SET(&ev[events++], client->sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
    EV_SET(&ev[events++], client->sockfd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
    if (kevent(client->pfd, ev, events, NULL, 0, NULL) == -1) return netc_error(POLL_FD);
#endif

    return 0;
};

int tcp_client_connect(struct tcp_client *client)
{
    socket_t sockfd = client->sockfd;
    struct sockaddr addr = client->sockaddr;
    socklen_t addrlen = sizeof(addr);

    int result = connect(sockfd, &addr, addrlen);
    if (result == -1 && errno != EINPROGRESS) return netc_error(CONNECT);

    return 0;
};

int tcp_client_send(struct tcp_client *client, char *message, size_t msglen, int flags)
{
    socket_t sockfd = client->sockfd;

    int result = send(sockfd, message, msglen, flags);
    if (result == -1) netc_error(BADSEND);

    return result;
};

int tcp_client_receive(struct tcp_client *client, char *message, size_t msglen, int flags)
{
    socket_t sockfd = client->sockfd;

    int result = recv(sockfd, message, msglen, flags);
    if (result == -1) netc_error(BADRECV);

    return result;
};

int tcp_client_close(struct tcp_client *client, bool is_error)
{
    if (client->on_disconnect != NULL) client->on_disconnect(client, is_error);

    socket_t sockfd = client->sockfd;
    client->listening = 0;
    
#ifdef _WIN32
    int result = closesocket(sockfd);
#else
    int result = close(sockfd);
#endif

    if (result == -1) return netc_error(CLOSE);

    return 0;
};

我运行了这个程序,希望由于传入数据事件而返回,但即使发送了传入数据,它仍然会阻止。epoll_wait()

套接字 TCP IO 非阻塞 epoll

评论


答: 暂无答案