Une bibliothèque de sockets C++ indépendante de la plate-forme avec prise en charge SSL transparente (et bien d'autres éléments).
Obtenez-le ici : [Bibliothèques statiques et partagées Windows x64 avec prise en charge SSL] ou [pas de prise en charge SSL]
# 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
socks::makeThreaded[SSL]Server
& socks::makeMultiplexed[SSL]Server
) # 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
};
Vous pouvez remplacer socks::factory::makeThreadedServer
par socks::factory::makeMultiplexedServer
. Le premier instancie un serveur qui crée un thread par client (en utilisant des E/S bloquantes), le second instancie un serveur avec une architecture plus évolutive, en utilisant le modèle de réacteur (plusieurs clients sont servis par thread en utilisant des E/S non bloquantes).
# 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;
}
}
Vous pouvez également transformer un socks::DatagramSocket
en un socket de datagramme « connecté » et utiliser l'interface socks::ClientSocket
.
socks::ClientSocket clientSocket = datagramSocket.makeClientSocket(peer);
/*
* from this point on 'datagramSocket' cannot be used anymore,
* its implementation has been moved to 'clientSocket'.
*/
ou
socks::ClientSocket clientSocket = datagramSocket.makeClientSocket(host, port); // same here
C'est un socks::ClientSocket
enveloppé dans une classe std::iostream
.
# 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;
}
}
Remplacez simplement les déclarations :
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<...>(...);
C'est ça. Pour les socks::ServerSocket
et socks::Server
vous aurez également besoin de fichiers de certificat et de clé (les noms par défaut sont « cert.pem » et « key.pem »). Pour générer un certificat de test et des fichiers de clés :
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
Cet espace de noms contient toutes les méthodes d'usine de la bibliothèque.
Un exemple plus élaboré : une implémentation de serveur FTP simple, mais entièrement fonctionnelle, utilisant des libsockets (y compris la prise en charge SSL, le mode passif, la reprise et la prise en charge FXP). Un rappel d'authentification est fourni pour l'utilisateur. Vous pouvez également enregistrer des commandes SITE personnalisées.
Vous pouvez l'essayer : binaire statique Windows x64
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 ();
}
Bientôt disponible... (en cours de développement/refactoring, ne fonctionnant actuellement que comme bibliothèque...)