У меня проблема с моим кодом, я пытаюсь создать HTTP-сервер, используя C++ 98 в MacOS, и я ожидаю, что блок чтения будет выполнен перед блоком записи, но происходит обратное, и я не не знаю почему. Поэтому я ожидаю сначала прочитать запрос, а затем отправить ответ. а здесь все наоборот это мой код:
/************************ 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;
}
}
}
и класс очереди:
/***********************************************************************
* 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, я ожидаю прочитать запрос, а затем отправить ответ
Для того, чтобы можно было читать из сокета, сначала другая сторона должна определить, что соединение завершено, затем она должна составить свой запрос и передать его по проводу. Затем ваша сторона должна получить информацию и обработать ее. Только тогда можно читать из сокета.
Чтобы можно было писать в сокет, вы должны определить, что соединение завершено. Вот и все. Если соединение завершено, можно писать.
Неудивительно, что запись в сокет возможна раньше, чем чтение из него. Вопрос в том, почему ваш код проверяет возможность записи в сокет, когда вы не хотите писать в сокет? Кроме того, почему ваш код пишет в сокет только потому, что это возможно, даже если вы даже не получили запрос с другой стороны?
Если у вас есть данные для записи на другую сторону, потому что вы еще даже не получили запрос от другой стороны, зачем вы проверяете, можно ли писать в сокет? Вы не хотите писать, даже если это возможно, так зачем проверять?
@REVERSI Это просто удача. Вы ожидаете двух вещей, которые могут произойти в любом порядке, и иногда они происходят в том порядке, в котором ваш код обрабатывает их правильно, а иногда они происходят в том порядке, в котором ваш код обрабатывает их неправильно.
Большое спасибо, что ответили мне, последний вопрос, пожалуйста, исходя из вопросов, которые вы задали, я понимаю, что могу принять соединение, открыть его только для ЧТЕНИЯ, затем, когда я прочитаю запрос, я открою его для записи тоже . и это работает, теперь я обрабатываю запрос, а затем отправляю ответ, я просто хочу спросить ваше мнение по этому поводу, пожалуйста
Это правильно. Проверяйте только, доступен ли сокет для записи, если вы хотите писать в него.
Большое спасибо за вашу помощь, ваши вопросы помогли мне определить новый фактор, который может помочь, я обнаружил, что при отладке с использованием lldb все работает, как и ожидалось, я получаю запрос и отправляю ответ. но обычное выполнение кода вызывает проблему, есть идеи по этому поводу, пожалуйста?