68 lines
2.0 KiB
C++
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;
|
|
}
|