在 kqueue 中写入事件之前接收到读取事件

read event is received before the write one in kqueue

我的代码有问题,我正在尝试在 MacOS 上使用 C++ 98 创建一个 HTTP 服务器,我希望读取块在写入块之前执行,但相反正在发生,我不知道为什么。 所以我希望先阅读请求,然后发送响应。但这里发生了相反的情况, 这是我的代码:

/************************ CONSTRUCTORS/DESTRUCTOR ************************/
webserv::Server::Server(int addr, int port)
{
    this->sock.create_socket(AF_INET, SOCK_STREAM, 0);
    this->sock.bind_socket(addr, port);
    this->sock.listen_socket(__MAX_BACKLOG__);
}

webserv::Server::~Server() {}

/************************ MEMBER FUNCTION ************************/
void    webserv::Server::lunch()
{
    this->kq.create_event(this->sock.getSocket(), EVFILT_READ);
    while (1)
        this->_lunch_worker();
}

void    webserv::Server::_lunch_worker(void)
{
    int ev_count = this->kq.get_event();
    static const char* index_html = "HTTP/1.0 200 OK\r\n" \
                                    "Content-Length: 86\r\n\r\n" \
                                    "<!DOCTYPE html>" \
                                    "<html><head>Hello, world!</head><body><h1>cdn-ish...</h1></body></html>\r\n";
    char buf[10000];

    for (int i = 0; i < ev_count; i++) {
        int fd = this->kq.get_fd(i);

        if (fd < 0) continue;
        if (fd == this->sock.getSocket()) {
            
            int clientaddr_size = sizeof(this->sock.getAddress());

            int clientfd = this->sock.accept_socket();
            if (clientfd < 0) {
                perror("accept");
                close(fd);
                return ;
            }
            this->kq.create_event(clientfd, EVFILT_READ);
            if (fcntl(clientfd, F_SETFL, O_NONBLOCK) < 0) {
                perror("fcntl");
                close(clientfd);
                close(fd);
            }
            this->kq.create_event(clientfd, EVFILT_WRITE, EV_ADD | EV_ONESHOT);
        // EXPECTING THIS BLOCK TO BE CHECKED/EXECUTED FIRST
        // BUT INSTEAD THE NEXT BLOCK (WRITE BLOCK) IS EXECUTED FIRST
        // THEN IN THE SECOND ITERATION THE READ BLOCK IS BEING EXECUTED
        } else if (this->kq.is_read_available(i)) {
            int len;
            std::cout << "READ" << std::endl;
            // memset(buf, 0, sizeof(buf));
            if ((len = recv(fd, buf, sizeof(buf), 0)) == 0) {
                std::cout << "READ: FD CLOSED = " << fd << std::endl;
                close(fd);
            }
            else if (len > 0)
            {
            }
            std::cout << "READ: LEN = " << len << std::endl;
        } else if (this->kq.is_write_available(i)) {
            int len = 0;
            if ((len = send(fd, index_html, strlen(index_html), 0)) != 0) {
            }
            std::cout << "WRITE: LEN = " << len << std::endl;
        }
    }
}

和kqueue class:

/***********************************************************************
* FILENAME :        Kqueue.cpp
*
* DESCRIPTION :
*       This File is the implementation of the functions
*       Defined in Kqueue.hpp
*
**/

# include "./Kqueue.hpp"
# include "../../OutputColors.hpp"

/************************ CONSTRUCTORS/DESTRUCTOR ************************/
webserv::Kqueue::Kqueue()
{
    this->_kq = kqueue();
    std::cout << "KQUEUE CREATED" << std::endl;
    this->test_error(this->_kq, "Creating Kqueue :");
    this->_n_ev = 0;
}

webserv::Kqueue::~Kqueue()
{
    close(this->_kq);
}

/************************ MEMBER FUNCTIONS ************************/
void    webserv::Kqueue::set_event(int fd, int filter, int flags, void *udata)
{
    EV_SET(&this->_ev_set, fd, filter, flags, 0, 0, udata);
}

void    webserv::Kqueue::add_event(void)
{
    int ret;

    ret = kevent(this->_kq, &this->_ev_set, 1, NULL, 0, NULL);
    this->test_error(ret, "Kqueue/add_even functions");
}

int     webserv::Kqueue::get_event(void)
{
    this->_n_ev = kevent(this->_kq, NULL, 0, this->_ev_list, __EV_LIST_SIZE__, NULL);
    this->test_error(this->_n_ev, "Kqueue/get_event function:");
    return (this->_n_ev);
}

void    webserv::Kqueue::create_event(int fd, int filter, int flags, void *udata)
{
    this->set_event(fd, filter, flags, udata);
    this->add_event();
}

bool    webserv::Kqueue::isEOF(int index)
{
    if (this->_n_ev <= index)
        this->test_error(-1, "Kqueue/isEOF function:");
    return (this->_ev_list[index].flags & EV_EOF);
}

bool    webserv::Kqueue::is_read_available(int index)
{
    if (this->_n_ev <= index)
        this->test_error(-1, "Kqueue/is_read_available function:");
    return (this->_ev_list[index].filter == EVFILT_READ);
}

bool    webserv::Kqueue::is_write_available(int index)
{
    if (this->_n_ev <= index)
        this->test_error(-1, "Kqueue/is_write_available function:");
    return (this->_ev_list[index].filter == EVFILT_WRITE);
}

void    webserv::Kqueue::test_error(int fd, const std::string &str)
{
    if (fd < 0)
    {
        std::cerr << RED << str << " ";
        perror("The following error occured: ");
        std::cerr << RESET;
        exit(EXIT_FAILURE);
    }
}

/************************ GETTERS/SETTERS ************************/
struct kevent   *webserv::Kqueue::get_event_list()
{
    return (this->_ev_list);
}

int &webserv::Kqueue::get_kq()
{
    return (this->_kq);
}

int             webserv::Kqueue::get_fd(int index)
{
    if (this->_n_ev <= index)
        this->test_error(-1, "Kqueue/get_ev_list function:");
    return (this->_ev_list[index].ident);
}

void    webserv::Kqueue::set_kqueue(int fd)
{
    this->_kq = fd;
}

有谁知道为什么在我的例子中 WRITE 在 READ 之前,我希望读取请求,然后发送响应

为了能够从套接字中读取数据,首先对方必须检测到连接已完成,然后它必须编写查询并将其发送到网络上。然后你方必须接收信息并进行处理。只有这样才能从套接字中读取。

为了能够写入套接字,您必须检测连接是否完成。而已。如果连接完成,就可以写入了。

毫不奇怪,可以先写入套接字,然后再从中读取。问题是——当您不想写入套接字时,为什么您的代码要检查是否可以写入套接字?另外,为什么你的代码写入套接字只是因为即使你甚至没有收到来自另一端的查询也可以这样做?

如果你没有数据可以写入另一端,因为你甚至还没有收到来自另一端的查询,你为什么要检查是否可以写入套接字?能写也不想写,为什么要查?