A platform independent C++ socket library with transparent SSL support (and some more stuff).
Get it here: [Windows x64 static & shared libraries with SSL support] or [no SSL support]
#include "libsockets.h"
void client() {
socks::ClientSocket clientSocket;
if (clientSocket.connectTo("127.0.0.1", "10000") == 0) {
std::string hello = "Hello World";
clientSocket.sendData(hello.c_str(), hello.size());
}
};
int main() {
client();
};
#include "libsockets.h"
void server() {
socks::ServerSocket serverSocket;
serverSocket.listenForConnections("127.0.0.1", "10000");
socks::ClientSocket clientSocket = serverSocket.acceptConnection();
char buf[512];
auto len = clientSocket.receiveData(buf, 512);
if (len > 0) {
buf[len]=0;
std::cout << buf << std::endl;
}
};
int main() {
server();
};
$> ./server &
$> ./client
#include "libsockets.h"
class MyContext {
public:
int value; // whatever context you need to maintain
};
void onReceive(socks::Context<MyContext> &context, std::istream &inp, std::ostream &outp) {
std::string word;
while (inp >> word)
outp << context.getContext().value++ << " " << word << std::endl;
};
void onConnect(socks::Context<MyContext> &context, std::istream &inp, std::ostream &outp) {
context.getContext().value = 0;
outp << "Hello" << std::endl;
};
int main() {
auto myServer = socks::factory::makeThreadedServer<MyContext>(
onReceive,
onConnect /*,
onDisconnect,
afterWrite
*/
); // onReceive is mandatory, onConnect, onDisconnect & afterWrite are optional
myServer.listen("127.0.0.1", "10000"); // serves
};
You can replace socks::factory::makeThreadedServer
with socks::factory::makeMultiplexedServer
. The first instantiates a server that creates one thread per client (using blocking I/O), the second instantiates a server with a more scalable architecture, using the reactor pattern (several clients are served per thread using non-blocking I/O).
#include "libsockets.h"
constexpr size_t bufferSize = 4096;
int main() {
socks::DatagramSocket datagramSocket;
datagramSocket.bindSocket("0.0.0.0", "10000");
char buffer[bufferSize];
std::pair<int, socks::SocketAddress> ret = datagramSocket.receiveFrom(buffer, bufferSize);
// ret = pair<bytes received, sender address>
if (ret.first > 0) {
auto peer = std::move(ret.second);
std::string reply = "received " + std::to_string(ret.first) + " bytes";
datagramSocket.sendTo(peer, reply.c_str(), reply.size());
} else {
std::cerr << "error receiving." << std::endl;
}
}
You can also turn a socks::DatagramSocket
into a 'connected' datagram socket and use socks::ClientSocket
's interface.
socks::ClientSocket clientSocket = datagramSocket.makeClientSocket(peer);
/*
* from this point on 'datagramSocket' cannot be used anymore,
* its implementation has been moved to 'clientSocket'.
*/
or
socks::ClientSocket clientSocket = datagramSocket.makeClientSocket(host, port); // same here
It's a socks::ClientSocket
wrapped in an std::iostream
class.
#include "libsockets.h"
int main() {
socks::SocketStream socketStream;
if (socketStream.connectTo("127.0.0.1", "10000") == 0) {
std::string buf;
std::getline(socketStream, buf); // receiving data
std::cout << buf << std::endl;
socketStream << "message received." << std::endl; // transmitting data
} else {
std::cerr << "error connecting." << std::endl;
}
}
Just replace the declarations:
socks::ClientSocket clientSocket = socks::factory::makeSSLClientSocket;
socks::ServerSocket serverSocket = socks::factory::makeSSLServerSocket;
socks::SocketStream socketStream = socks::factory::makeSSLSocketStream;
socks::Server server = socks::factory::makeThreadedSSLServer<...>(...);
socks::Server server = socks::factory::makeMultiplexedSSLServer<...>(...);
That's it. For the socks::ServerSocket
and socks::Server
you'll also need certificate and key files (default names are 'cert.pem' and 'key.pem').
To generate test certificate and key files:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
This namespace contains all library's factory methods.
A more elaborate example: a simple, yet fully functional, FTP server implementation using libsockets (including SSL support, passive mode, resume & FXP support). An authentication callback is provided for the user. You can also register custom SITE commands.
You can try it: Windows x64 static binary
AuthenticationFunction FTPClientInfo::authenticate =
[](const std::string &username, const std::string &password, FTPClientInfo& clientInfo) {
/*
* in here a user profile can be loaded into 'clientInfo'
* upon authentication in order to define, for example,
* a home dir, chroot, etc.
*/
return authService.authenticate(username, password);
};
int main(int argc, char **argv) {
FTPServer ftpServer;
// SITE CLIENT COUNT
ftpServer.registerSiteCommand(
"CLIENT",
[&ftpServer](const std::string ¶ms, FTPClientInfo &clientInfo) {
std::stringstream ss(params);
std::string p1;
ss >> p1;
std::transform(p1.begin(), p1.end(), p1.begin(), ::toupper);
if (p1 == "COUNT")
return "200 There is/are " + std::to_string(ftpServer.getClientCount()) + " client(s) online.";
else
return std::string("501 Invalid SITE CLIENT parameter.");
});
ftpServer.start();
}
Coming soon... (under development/refactoring, currently only working as a library...)