separate main tests in differents files and added comparison in readme
This commit is contained in:
@@ -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(): ");
|
||||
@@ -12,11 +13,21 @@ Webserv::Webserv()
|
||||
}
|
||||
}
|
||||
|
||||
/* Webserv::Webserv(Webserv const &src)
|
||||
{
|
||||
|
||||
} */
|
||||
|
||||
Webserv::~Webserv()
|
||||
{
|
||||
std::cout << "Server destroyed\n";
|
||||
}
|
||||
|
||||
/* Webserv & Webserv::operator=(Webserv const &rhs)
|
||||
{
|
||||
|
||||
} */
|
||||
|
||||
|
||||
///////////////
|
||||
// Functions //
|
||||
@@ -56,36 +67,31 @@ 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);
|
||||
if (accepted_fd == -1)
|
||||
{
|
||||
::perror("err accept(): ");
|
||||
//throw WebservError();
|
||||
continue;
|
||||
}
|
||||
std::cout << "accept()\n";
|
||||
|
||||
// "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)
|
||||
|
||||
151
srcs/main.cpp
151
srcs/main.cpp
@@ -1,7 +1,4 @@
|
||||
|
||||
/*
|
||||
luke version
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
@@ -13,8 +10,8 @@ int main(void)
|
||||
{
|
||||
Webserv serv;
|
||||
|
||||
// https://security.stackexchange.com/questions/169213/how-to-chose-a-port-to-run-an-application-on-localhost
|
||||
serv.bind(8080);
|
||||
// https://security.stackexchange.com/questions/169213/how-to-chose-a-port-to-run-an-application-on-localhost
|
||||
serv.bind(4040);
|
||||
serv.listen(512); // 512 max connections arbitrary
|
||||
serv.start();
|
||||
}
|
||||
@@ -24,112 +21,46 @@ int main(void)
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
wip hugo version
|
||||
|
||||
|
||||
______
|
||||
listen_fd = SOCKET() : create a listening socket
|
||||
__________
|
||||
SETSOCKOPT() : Allow socket descriptor to be reuseable
|
||||
_____
|
||||
IOCTL() : set listen_fd and all incoming socket to be non-blocking
|
||||
____
|
||||
BIND(port) : associate listen_fd to a port
|
||||
______
|
||||
LISTEN(nb_queue) : queue the incoming connections to listen_fd, up to a chosen number
|
||||
|
||||
fds[1] = listen_fd
|
||||
|
||||
loop
|
||||
. ____
|
||||
. POLL(fds[]) :
|
||||
.
|
||||
. loop through fds[]
|
||||
. .
|
||||
. . POLLIN && listen_fd ? : readable socket and this is the listening one
|
||||
. . . ______
|
||||
. . . new_fd = ACCEPT() : extract first connection request in queue of listen_fd
|
||||
. . . and creates a new socket that is connected
|
||||
. . .
|
||||
. . . fds[] += new_fd
|
||||
. .
|
||||
. . POLLIN ? : readable socket and this is an active one
|
||||
. . . ____
|
||||
. . . RECV() : read data in socket created by accept()
|
||||
. . . ____
|
||||
. . . SEND() : write data in socket created by accept()
|
||||
|
||||
loop through fds[] :
|
||||
. _____
|
||||
. CLOSE(fds[])
|
||||
|
||||
|
||||
*/
|
||||
# include <map>
|
||||
# include <exception>
|
||||
# include <stdexcept>
|
||||
# include <fcntl.h> // fcntl
|
||||
|
||||
# include <unistd.h> // close
|
||||
# include <stdlib.h> // exit
|
||||
# include <iostream> // cout, cin
|
||||
# include <cerrno> // errno
|
||||
# include <cstdio> // perror
|
||||
# include <string.h> // memset
|
||||
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
||||
# include <netinet/in.h> // sockaddr_in
|
||||
# include <arpa/inet.h> // inet_ntoa, inet_addr, htonl, htons, ntohl, ntohs
|
||||
# include <poll.h> // poll
|
||||
|
||||
# define INVALID -1
|
||||
# define BACKLOG 20
|
||||
// test with curl http://localhost:PORT (replace PORT by the port number you choose below)
|
||||
# define PORT 4040
|
||||
|
||||
int main()
|
||||
{
|
||||
struct sockaddr_in my_addr;
|
||||
struct sockaddr_in their_addr;
|
||||
int sckt_fd;
|
||||
int axpt_fd;
|
||||
int lstn;
|
||||
socklen_t addr_len;
|
||||
|
||||
// INIT SOCKET
|
||||
sckt_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
if (sckt_fd == INVALID)
|
||||
{
|
||||
perror("err socket(): ");
|
||||
return 0;
|
||||
}
|
||||
std::cout << "server init\n";
|
||||
|
||||
// BIND IT
|
||||
memset(&my_addr, 0, sizeof(my_addr));
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_port = htons(PORT);
|
||||
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
// my_addr.sin_addr.s_addr = inet_addr("10.12.110.57");
|
||||
bind(sckt_fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
|
||||
if (sckt_fd == INVALID)
|
||||
{
|
||||
perror("err bind(): ");
|
||||
return 0;
|
||||
}
|
||||
std::cout << "socket bind to port: " << PORT << "\n";
|
||||
// https://beej.us/guide/bgnet/html/index-wide.html#cb29 :
|
||||
//
|
||||
// Sometimes, you might notice, you try to rerun a server and bind() fails, claiming “Address already in use.” What does that mean? Well, a little bit of a socket that was connected is still hanging around in the kernel, and it’s hogging the port. You can either wait for it to clear (a minute or so), or add code to your program allowing it to reuse the port, like this:
|
||||
//
|
||||
// int yes=1;
|
||||
//
|
||||
// // lose the pesky "Address already in use" error message
|
||||
// if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes) == -1) {
|
||||
// perror("setsockopt");
|
||||
// exit(1);
|
||||
// }
|
||||
|
||||
// LISTEN ON IT
|
||||
lstn = listen(sckt_fd, BACKLOG);
|
||||
if (lstn == INVALID)
|
||||
{
|
||||
perror("err listen(): ");
|
||||
return 0;
|
||||
}
|
||||
std::cout << "server listening\n";
|
||||
|
||||
// ACCEPT INCOMING CONNECT
|
||||
while (1)
|
||||
{
|
||||
|
||||
addr_len = sizeof(their_addr);
|
||||
axpt_fd = accept(sckt_fd, (sockaddr*)&their_addr, &addr_len);
|
||||
if (axpt_fd == INVALID)
|
||||
{
|
||||
perror("err accept(): ");
|
||||
continue;
|
||||
}
|
||||
std::cout << "server accepted a socket from: "
|
||||
<< inet_ntoa(their_addr.sin_addr)
|
||||
<< "\n";
|
||||
|
||||
// // with a fork
|
||||
// if (!fork()) // child process
|
||||
// {
|
||||
// close(sckt_fd); // child doesn't need the listener
|
||||
// if (send(axpt_fd, "hello world !", 13, 0) == INVALID)
|
||||
// perror("err send(): ");
|
||||
// close(axpt_fd);
|
||||
// exit(0);
|
||||
// }
|
||||
// close(axpt_fd); // parent doesn't need this
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
163
srcs/main_1.cpp
Normal file
163
srcs/main_1.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
|
||||
# include <unistd.h> // close
|
||||
# include <stdlib.h> // exit
|
||||
# include <iostream> // cout, cin
|
||||
# include <cerrno> // errno
|
||||
# include <cstdio> // perror
|
||||
# include <string.h> // memset
|
||||
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
||||
# include <netinet/in.h> // sockaddr_in
|
||||
# include <arpa/inet.h> // inet_ntoa, inet_addr, htonl, htons, ntohl, ntohs
|
||||
# include <poll.h> // poll
|
||||
# include <fcntl.h> // fcntl
|
||||
|
||||
# define INVALID -1
|
||||
# define BACKLOG 20
|
||||
// test with curl http://localhost:PORT (replace PORT by the port number you choose below)
|
||||
# define PORT 4040
|
||||
# define BUFSIZE 8192
|
||||
# define MSG_TEST "Le Webserv / 20 =D\n"
|
||||
# define MSG_BOUNCE "bounced properly ;)\n"
|
||||
|
||||
int main()
|
||||
{
|
||||
struct sockaddr_in my_addr, their_addr;
|
||||
int sckt_fd, axpt_fd, lstn, ret;
|
||||
socklen_t addr_len;
|
||||
struct pollfd poll_s;
|
||||
char buf[BUFSIZE];
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// SOCKET //
|
||||
// //
|
||||
// --------------------------------------------------------------------------- //
|
||||
sckt_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
if (sckt_fd == INVALID)
|
||||
{
|
||||
perror("err socket()");
|
||||
return 0;
|
||||
}
|
||||
std::cout << "server init\n";
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// BIND //
|
||||
// //
|
||||
// --------------------------------------------------------------------------- //
|
||||
memset(&my_addr, 0, sizeof(my_addr));
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_port = htons(PORT);
|
||||
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
// my_addr.sin_addr.s_addr = inet_addr("10.12.110.57");
|
||||
bind(sckt_fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
|
||||
if (sckt_fd == INVALID)
|
||||
{
|
||||
perror("err bind()");
|
||||
return 0;
|
||||
}
|
||||
std::cout << "socket bind to port: " << PORT << "\n";
|
||||
// https://beej.us/guide/bgnet/html/index-wide.html#cb29 :
|
||||
//
|
||||
// Sometimes, you might notice, you try to rerun a server and bind() fails, claiming “Address already in use.” What does that mean? Well, a little bit of a socket that was connected is still hanging around in the kernel, and it’s hogging the port. You can either wait for it to clear (a minute or so), or add code to your program allowing it to reuse the port, like this:
|
||||
//
|
||||
// int yes=1;
|
||||
//
|
||||
// // lose the pesky "Address already in use" error message
|
||||
// if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes) == -1) {
|
||||
// perror("setsockopt");
|
||||
// exit(1);
|
||||
// }
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// LISTEN //
|
||||
// mark the socket as passive, to receive incoming connection requests //
|
||||
// --------------------------------------------------------------------------- //
|
||||
lstn = listen(sckt_fd, BACKLOG);
|
||||
if (lstn == INVALID)
|
||||
{
|
||||
perror("err listen()");
|
||||
return 0;
|
||||
}
|
||||
std::cout << "server listening\n";
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// ACCEPT //
|
||||
// extract first connection request in the queue of the listening socket //
|
||||
// and creates a new socket that is connected, and returns it //
|
||||
// ----------------------------------------------------------------------- //
|
||||
addr_len = sizeof(their_addr);
|
||||
axpt_fd = accept(sckt_fd, (sockaddr*)&their_addr, &addr_len);
|
||||
if (axpt_fd == INVALID)
|
||||
{
|
||||
perror("err accept()");
|
||||
continue;
|
||||
}
|
||||
std::cout << "server accepted a socket from: "
|
||||
<< inet_ntoa(their_addr.sin_addr)
|
||||
<< "\n";
|
||||
|
||||
|
||||
/////////////////////
|
||||
// luke version
|
||||
/////////////////////
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// FCNTL //
|
||||
// manipulate the new socket fd to mark it as non-blocking //
|
||||
// ----------------------------------------------------------------------- //
|
||||
fcntl(axpt_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// POLL //
|
||||
// waits for an event on the axpt_fd : there is data to read //
|
||||
// ----------------------------------------------------------------------- //
|
||||
poll_s.fd = axpt_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 << "poll()\n";
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// RECV //
|
||||
// act as read, read the data of the socket created by accept() //
|
||||
// ----------------------------------------------------------------------- //
|
||||
ret = recv(axpt_fd, buf, BUFSIZE, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
perror("err recv()");
|
||||
if (send(axpt_fd, MSG_BOUNCE, sizeof MSG_BOUNCE - 1, 0) == -1)
|
||||
perror("err send()");
|
||||
close(axpt_fd);
|
||||
continue;
|
||||
}
|
||||
std::cout << "recv()\n";
|
||||
buf[ret] = '\0';
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// SEND //
|
||||
// act as write, write data in the socket created by accept() //
|
||||
// ----------------------------------------------------------------------- //
|
||||
std::cout << "send()\n";
|
||||
if (send(axpt_fd, buf, ret, 0) == -1) // echo the read
|
||||
perror("err send()");
|
||||
if (send(axpt_fd, MSG_TEST, sizeof MSG_TEST - 1, 0) == -1)
|
||||
perror("err send()");
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// CLOSE //
|
||||
// close the file descriptor of the connected socket //
|
||||
// ----------------------------------------------------------------------- //
|
||||
close(axpt_fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
163
srcs/main_hugo.cpp
Normal file
163
srcs/main_hugo.cpp
Normal file
@@ -0,0 +1,163 @@
|
||||
|
||||
# include <unistd.h> // close
|
||||
# include <stdlib.h> // exit
|
||||
# include <iostream> // cout, cin
|
||||
# include <cerrno> // errno
|
||||
# include <cstdio> // perror
|
||||
# include <string.h> // memset
|
||||
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
||||
# include <netinet/in.h> // sockaddr_in
|
||||
# include <arpa/inet.h> // inet_ntoa, inet_addr, htonl, htons, ntohl, ntohs
|
||||
# include <poll.h> // poll
|
||||
# include <fcntl.h> // fcntl
|
||||
|
||||
# define INVALID -1
|
||||
# define BACKLOG 20
|
||||
// test with curl http://localhost:PORT (replace PORT by the port number you choose below)
|
||||
# define PORT 4040
|
||||
# define BUFSIZE 8192
|
||||
# define MSG_TEST "Le Webserv / 20 =D\n"
|
||||
# define MSG_BOUNCE "bounced properly ;)\n"
|
||||
|
||||
int main()
|
||||
{
|
||||
struct sockaddr_in my_addr, their_addr;
|
||||
int sckt_fd, axpt_fd, lstn, ret;
|
||||
socklen_t addr_len;
|
||||
struct pollfd poll_s;
|
||||
char buf[BUFSIZE];
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// SOCKET //
|
||||
// //
|
||||
// --------------------------------------------------------------------------- //
|
||||
sckt_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
if (sckt_fd == INVALID)
|
||||
{
|
||||
perror("err socket()");
|
||||
return 0;
|
||||
}
|
||||
std::cout << "server init\n";
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// BIND //
|
||||
// //
|
||||
// --------------------------------------------------------------------------- //
|
||||
memset(&my_addr, 0, sizeof(my_addr));
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_port = htons(PORT);
|
||||
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
// my_addr.sin_addr.s_addr = inet_addr("10.12.110.57");
|
||||
bind(sckt_fd, (struct sockaddr *)&my_addr, sizeof(my_addr));
|
||||
if (sckt_fd == INVALID)
|
||||
{
|
||||
perror("err bind()");
|
||||
return 0;
|
||||
}
|
||||
std::cout << "socket bind to port: " << PORT << "\n";
|
||||
// https://beej.us/guide/bgnet/html/index-wide.html#cb29 :
|
||||
//
|
||||
// Sometimes, you might notice, you try to rerun a server and bind() fails, claiming “Address already in use.” What does that mean? Well, a little bit of a socket that was connected is still hanging around in the kernel, and it’s hogging the port. You can either wait for it to clear (a minute or so), or add code to your program allowing it to reuse the port, like this:
|
||||
//
|
||||
// int yes=1;
|
||||
//
|
||||
// // lose the pesky "Address already in use" error message
|
||||
// if (setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes) == -1) {
|
||||
// perror("setsockopt");
|
||||
// exit(1);
|
||||
// }
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------- //
|
||||
// LISTEN //
|
||||
// mark the socket as passive, to receive incoming connection requests //
|
||||
// --------------------------------------------------------------------------- //
|
||||
lstn = listen(sckt_fd, BACKLOG);
|
||||
if (lstn == INVALID)
|
||||
{
|
||||
perror("err listen()");
|
||||
return 0;
|
||||
}
|
||||
std::cout << "server listening\n";
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// ACCEPT //
|
||||
// extract first connection request in the queue of the listening socket //
|
||||
// and creates a new socket that is connected, and returns it //
|
||||
// ----------------------------------------------------------------------- //
|
||||
addr_len = sizeof(their_addr);
|
||||
axpt_fd = accept(sckt_fd, (sockaddr*)&their_addr, &addr_len);
|
||||
if (axpt_fd == INVALID)
|
||||
{
|
||||
perror("err accept()");
|
||||
continue;
|
||||
}
|
||||
std::cout << "server accepted a socket from: "
|
||||
<< inet_ntoa(their_addr.sin_addr)
|
||||
<< "\n";
|
||||
|
||||
|
||||
/////////////////////
|
||||
// luke version
|
||||
/////////////////////
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// FCNTL //
|
||||
// manipulate the new socket fd to mark it as non-blocking //
|
||||
// ----------------------------------------------------------------------- //
|
||||
fcntl(axpt_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// POLL //
|
||||
// waits for an event on the axpt_fd : there is data to read //
|
||||
// ----------------------------------------------------------------------- //
|
||||
poll_s.fd = axpt_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 << "poll()\n";
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// RECV //
|
||||
// act as read, read the data of the socket created by accept() //
|
||||
// ----------------------------------------------------------------------- //
|
||||
ret = recv(axpt_fd, buf, BUFSIZE, 0);
|
||||
if (ret == -1)
|
||||
{
|
||||
perror("err recv()");
|
||||
if (send(axpt_fd, MSG_BOUNCE, sizeof MSG_BOUNCE - 1, 0) == -1)
|
||||
perror("err send()");
|
||||
close(axpt_fd);
|
||||
continue;
|
||||
}
|
||||
std::cout << "recv()\n";
|
||||
buf[ret] = '\0';
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// SEND //
|
||||
// act as write, write data in the socket created by accept() //
|
||||
// ----------------------------------------------------------------------- //
|
||||
std::cout << "send()\n";
|
||||
if (send(axpt_fd, buf, ret, 0) == -1) // echo the read
|
||||
perror("err send()");
|
||||
if (send(axpt_fd, MSG_TEST, sizeof MSG_TEST - 1, 0) == -1)
|
||||
perror("err send()");
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
// CLOSE //
|
||||
// close the file descriptor of the connected socket //
|
||||
// ----------------------------------------------------------------------- //
|
||||
close(axpt_fd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
25
srcs/main_luke.cpp
Normal file
25
srcs/main_luke.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <stdexcept>
|
||||
#include "Webserv.hpp"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
try
|
||||
{
|
||||
Webserv serv;
|
||||
|
||||
// https://security.stackexchange.com/questions/169213/how-to-chose-a-port-to-run-an-application-on-localhost
|
||||
//serv.bind(4040);
|
||||
serv.bind(4040);
|
||||
serv.listen(512); // 512 max connections arbitrary
|
||||
serv.start();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cout << e.what() << '\n';
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
308
srcs/main_poll.cpp
Normal file
308
srcs/main_poll.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
|
||||
// https://www.ibm.com/docs/en/i/7.2?topic=designs-using-poll-instead-select
|
||||
|
||||
# include <unistd.h> // close
|
||||
# include <stdlib.h> // exit
|
||||
# include <iostream> // cout, cin
|
||||
# include <cerrno> // errno
|
||||
# include <cstdio> // perror
|
||||
# include <string.h> // memset
|
||||
# include <sys/socket.h> // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname
|
||||
# include <netinet/in.h> // sockaddr_in
|
||||
# include <arpa/inet.h> // inet_ntoa, inet_addr, htonl, htons, ntohl, ntohs
|
||||
# include <poll.h> // poll
|
||||
# include <fcntl.h> // fcntl
|
||||
# include <sys/ioctl.h> // ioctl
|
||||
|
||||
# define FALSE 0
|
||||
# define TRUE 1
|
||||
# define SERVER_PORT 4040
|
||||
|
||||
int main ()
|
||||
{
|
||||
int len, rc, on = 1;
|
||||
int listen_sd = -1, new_sd = -1;
|
||||
int end_server = FALSE, compress_array = FALSE;
|
||||
int close_conn;
|
||||
char buffer[80];
|
||||
struct sockaddr_in addr;
|
||||
struct pollfd fds[200];
|
||||
// struct pollfd fds[1];
|
||||
// struct pollfd fds;
|
||||
int nfds = 1, current_size = 0, i, j;
|
||||
|
||||
// *************************************************************
|
||||
// * Create an AF_INET stream socket to receive incoming *
|
||||
// * connections on *
|
||||
// *************************************************************
|
||||
listen_sd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (listen_sd < 0)
|
||||
{
|
||||
perror("socket() failed");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// *************************************************************
|
||||
// * Allow socket descriptor to be reuseable *
|
||||
// *************************************************************
|
||||
rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
|
||||
if (rc < 0)
|
||||
{
|
||||
perror("setsockopt() failed");
|
||||
close(listen_sd);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// *************************************************************
|
||||
// * Set socket to be nonblocking. All of the sockets for *
|
||||
// * the incoming connections will also be nonblocking since *
|
||||
// * they will inherit that state from the listening socket. *
|
||||
// *************************************************************
|
||||
rc = ioctl(listen_sd, FIONBIO, (char *)&on);
|
||||
if (rc < 0)
|
||||
{
|
||||
perror("ioctl() failed");
|
||||
close(listen_sd);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// *************************************************************
|
||||
// * Bind the socket *
|
||||
// *************************************************************
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
// memcpy(&addr.sin_addr, &inaddr_any, sizeof(inaddr_any));
|
||||
addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
addr.sin_port = htons(SERVER_PORT);
|
||||
rc = bind(listen_sd, (struct sockaddr *)&addr, sizeof(addr));
|
||||
if (rc < 0)
|
||||
{
|
||||
perror("bind() failed");
|
||||
close(listen_sd);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// *************************************************************
|
||||
// * Set the listen back log *
|
||||
// *************************************************************
|
||||
rc = listen(listen_sd, 32);
|
||||
if (rc < 0)
|
||||
{
|
||||
perror("listen() failed");
|
||||
close(listen_sd);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// *************************************************************
|
||||
// * Initialize the pollfd structure *
|
||||
// *************************************************************
|
||||
memset(fds, 0 , sizeof(fds));
|
||||
|
||||
// *************************************************************
|
||||
// * Set up the initial listening socket *
|
||||
// *************************************************************
|
||||
fds[0].fd = listen_sd;
|
||||
fds[0].events = POLLIN;
|
||||
|
||||
// *************************************************************
|
||||
// * Loop waiting for incoming connects or for incoming data *
|
||||
// * on any of the connected sockets. *
|
||||
// *************************************************************
|
||||
do
|
||||
{
|
||||
// ***********************************************************
|
||||
// * Call poll() *
|
||||
// ***********************************************************
|
||||
poll(fds, nfds, -1);
|
||||
|
||||
// ***********************************************************
|
||||
// * One or more descriptors are readable. Need to *
|
||||
// * determine which ones they are. *
|
||||
// ***********************************************************
|
||||
current_size = nfds;
|
||||
for (i = 0; i < current_size; i++)
|
||||
{
|
||||
// *********************************************************
|
||||
// * Loop through to find the descriptors that returned *
|
||||
// * POLLIN and determine whether it's the listening *
|
||||
// * or the active connection. *
|
||||
// *********************************************************
|
||||
if(fds[i].revents == 0)
|
||||
continue;
|
||||
|
||||
// *********************************************************
|
||||
// * If revents is not POLLIN, it's an unexpected result, *
|
||||
// * log and end the server. *
|
||||
// *********************************************************
|
||||
if(fds[i].revents != POLLIN)
|
||||
{
|
||||
printf(" Error! revents = %d\n", fds[i].revents);
|
||||
end_server = TRUE;
|
||||
break;
|
||||
|
||||
}
|
||||
if (fds[i].fd == listen_sd)
|
||||
{
|
||||
// *******************************************************
|
||||
// * Listening descriptor is readable. *
|
||||
// *******************************************************
|
||||
printf(" Listening socket is readable\n");
|
||||
|
||||
// *******************************************************
|
||||
// * Accept all incoming connections that are *
|
||||
// * queued up on the listening socket before we *
|
||||
// * loop back and call poll again. *
|
||||
// *******************************************************
|
||||
do
|
||||
{
|
||||
// *****************************************************
|
||||
// * Accept each incoming connection. If *
|
||||
// * accept fails with EWOULDBLOCK, then we *
|
||||
// * have accepted all of them. Any other *
|
||||
// * failure on accept will cause us to end the *
|
||||
// * server. *
|
||||
// *****************************************************
|
||||
new_sd = accept(listen_sd, NULL, NULL);
|
||||
if (new_sd < 0)
|
||||
{
|
||||
if (errno != EWOULDBLOCK)
|
||||
{
|
||||
perror(" accept() failed");
|
||||
end_server = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// *****************************************************
|
||||
// * Add the new incoming connection to the *
|
||||
// * pollfd structure *
|
||||
// *****************************************************
|
||||
printf(" New incoming connection - %d\n", new_sd);
|
||||
fds[nfds].fd = new_sd;
|
||||
fds[nfds].events = POLLIN;
|
||||
nfds++;
|
||||
|
||||
// *****************************************************
|
||||
// * Loop back up and accept another incoming *
|
||||
// * connection *
|
||||
// *****************************************************
|
||||
} while (new_sd != -1);
|
||||
}
|
||||
|
||||
// *********************************************************
|
||||
// * This is not the listening socket, therefore an *
|
||||
// * existing connection must be readable *
|
||||
// *********************************************************
|
||||
|
||||
else
|
||||
{
|
||||
printf(" Descriptor %d is readable\n", fds[i].fd);
|
||||
close_conn = FALSE;
|
||||
// *******************************************************
|
||||
// * Receive all incoming data on this socket *
|
||||
// * before we loop back and call poll again. *
|
||||
// *******************************************************
|
||||
|
||||
do
|
||||
{
|
||||
// *****************************************************
|
||||
// * Receive data on this connection until the *
|
||||
// * recv fails with EWOULDBLOCK. If any other *
|
||||
// * failure occurs, we will close the *
|
||||
// * connection. *
|
||||
// *****************************************************
|
||||
rc = recv(fds[i].fd, buffer, sizeof(buffer), 0);
|
||||
if (rc < 0)
|
||||
{
|
||||
if (errno != EWOULDBLOCK)
|
||||
{
|
||||
perror(" recv() failed");
|
||||
close_conn = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// *****************************************************
|
||||
// * Check to see if the connection has been *
|
||||
// * closed by the client *
|
||||
// *****************************************************
|
||||
if (rc == 0)
|
||||
{
|
||||
printf(" Connection closed\n");
|
||||
close_conn = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
// *****************************************************
|
||||
// * Data was received *
|
||||
// *****************************************************
|
||||
len = rc;
|
||||
printf(" %d bytes received\n", len);
|
||||
|
||||
// *****************************************************
|
||||
// * Echo the data back to the client *
|
||||
// *****************************************************
|
||||
rc = send(fds[i].fd, buffer, len, 0);
|
||||
if (rc < 0)
|
||||
{
|
||||
perror(" send() failed");
|
||||
close_conn = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
} while(TRUE);
|
||||
|
||||
// *******************************************************
|
||||
// * If the close_conn flag was turned on, we need *
|
||||
// * to clean up this active connection. This *
|
||||
// * clean up process includes removing the *
|
||||
// * descriptor. *
|
||||
// *******************************************************
|
||||
if (close_conn)
|
||||
{
|
||||
close(fds[i].fd);
|
||||
fds[i].fd = -1;
|
||||
compress_array = TRUE;
|
||||
}
|
||||
|
||||
|
||||
} // End of existing connection is readable
|
||||
} // End of loop through pollable descriptors
|
||||
|
||||
// ***********************************************************
|
||||
// * If the compress_array flag was turned on, we need *
|
||||
// * to squeeze together the array and decrement the number *
|
||||
// * of file descriptors. We do not need to move back the *
|
||||
// * events and revents fields because the events will always*
|
||||
// * be POLLIN in this case, and revents is output. *
|
||||
// ***********************************************************
|
||||
if (compress_array)
|
||||
{
|
||||
compress_array = FALSE;
|
||||
for (i = 0; i < nfds; i++)
|
||||
{
|
||||
if (fds[i].fd == -1)
|
||||
{
|
||||
for(j = i; j < nfds; j++)
|
||||
{
|
||||
fds[j].fd = fds[j+1].fd;
|
||||
}
|
||||
i--;
|
||||
nfds--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} while (end_server == FALSE); // End of serving running.
|
||||
|
||||
// *************************************************************
|
||||
// * Clean up all of the sockets that are open *
|
||||
// *************************************************************
|
||||
for (i = 0; i < nfds; i++)
|
||||
{
|
||||
if(fds[i].fd >= 0)
|
||||
close(fds[i].fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
303
srcs/main_select.cpp
Normal file
303
srcs/main_select.cpp
Normal file
@@ -0,0 +1,303 @@
|
||||
|
||||
# include <unistd.h> // close
|
||||
# include <string.h>
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
# include <sys/ioctl.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/time.h>
|
||||
# include <netinet/in.h>
|
||||
# include <errno.h>
|
||||
|
||||
#define SERVER_PORT 4040
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
int main ()
|
||||
{
|
||||
int i, len, rc, on = 1;
|
||||
int listen_sd, max_sd, new_sd;
|
||||
int desc_ready, end_server = FALSE;
|
||||
int close_conn;
|
||||
char buffer[80];
|
||||
struct sockaddr_in6 addr;
|
||||
struct timeval timeout;
|
||||
fd_set master_set, working_set;
|
||||
|
||||
/*************************************************************/
|
||||
/* Create an AF_INET6 stream socket to receive incoming */
|
||||
/* connections on */
|
||||
/*************************************************************/
|
||||
listen_sd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (listen_sd < 0)
|
||||
{
|
||||
perror("socket() failed");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* Allow socket descriptor to be reuseable */
|
||||
/*************************************************************/
|
||||
rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR,
|
||||
(char *)&on, sizeof(on));
|
||||
if (rc < 0)
|
||||
{
|
||||
perror("setsockopt() failed");
|
||||
close(listen_sd);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* Set socket to be nonblocking. All of the sockets for */
|
||||
/* the incoming connections will also be nonblocking since */
|
||||
/* they will inherit that state from the listening socket. */
|
||||
/*************************************************************/
|
||||
rc = ioctl(listen_sd, FIONBIO, (char *)&on);
|
||||
if (rc < 0)
|
||||
{
|
||||
perror("ioctl() failed");
|
||||
close(listen_sd);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* Bind the socket */
|
||||
/*************************************************************/
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin6_family = AF_INET6;
|
||||
memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
|
||||
addr.sin6_port = htons(SERVER_PORT);
|
||||
rc = bind(listen_sd,
|
||||
(struct sockaddr *)&addr, sizeof(addr));
|
||||
if (rc < 0)
|
||||
{
|
||||
perror("bind() failed");
|
||||
close(listen_sd);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* Set the listen back log */
|
||||
/*************************************************************/
|
||||
rc = listen(listen_sd, 32);
|
||||
if (rc < 0)
|
||||
{
|
||||
perror("listen() failed");
|
||||
close(listen_sd);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
/* Initialize the master fd_set */
|
||||
/*************************************************************/
|
||||
FD_ZERO(&master_set);
|
||||
max_sd = listen_sd;
|
||||
FD_SET(listen_sd, &master_set);
|
||||
|
||||
/*************************************************************/
|
||||
/* Initialize the timeval struct to 3 minutes. If no */
|
||||
/* activity after 3 minutes this program will end. */
|
||||
/*************************************************************/
|
||||
timeout.tv_sec = 3 * 60;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
/*************************************************************/
|
||||
/* Loop waiting for incoming connects or for incoming data */
|
||||
/* on any of the connected sockets. */
|
||||
/*************************************************************/
|
||||
do
|
||||
{
|
||||
/**********************************************************/
|
||||
/* Copy the master fd_set over to the working fd_set. */
|
||||
/**********************************************************/
|
||||
memcpy(&working_set, &master_set, sizeof(master_set));
|
||||
|
||||
/**********************************************************/
|
||||
/* Call select() and wait 3 minutes for it to complete. */
|
||||
/**********************************************************/
|
||||
printf("Waiting on select()...\n");
|
||||
rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);
|
||||
|
||||
/**********************************************************/
|
||||
/* Check to see if the select call failed. */
|
||||
/**********************************************************/
|
||||
if (rc < 0)
|
||||
{
|
||||
perror(" select() failed");
|
||||
break;
|
||||
}
|
||||
|
||||
/**********************************************************/
|
||||
/* Check to see if the 3 minute time out expired. */
|
||||
/**********************************************************/
|
||||
if (rc == 0)
|
||||
{
|
||||
printf(" select() timed out. End program.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/**********************************************************/
|
||||
/* One or more descriptors are readable. Need to */
|
||||
/* determine which ones they are. */
|
||||
/**********************************************************/
|
||||
desc_ready = rc;
|
||||
for (i=0; i <= max_sd && desc_ready > 0; ++i)
|
||||
{
|
||||
/*******************************************************/
|
||||
/* Check to see if this descriptor is ready */
|
||||
/*******************************************************/
|
||||
if (FD_ISSET(i, &working_set))
|
||||
{
|
||||
/****************************************************/
|
||||
/* A descriptor was found that was readable - one */
|
||||
/* less has to be looked for. This is being done */
|
||||
/* so that we can stop looking at the working set */
|
||||
/* once we have found all of the descriptors that */
|
||||
/* were ready. */
|
||||
/****************************************************/
|
||||
desc_ready -= 1;
|
||||
|
||||
/****************************************************/
|
||||
/* Check to see if this is the listening socket */
|
||||
/****************************************************/
|
||||
if (i == listen_sd)
|
||||
{
|
||||
printf(" Listening socket is readable\n");
|
||||
/*************************************************/
|
||||
/* Accept all incoming connections that are */
|
||||
/* queued up on the listening socket before we */
|
||||
/* loop back and call select again. */
|
||||
/*************************************************/
|
||||
do
|
||||
{
|
||||
/**********************************************/
|
||||
/* Accept each incoming connection. If */
|
||||
/* accept fails with EWOULDBLOCK, then we */
|
||||
/* have accepted all of them. Any other */
|
||||
/* failure on accept will cause us to end the */
|
||||
/* server. */
|
||||
/**********************************************/
|
||||
new_sd = accept(listen_sd, NULL, NULL);
|
||||
if (new_sd < 0)
|
||||
{
|
||||
if (errno != EWOULDBLOCK)
|
||||
{
|
||||
perror(" accept() failed");
|
||||
end_server = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/**********************************************/
|
||||
/* Add the new incoming connection to the */
|
||||
/* master read set */
|
||||
/**********************************************/
|
||||
printf(" New incoming connection - %d\n", new_sd);
|
||||
FD_SET(new_sd, &master_set);
|
||||
if (new_sd > max_sd)
|
||||
max_sd = new_sd;
|
||||
|
||||
/**********************************************/
|
||||
/* Loop back up and accept another incoming */
|
||||
/* connection */
|
||||
/**********************************************/
|
||||
} while (new_sd != -1);
|
||||
}
|
||||
|
||||
/****************************************************/
|
||||
/* This is not the listening socket, therefore an */
|
||||
/* existing connection must be readable */
|
||||
/****************************************************/
|
||||
else
|
||||
{
|
||||
printf(" Descriptor %d is readable\n", i);
|
||||
close_conn = FALSE;
|
||||
/*************************************************/
|
||||
/* Receive all incoming data on this socket */
|
||||
/* before we loop back and call select again. */
|
||||
/*************************************************/
|
||||
do
|
||||
{
|
||||
/**********************************************/
|
||||
/* Receive data on this connection until the */
|
||||
/* recv fails with EWOULDBLOCK. If any other */
|
||||
/* failure occurs, we will close the */
|
||||
/* connection. */
|
||||
/**********************************************/
|
||||
rc = recv(i, buffer, sizeof(buffer), 0);
|
||||
if (rc < 0)
|
||||
{
|
||||
if (errno != EWOULDBLOCK)
|
||||
{
|
||||
perror(" recv() failed");
|
||||
close_conn = TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/**********************************************/
|
||||
/* Check to see if the connection has been */
|
||||
/* closed by the client */
|
||||
/**********************************************/
|
||||
if (rc == 0)
|
||||
{
|
||||
printf(" Connection closed\n");
|
||||
close_conn = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/**********************************************/
|
||||
/* Data was received */
|
||||
/**********************************************/
|
||||
len = rc;
|
||||
printf(" %d bytes received\n", len);
|
||||
|
||||
/**********************************************/
|
||||
/* Echo the data back to the client */
|
||||
/**********************************************/
|
||||
rc = send(i, buffer, len, 0);
|
||||
if (rc < 0)
|
||||
{
|
||||
perror(" send() failed");
|
||||
close_conn = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
} while (TRUE);
|
||||
|
||||
/*************************************************/
|
||||
/* If the close_conn flag was turned on, we need */
|
||||
/* to clean up this active connection. This */
|
||||
/* clean up process includes removing the */
|
||||
/* descriptor from the master set and */
|
||||
/* determining the new maximum descriptor value */
|
||||
/* based on the bits that are still turned on in */
|
||||
/* the master set. */
|
||||
/*************************************************/
|
||||
if (close_conn)
|
||||
{
|
||||
close(i);
|
||||
FD_CLR(i, &master_set);
|
||||
if (i == max_sd)
|
||||
{
|
||||
while (FD_ISSET(max_sd, &master_set) == FALSE)
|
||||
max_sd -= 1;
|
||||
}
|
||||
}
|
||||
} /* End of existing connection is readable */
|
||||
} /* End of if (FD_ISSET(i, &working_set)) */
|
||||
} /* End of loop through selectable descriptors */
|
||||
|
||||
} while (end_server == FALSE);
|
||||
|
||||
/*************************************************************/
|
||||
/* Clean up all of the sockets that are open */
|
||||
/*************************************************************/
|
||||
for (i=0; i <= max_sd; ++i)
|
||||
{
|
||||
if (FD_ISSET(i, &master_set))
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user