From a9b1843ebad23fbc241ddfa1cee8fcf196b9fb39 Mon Sep 17 00:00:00 2001 From: LuckyLaszlo Date: Tue, 12 Jul 2022 00:51:43 +0200 Subject: [PATCH 1/3] basic tcp connection. --- .gitignore | 1 + Makefile | 16 +++---- include_memo.hpp | 4 +- srcs/Webserv.cpp | 108 ++++++++++++++++++++++++++++++++++++++++++++--- srcs/Webserv.hpp | 19 +++++++-- srcs/main.cpp | 21 +++++++++ 6 files changed, 149 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index a9f2ccf..0137954 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ Thumbs.db ubuntu_tester ubuntu_cgi_tester +webserv diff --git a/Makefile b/Makefile index 9c67075..17f51b6 100644 --- a/Makefile +++ b/Makefile @@ -5,19 +5,19 @@ CXX = c++ CXXFLAGS = -Wall -Wextra -Werror CXXFLAGS += -std=c++98 CXXFLAGS += -I$(HEADERS_D) -#CXXFLAGS += -g +CXXFLAGS += -g #CXXFLAGS += -O3 -SHELL = /bin/zsh +#SHELL = /bin/zsh VPATH = $(DIR_SRCS) -DIR_SRCS = tests +DIR_SRCS = srcs -HEADERS_D = ./templates -HEADERS = +HEADERS_D = ./srcs +HEADERS = Webserv.hpp DEPENDENCIES = $(HEADERS:%=$(HEADERS_D)/%) -SRCS = main.cpp +SRCS = main.cpp Webserv.cpp DIR_OBJS = builds OBJS = $(SRCS:%.cpp=$(DIR_OBJS)/%.o) @@ -28,7 +28,7 @@ OBJS = $(SRCS:%.cpp=$(DIR_OBJS)/%.o) all: $(NAME) -$(DIR_OBJS)/%.o: $(SRCS) | $(DIR_OBJS) +$(DIR_OBJS)/%.o: %.cpp | $(DIR_OBJS) $(CXX) $(CXXFLAGS) -c $< -o $@ $(DIR_OBJS): @@ -37,7 +37,7 @@ $(DIR_OBJS): $(OBJS): $(DEPENDENCIES) #$(OBJS): $(DEPENDENCIES) Makefile -$(NAME) : +$(NAME) : $(OBJS) $(CXX) $(OBJS) -o $(NAME) clean: diff --git a/include_memo.hpp b/include_memo.hpp index 2ed2454..79cc3de 100644 --- a/include_memo.hpp +++ b/include_memo.hpp @@ -3,7 +3,9 @@ # include # include // errno # include // perror - +# include +# include +# include // close // -------------------------------------------- // Verifier si les fonctions sont dispos dans des headers C++ diff --git a/srcs/Webserv.cpp b/srcs/Webserv.cpp index d446827..884218a 100644 --- a/srcs/Webserv.cpp +++ b/srcs/Webserv.cpp @@ -3,25 +3,119 @@ Webserv::Webserv() { - _socket_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + std::cout << "Server init\n"; + _socket_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); if (_socket_fd == -1) { - perror("socket(): "); + ::perror("err socket(): "); throw std::runtime_error("Socket init"); } } -Webserv::Webserv(Webserv const &src) +/* Webserv::Webserv(Webserv const &src) { -} +} */ Webserv::~Webserv() { - + std::cout << "Server destroyed\n"; } -Webserv & Webserv::operator=(Webserv const &rhs) +/* Webserv & Webserv::operator=(Webserv const &rhs) { -} \ No newline at end of file +} */ + + /////////////// + // Functions // + +void Webserv::bind(in_port_t port) +{ + // cast invalid ? how to ? + // const struct sockaddr* cast_test = static_cast(addr); + + struct sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = ::htons(port); + addr.sin_addr.s_addr = ::htonl(INADDR_ANY); // htonl useless with 0 value (INADDR_ANY) + + if (::bind(_socket_fd, (const sockaddr*)&addr, sizeof addr) == -1) + { + ::perror("err bind(): "); + throw std::runtime_error("Socket bind"); + } +} + +void Webserv::listen(unsigned int max_connections) +{ + if (::listen(_socket_fd, max_connections) == -1) + { + ::perror("err listen(): "); + throw std::runtime_error("Socket listen"); + } +} + +#define BUFSIZE 8192 +#define MSG_TEST "Le Webserv / 20 =D\n" +#define MSG_BOUNCE "bounced properly ;)\n" // placeholder + +void Webserv::start() +{ + struct sockaddr_in addr; + socklen_t addr_len; + int accepted_fd; + + // poll (or equivalent) + struct pollfd poll_s; + poll_s.fd = _socket_fd; + poll_s.events = POLLIN; + + char buf[BUFSIZE]; // WIP buffer. need to try with std::vector or std::string. + int ret; + + std::cout << "Server started\n"; + while (1) + { + std::cout << "----------\n"; + std::cout << "poll()\n"; // poll (or equivalent) + ::poll(&poll_s, 1, -1); + + std::cout << "accept()\n"; + addr_len = sizeof addr; + accepted_fd = ::accept(_socket_fd, (sockaddr*)&addr, &addr_len); + if (accepted_fd == -1) + { + ::perror("err accept(): "); + continue; + } + + // "Your server must never block and the client can be bounced properly if necessary". + // NO-Block OK, but how to handle it ? Miss the bouncing part. + ::fcntl(accepted_fd, F_SETFL, O_NONBLOCK); + + std::cout << "recv()\n"; + ret = ::recv(accepted_fd, buf, BUFSIZE, 0); + if (ret == -1) + { + ::perror("err recv(): "); + if (::send(accepted_fd, MSG_BOUNCE, sizeof MSG_BOUNCE - 1, 0) == -1) + ::perror("err send(): "); + ::close(accepted_fd); + continue; + } + /* + if (ret == BUFSIZE) + // send error like "request too long" to client + */ + buf[ret] = '\0'; + + std::cout << "send()\n"; + if (::send(accepted_fd, buf, ret, 0) == -1) // echo the read + ::perror("err send(): "); + if (::send(accepted_fd, MSG_TEST, sizeof MSG_TEST - 1, 0) == -1) + ::perror("err send(): "); + + ::close(accepted_fd); + } +} diff --git a/srcs/Webserv.hpp b/srcs/Webserv.hpp index fed19a2..01f87f1 100644 --- a/srcs/Webserv.hpp +++ b/srcs/Webserv.hpp @@ -8,17 +8,28 @@ # include // perror # include # include +# include // close +# include // cout, cin # include // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname +# include // sockaddr_in +// # include // usefull for what ? +# include // htonl, htons, ntohl, ntohs, inet_addr + +# include // poll +# include // fcntl class Webserv { public: - //Webserv(Placeholder); Webserv(); - Webserv(Webserv const &src); + // Webserv(Webserv const &src); ~Webserv(); - Webserv &operator=(Webserv const &rhs); + // Webserv &operator=(Webserv const &rhs); + + void bind(in_port_t port); + void listen(unsigned int max_connections); + void start(); private: int _socket_fd; @@ -27,4 +38,4 @@ class Webserv }; -#endif \ No newline at end of file +#endif diff --git a/srcs/main.cpp b/srcs/main.cpp index 8b13789..7fe8d61 100644 --- a/srcs/main.cpp +++ b/srcs/main.cpp @@ -1 +1,22 @@ +#include +#include +#include +#include + +int main(void) +{ + try + { + Webserv serv; + + serv.bind(80); + serv.listen(512); // 512 max connections arbitrary + serv.start(); + } + catch (std::exception& e) + { + std::cout << e.what() << '\n'; + } + return (0); +} From eb5e719750666568c67b4a35b1b6c0b19ceccb61 Mon Sep 17 00:00:00 2001 From: LuckyLaszlo Date: Tue, 12 Jul 2022 19:37:47 +0200 Subject: [PATCH 2/3] change to poll() call --- Makefile | 2 +- srcs/Webserv.cpp | 25 +++++++++++-------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index 17f51b6..1666787 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ NAME = webserv CXX = c++ -CXXFLAGS = -Wall -Wextra -Werror +CXXFLAGS = -Wall -Wextra #-Werror CXXFLAGS += -std=c++98 CXXFLAGS += -I$(HEADERS_D) CXXFLAGS += -g diff --git a/srcs/Webserv.cpp b/srcs/Webserv.cpp index 884218a..ed33b11 100644 --- a/srcs/Webserv.cpp +++ b/srcs/Webserv.cpp @@ -4,7 +4,8 @@ Webserv::Webserv() { std::cout << "Server init\n"; - _socket_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + // _socket_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + _socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); if (_socket_fd == -1) { ::perror("err socket(): "); @@ -38,7 +39,7 @@ void Webserv::bind(in_port_t port) struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = ::htons(port); - addr.sin_addr.s_addr = ::htonl(INADDR_ANY); // htonl useless with 0 value (INADDR_ANY) + addr.sin_addr.s_addr = ::htonl(INADDR_ANY); // htonl useless with 0 value (INADDR_ANY) ? if (::bind(_socket_fd, (const sockaddr*)&addr, sizeof addr) == -1) { @@ -65,22 +66,14 @@ void Webserv::start() struct sockaddr_in addr; socklen_t addr_len; int accepted_fd; - - // poll (or equivalent) - struct pollfd poll_s; - poll_s.fd = _socket_fd; - poll_s.events = POLLIN; - - char buf[BUFSIZE]; // WIP buffer. need to try with std::vector or std::string. - int ret; + struct pollfd poll_s; + char buf[BUFSIZE]; // WIP buffer. need to try with std::vector or std::string. + int ret; std::cout << "Server started\n"; while (1) { std::cout << "----------\n"; - std::cout << "poll()\n"; // poll (or equivalent) - ::poll(&poll_s, 1, -1); - std::cout << "accept()\n"; addr_len = sizeof addr; accepted_fd = ::accept(_socket_fd, (sockaddr*)&addr, &addr_len); @@ -89,11 +82,15 @@ void Webserv::start() ::perror("err accept(): "); continue; } - // "Your server must never block and the client can be bounced properly if necessary". // NO-Block OK, but how to handle it ? Miss the bouncing part. ::fcntl(accepted_fd, F_SETFL, O_NONBLOCK); + std::cout << "poll()\n"; // poll (or equivalent) + poll_s.fd = accepted_fd; + poll_s.events = POLLIN; // We need a way to valid POLLOUT and POLLIN at the same time (both, not one of them) + ::poll(&poll_s, 1, -1); + std::cout << "recv()\n"; ret = ::recv(accepted_fd, buf, BUFSIZE, 0); if (ret == -1) From cb8cc22c67d36863885d3653ee6c32bc70be92d3 Mon Sep 17 00:00:00 2001 From: LuckyLaszlo Date: Wed, 20 Jul 2022 20:19:13 +0200 Subject: [PATCH 3/3] example for new usage of poll() --- srcs/Webserv.cpp | 125 +++++++++++++++++++++++++++++++---------------- srcs/Webserv.hpp | 2 + 2 files changed, 85 insertions(+), 42 deletions(-) diff --git a/srcs/Webserv.cpp b/srcs/Webserv.cpp index ed33b11..8bd7259 100644 --- a/srcs/Webserv.cpp +++ b/srcs/Webserv.cpp @@ -4,11 +4,11 @@ Webserv::Webserv() { std::cout << "Server init\n"; - // _socket_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); - _socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); + _socket_fd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0); + //_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0); if (_socket_fd == -1) { - ::perror("err socket(): "); + std::perror("err socket(): "); throw std::runtime_error("Socket init"); } } @@ -37,13 +37,14 @@ void Webserv::bind(in_port_t port) // const struct sockaddr* cast_test = static_cast(addr); struct sockaddr_in addr; + std::memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_port = ::htons(port); addr.sin_addr.s_addr = ::htonl(INADDR_ANY); // htonl useless with 0 value (INADDR_ANY) ? if (::bind(_socket_fd, (const sockaddr*)&addr, sizeof addr) == -1) { - ::perror("err bind(): "); + std::perror("err bind(): "); throw std::runtime_error("Socket bind"); } } @@ -52,7 +53,7 @@ void Webserv::listen(unsigned int max_connections) { if (::listen(_socket_fd, max_connections) == -1) { - ::perror("err listen(): "); + std::perror("err listen(): "); throw std::runtime_error("Socket listen"); } } @@ -66,53 +67,93 @@ void Webserv::start() struct sockaddr_in addr; socklen_t addr_len; int accepted_fd; - struct pollfd poll_s; - char buf[BUFSIZE]; // WIP buffer. need to try with std::vector or std::string. + char buf[BUFSIZE+1]; // WIP buffer. need to try with std::vector or std::string. int ret; + int timeout = 3000; + int i = 1; + int nfds = 1; + int ret_nfds; + + _poll_s[0].fd = _socket_fd; + _poll_s[0].events = POLLIN; + std::cout << "Server started\n"; - while (1) + int i_loop = 0; + ++i_loop; + std::cout << i_loop << "----------\n"; + std::cout << "loop poll()\n"; + while ( (ret_nfds = ::poll(_poll_s, nfds, timeout)) != -1) { - std::cout << "----------\n"; - std::cout << "accept()\n"; - addr_len = sizeof addr; - accepted_fd = ::accept(_socket_fd, (sockaddr*)&addr, &addr_len); - if (accepted_fd == -1) + i = 1; + while (i < nfds) { - ::perror("err accept(): "); - continue; + if (ret_nfds == 0) + { + std::cout << "timeout\n"; + ::close(_poll_s[i].fd); + _poll_s[i].fd = -1; + --nfds; + } + + else if (_poll_s[i].revents & POLLIN) // READ + { + std::cout << "recv()\n"; + ret = ::recv(_poll_s[i].fd, buf, BUFSIZE, 0); + if (ret == -1) + { + std::perror("err recv(): "); + if (::send(_poll_s[i].fd, MSG_BOUNCE, sizeof MSG_BOUNCE - 1, 0) == -1) + std::perror("err send(): "); + + ::close(_poll_s[i].fd); + _poll_s[i].fd = -1; + --nfds; + continue; + } + /* + if (ret == BUFSIZE) + // send error like "request too long" to client + */ + buf[ret] = '\0'; + _poll_s[i].events = POLLOUT; + } + + else if (_poll_s[i].revents & POLLOUT) // WRITE + { + std::cout << "send()\n"; + if (::send(_poll_s[i].fd, buf, ret, 0) == -1) // echo the read + std::perror("err send(): "); + if (::send(_poll_s[i].fd, MSG_TEST, sizeof MSG_TEST - 1, 0) == -1) + std::perror("err send(): "); + + ::close(_poll_s[i].fd); + _poll_s[i].fd = -1; + --nfds; + } + + ++i; } - // "Your server must never block and the client can be bounced properly if necessary". - // NO-Block OK, but how to handle it ? Miss the bouncing part. - ::fcntl(accepted_fd, F_SETFL, O_NONBLOCK); - std::cout << "poll()\n"; // poll (or equivalent) - poll_s.fd = accepted_fd; - poll_s.events = POLLIN; // We need a way to valid POLLOUT and POLLIN at the same time (both, not one of them) - ::poll(&poll_s, 1, -1); - std::cout << "recv()\n"; - ret = ::recv(accepted_fd, buf, BUFSIZE, 0); - if (ret == -1) + if ((_poll_s[0].fd == _socket_fd) && (_poll_s[0].revents & POLLIN)) // ACCEPT { - ::perror("err recv(): "); - if (::send(accepted_fd, MSG_BOUNCE, sizeof MSG_BOUNCE - 1, 0) == -1) - ::perror("err send(): "); - ::close(accepted_fd); - continue; + std::cout << "accept()\n"; + addr_len = sizeof addr; + accepted_fd = ::accept(_socket_fd, (sockaddr*)&addr, &addr_len); + if (accepted_fd == -1) + { + std::perror("err accept(): "); + continue; + } + ::fcntl(accepted_fd, F_SETFL, O_NONBLOCK); + _poll_s[nfds].fd = accepted_fd; + _poll_s[nfds].events = POLLIN; + ++nfds; } - /* - if (ret == BUFSIZE) - // send error like "request too long" to client - */ - buf[ret] = '\0'; - - std::cout << "send()\n"; - if (::send(accepted_fd, buf, ret, 0) == -1) // echo the read - ::perror("err send(): "); - if (::send(accepted_fd, MSG_TEST, sizeof MSG_TEST - 1, 0) == -1) - ::perror("err send(): "); - ::close(accepted_fd); + ++i_loop; + std::cout << i_loop << "----------\n"; + std::cout << "loop poll()\n"; } } diff --git a/srcs/Webserv.hpp b/srcs/Webserv.hpp index 01f87f1..7c3fd4f 100644 --- a/srcs/Webserv.hpp +++ b/srcs/Webserv.hpp @@ -10,6 +10,7 @@ # include # include // close # include // cout, cin +# include // memset # include // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname # include // sockaddr_in @@ -33,6 +34,7 @@ class Webserv private: int _socket_fd; + struct pollfd _poll_s[42]; // 42 PLACEHOLDER std::map _request; std::map _response;