TalTech_cpp/CT4/server.cpp

68 lines
2.0 KiB
C++

#include <sys/socket.h> // socket functions
#include <netinet/in.h> // sockaddr_in
#include <cstdlib> // exit() and EXIT_FAILURE
#include <iostream> // cout
#include <unistd.h> // read
#include <signal.h>
#include <ext/stdio_filebuf.h> // buffered stream
#include <thread>
using SOCKET_FD = int;
constexpr uint16_t LISTEN_PORT = 42069;
const int yes = 1;
SOCKET_FD listener = -1, connection = -1;
void handle_client(SOCKET_FD client_fd, bool (*handler)(std::istream&, std::ostream&, void*), void* ctx) {
using Filebuf = __gnu_cxx::stdio_filebuf<char>;
// setup input stream
FILE* fin = fdopen(client_fd, "r");
Filebuf in_buf(fin, std::ios::in);
std::istream is(&in_buf);
// setup output stream
FILE* fout = fdopen(client_fd, "w");
Filebuf out_buf(fout, std::ios::out);
std::ostream os(&out_buf);
signal(SIGPIPE, SIG_IGN); // ignore pipe signal
if (handler(is, os, ctx)) {
shutdown(listener, SHUT_RD);
}
fclose(fin);
fclose(fout);
close(client_fd);
}
void serve(bool (*handler)(std::istream&, std::ostream&, void*), void* ctx) {
sockaddr_in addr{ .sin_family = AF_INET, .sin_port = htons(LISTEN_PORT), .sin_addr = in_addr{ .s_addr = INADDR_ANY } };
if ((listener = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0 ||
bind(listener, (const sockaddr*) &addr, sizeof(addr)) < 0 ||
listen(listener, 10) < 0) {
goto cleanup;
}
while ((connection = accept(listener, nullptr, nullptr)) > 0) {
std::thread(handle_client, connection, handler, ctx).detach();
}
cleanup:
std::cout << "Server shutting down..." << std::endl;
if (connection != -1) close(connection);
if (listener != -1) close(listener);
}
bool h(std::istream& is, std::ostream& os, void* ctx) {
std::string s;
os << "hello from " << std::this_thread::get_id() <<"! are you there?" << std::endl;
is >> s;
os << s << " to you too!" << std::endl;
return true;
}