164 lines
6.2 KiB
C++
164 lines
6.2 KiB
C++
|
||
# 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;
|
||
}
|
||
|