Nous aborderons le routage du trafic Internet. Nous apprendrons qu'il existe deux versions du protocole Internet : IPv4 et IPv6. Nous découvrirons qu'IPv4 dispose d'un nombre limité d'adresses, et ces adresses s'épuisent. Nous verrons que l'un des principaux avantages d'IPv6 est qu'il offre suffisamment d'espace d'adressage pour que chaque système possède sa propre adresse unique routable publiquement. Nous découvrirons que le nombre limité d'adresses IPv4 est largement compensé par la traduction d'adresses réseau effectuée par les routeurs. Nous montrerons comment détecter votre adresse IP locale à l'aide des utilitaires et des API fournis par le système d'exploitation. Nous découvrirons que les API permettant de lister les adresses IP locales diffèrent sensiblement entre Windows et les systèmes d'exploitation de type Unix.
Nous avons affiché les routeurs entre notre système Windows et un système de destination. Cette opération permet d'identifier les routeurs par lesquels doit transiter une requête en provenance de notre système avant d'atteindre une adresse de destination. Nous avons ouvert un Terminal et avons exécuté la commande d'affichage des routeurs.
// Terminal...
tracert example.com
...
// TerminalNous avons listé les routeurs entre notre système Unix et un système de destination. Cela permet de connaître les routeurs par lesquels doit transiter une requête en provenance de notre système avant d'atteindre une adresse de destination. Nous avons ouvert un Terminal et avons exécuté la commande d'affichage des routeurs.
// Terminal...
traceroute example.com
...
Nous avons affiché les adresses IP locales disponibles sur notre système Windows. L'adresse IP locale permet à un client de notre réseau local d'accéder à notre serveur local. Nous avons ouvert un Terminal et avons exécuté la commande d'affichage des adresses IP.
// Terminal...
ipconfig
...
// TerminalNous avons affiché les adresses IP locales disponibles sur notre système Unix. L'adresse IP locale permet à un client de notre réseau local d'accéder à notre serveur local. Nous avons ouvert un Terminal et avons exécuté la commande d'affichage des adresses IP.
// Terminal...
ip a s
...
// TerminalNous avons affiché l'adresse IP publique de notre interface réseau. L'adresse IP publique permet à un client extérieur à notre réseau local d'accéder à notre serveur local depuis internet grâce à une redirection de port. Nous nous sommes rendus sur la page web d'un service de gestion d'adresse IP publique (
https://api.ipify.org/) et avons pu afficher notre adresse IP publique.
// Navigateur web...
https://api.ipify.org/
...
// Navigateur webNous avons écrit un programme en C/C++ permettant d'initialiser et de nettoyer l'API des sockets, portable sur les systèmes Windows, Linux ou MacOS. Même si cette opération est uniquement obligatoire pour les systèmes Windows, nous devons, néanmoins, assurer la portabilité du code source sur les autres systèmes afin d'offrir une même interface pour tous les systèmes.
Nous avons assuré la portabilité du code source dans le fichier (CMakeLists.txt). Sur les systèmes Windows (WIN32), nous avons ajouté le fichier (SocketWin.cpp) aux codes sources. Dans le cas contraire, sur les systèmes Linux ou MacOS, nous avons ajouté le fichier (SocketUnix.cpp) aux codes sources.
// CMakeLists.txt...
if(WIN32)
list(APPEND SRC_FILES
SocketWin.cpp
)
else()
list(APPEND SRC_FILES
SocketUnix.cpp
)
endif()
...Nous avons assuré la portabilité du code source dans le fichier (Socket.hpp). Sur les systèmes Windows (_WIN32), nous avons défini la version minimale du système d'exploitation Windows compatible avec le code source (_WIN32_WINNT), nous avons inclus le fichier d'entête de l'API des sockets Windows (Winsock2.h) et nous avons édité les liens du code source avec la librairie de l'API des sockets Windows (ws2_32.lib). Sur les systèmes Linux ou MacOS, nous n'avons pas eu besoin d'initialiser ou de nettoyer l'API des sockets.
// Socket.hpp...
#if defined(_WIN32)
#ifndef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_WIN6
#endif
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#endif
...
Nous avons initialisé l'API des sockets sur les systèmes Windows avec la fonction (WSAStartup). Sur les systèmes Linux ou MacOS, nous n'avons pas eu besoin d'initialiser l'API des sockets.
// SocketWin.cpp...
bool Socket::initSocket() const
{
WSADATA d;
if (WSAStartup(MAKEWORD(2, 2), &d))
{
printf("initSocket() failed.\n");
return false;
}
return true;
}
...Nous avons nettoyé l'API des sockets sur les systèmes Windows avec la fonction (WSACleanup). Sur les systèmes Linux ou MacOS, nous n'avons pas besoin d'initialiser l'API des sockets.
// SocketWin.cpp...
void Socket::cleanSocket() const
{
WSACleanup();
}
...Nous avons automatisé le nettoyage de l'API des sockets. Nous avons créé une classe (SocketClean) et avons appelé la méthode (cleanSocket) dans le destructeur de la classe (~SocketClean). Ainsi, après l'initialisation de l'API des sockets, nous pouvons créer une instance de la classe (SocketClean) pour garantir le nettoyage automatique de l'API des sockets.
// Socket.cpp...
SocketClean::~SocketClean()
{
Socket oSocket;
oSocket.cleanSocket();
}
...Nous avons initialisé l'API des sockets (initSocket) et avons créé une instance de la classe (SocketClean) pour nettoyer automatiquement l'API des sockets sur les systèmes Windows, Linux ou MacOS.
// main.cpp#include "Socket.hpp"
int main(int _argc, char** _argv)
{
Socket oSocket;
if (!oSocket.initSocket())
return 0;
SocketClean oSocketClean;
printf("Socket() Ok.\n");
return 0;
}// Socket.hpp#pragma once
// Windows
#if defined(_WIN32)
#ifndef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_WIN6
#endif
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
#endif
#include <iostream>
// Socket
class Socket
{
public:
explicit Socket();
~Socket();
bool initSocket() const;
void cleanSocket() const;
};
// SocketClean
class SocketClean
{
public:
explicit SocketClean();
~SocketClean();
};// Socket.cpp#include "Socket.hpp"
// Socket
Socket::Socket()
{
}
Socket::~Socket()
{
}
// SocketClean
SocketClean::SocketClean()
{
}
SocketClean::~SocketClean()
{
Socket oSocket;
oSocket.cleanSocket();
}// SocketWin.cpp#include "Socket.hpp"
bool Socket::initSocket() const
{
WSADATA d;
if (WSAStartup(MAKEWORD(2, 2), &d))
{
printf("initSocket() failed.\n");
return false;
}
return true;
}
void Socket::cleanSocket() const
{
WSACleanup();
}// SocketUnix.cpp#include "Socket.hpp"
bool Socket::initSocket() const
{
return true;
}
void Socket::cleanSocket() const
{
}// CMakeLists.txtcmake_minimum_required(VERSION 3.10.0)
project(c01-socket-init VERSION 0.1.0 LANGUAGES C CXX)
set(SRC_FILES
main.cpp
Socket.hpp
Socket.cpp
)
if(WIN32)
list(APPEND SRC_FILES
SocketWin.cpp
)
else()
list(APPEND SRC_FILES
SocketUnix.cpp
)
endif()
add_executable(${PROJECT_NAME}
${SRC_FILES}
)Nous avons affiché un message « socket() Ok. » pour indiquer que l'initialisation de l'API des sockets s'est bien déroulé.
// TerminalNous avons écrit un programme en C/C++ permettant d'afficher la liste des cartes réseau sur une machine. Nous avons assuré la portabilité du code source sur les systèmes Windows, Linux ou MacOS en offrant une même interface pour tous les systèmes.
Nous avons assuré la portabilité du code source sur les systèmes Windows, Linux ou MacOS à partir du fichier de configuration (CMakeLists.txt). Sur les systèmes Windows (WIN32), nous avons ajouté le fichier (AdapterWin.cpp) aux codes sources. Dans le cas contraire, sur les systèmes Linux ou MacOS, nous avons ajouté le fichier (AdapterUnix.cpp) aux codes sources.
// CMakeLists.txt...
if(WIN32)
list(APPEND SRC_FILES
AdapterWin.cpp
)
else()
list(APPEND SRC_FILES
AdapterUnix.cpp
)
endif()
...Nous avons assuré la portabilité du code source dans le fichier (Adapter.hpp). Sur les systèmes Windows (_WIN32), nous avons défini la version minimale du système d'exploitation Windows compatible avec le code source (_WIN32_WINNT), nous avons inclus les fichiers d'entête de l'API des sockets Windows (winsock2.h, iphlpapi.h, ws2tcpip.h) et nous avons édité les liens du code source avec les librairies de l'API des sockets Windows (ws2_32.lib, iphlpapi.lib). Sur les systèmes Linux ou MacOS, nous avons inclus les fichiers d'entête de l'API des sockets Unix (socket.h, netdb.h, ifaddrs.h).
// Adapter.hpp...
// Windows
#if defined (_WIN32)
#ifndef _WIN32_WINNT
#define _WIN32_WINNT _WIN32_WINNT_WIN6
#endif
#include <winsock2.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
#else
// Unix
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#endif
...
Nous avons assuré la portabilité du code source au niveau de la structure de données des paramètres de cartes réseau (AdapterParams) dans le fichier (Adapter.hpp). Sur les systèmes Windows (_WIN32), nous avons déclaré le pointeur de cartes réseau (adapters) de type (PIP_ADAPTER_ADDRESSES) et la taille de sa mémoire tampon (size) de type (DWORD). Sur les systèmes Linux ou MacOS, nous avons déclaré le pointeur d'interfaces réseau (addresses) de type (ifaddrs).
// Adapter.hpp...
struct AdapterParams
{
#if defined(_WIN32)
DWORD size;
PIP_ADAPTER_ADDRESSES adapters = nullptr;
#else
struct ifaddrs* addresses = nullptr;
#endif
};
...Nous avons chargé les cartes réseau sur les systèmes Windows. Nous avons récupéré la liste des cartes réseau avec la fonction (GetAdaptersAddresses). Nous avons récupéré la famille de l'adresse IP de l'interface réseau à partir de la propriété (sa_family). Nous avons récupéré l'adresse IP de l'interface réseau avec la fonction (getnameinfo).
// AdapterWin.cpp...
bool Adapter::loadAdapters(AdapterParams& _params) const
{
_params.adapters =
(PIP_ADAPTER_ADDRESSES)malloc(config::adapter::memorySize);
if (!_params.adapters)
{
fprintf(stderr, "loadAdapters(1) failed.|size=%ld\n", _params.size);
return false;
}
int adpaterResult = GetAdaptersAddresses(AF_UNSPEC,
GAA_FLAG_INCLUDE_PREFIX, 0, _params.adapters, &_params.size);
if (adpaterResult == ERROR_BUFFER_OVERFLOW)
{
fprintf(stderr, "loadAdapters(2) failed.|size=%ld\n", _params.size);
return false;
}
PIP_ADAPTER_ADDRESSES adapter = _params.adapters;
while (adapter)
{
AdapterNameParams* adapterName = _params.addAdapterName();
adapterName->name = oTools.toString(adapter->FriendlyName);
PIP_ADAPTER_UNICAST_ADDRESS address = adapter->FirstUnicastAddress;
while (address)
{
int family = address->Address.lpSockaddr->sa_family;
if (family == AF_INET || family == AF_INET6)
{
AdapterAddressParams* addressName =
_params.addApaterAddress(adapterName);
addressName->family = (family == AF_INET) ? "IPv4" : "IPv6";
char addressIP[100];
getnameinfo(address->Address.lpSockaddr,
address->Address.iSockaddrLength,
addressIP, sizeof(addressIP), 0, 0, NI_NUMERICHOST);
addressName->address = addressIP;
}
address = address->Next;
}
adapter = adapter->Next;
}
return true;
}
...Nous avons chargé les cartes réseau sur les systèmes Linux ou MacOS. Nous avons récupéré la liste des cartes réseau avec la fonction (getifaddrs). Nous avons récupéré la famille de l'adresse IP de l'interface réseau à partir de la propriété (family). Nous avons récupéré l'adresse IP de l'interface réseau avec la fonction (getnameinfo).
// AdapterUnix.cpp...
bool Adapter::loadAdapters(AdapterParams& _params) const
{
if (getifaddrs(&_params.addresses) == -1)
{
fprintf(stderr, "loadAdapters() failed.\n");
return false;
}
struct ifaddrs* address = _params.addresses;
while (address)
{
if (address->ifa_addr == nullptr)
{
address = address->ifa_next;
continue;
}
int family = address->ifa_addr->sa_family;
if (family == AF_INET || family == AF_INET6)
{
AdapterNameParams* adapterName;
if (!_params.getAdapterName(&adapterName, address->ifa_name))
{
adapterName = _params.addAdapterName();
adapterName->name = address->ifa_name;
}
AdapterAddressParams* addressName =
_params.addApaterAddress(adapterName);
addressName->family = (family == AF_INET) ? "IPv4" : "IPv6";
char ap[100];
const int family_size = (family == AF_INET) ?
sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
getnameinfo(
address->ifa_addr, family_size, ap, sizeof(ap), 0, 0,
NI_NUMERICHOST);
addressName->address = ap;
}
address = address->ifa_next;
}
return true;
}
...Nous avons affiché les cartes réseau sur les systèmes Windows, Linux ou MacOS.
// Adapter.cpp...
void Adapter::printAdapters(const AdapterParams& _params) const
{
const int margin = 13;
printf("---\n");
printf("AdapterNameParams:\n");
for (auto* adapterName : _params.adapterNameList)
{
printf("---\n");
printf("Adapter name: %s\n", adapterName->name.c_str());
for (auto* addressName : _params.getAddressList(adapterName))
{
printf("\t%s\t%s\n", addressName->family.c_str(),
addressName->address.c_str());
}
}
}
...Nous avons initialisé l'API des sockets avec la fonction (initSocket) et avons créé une instance de la classe (AdapterClean) pour nettoyer automatiquement l'API des sockets. Nous avons chargé les cartes réseau disponibles sur notre système Windows, Linux ou MacOS avec la fonction (loadAdapters) et avons affiché la liste des cartes réseau avec la fonction (printAdapters).
// main.cpp#include "Adapter.hpp"
int main(int _argc, char** _argv)
{
Adapter oAdapter;
AdapterParams adapterParams;
if (!oAdapter.initSocket())
return 0;
AdapterClean oAdapterClean;
if (!oAdapter.loadAdapters(adapterParams))
return 0;
oAdapter.print(adapterParams);
oAdapter.printAdapters(adapterParams);
return 0;
}
// Adapter.hpp#pragma once
// Windows
#if defined (_WIN32)
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#include <winsock2.h>
#include <iphlpapi.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "iphlpapi.lib")
#else
// Unix
#include <sys/socket.h>
#include <netdb.h>
#include <ifaddrs.h>
#endif
// Common
#include <iostream>
#include <string>
#include <vector>
// Params
struct AdapterParams;
struct AdapterNameParams;
struct AdapterAddressParams;
using AdapterNameList = std::vector<AdapterNameParams*>;
using AdapterAddressList = std::vector<AdapterAddressParams*>;
// AdapterParams
struct AdapterParams
{
#if defined(_WIN32)
DWORD size;
PIP_ADAPTER_ADDRESSES adapters = nullptr;
#else
struct ifaddrs* addresses = nullptr;
#endif
AdapterNameList adapterNameList;
AdapterAddressList addressNameList;
explicit AdapterParams();
~AdapterParams();
AdapterNameParams* addAdapterName();
AdapterAddressParams* addApaterAddress(AdapterNameParams* _adapterName);
bool getAdapterName(AdapterNameParams** _adapterName, const std::string& _name) const;
AdapterAddressList getAddressList(AdapterNameParams* _adapterName) const;
};
// AdapterNameParams
struct AdapterNameParams
{
std::string name;
};
// AdapterAddressParams
struct AdapterAddressParams
{
AdapterNameParams* adapterName;
std::string address;
std::string family;
explicit AdapterAddressParams(AdapterNameParams* _adapterName);
~AdapterAddressParams();
};
// Adapter
class Adapter
{
public:
explicit Adapter();
~Adapter();
bool initSocket() const;
void cleanSocket() const;
bool loadAdapters(AdapterParams& _params) const;
void print(const AdapterParams& _params) const;
void printAdapters(const AdapterParams& _params) const;
};
// AdapterClean
class AdapterClean
{
public:
explicit AdapterClean();
~AdapterClean();
};
// AdapterException
class AdapterException : public std::exception
{
public:
explicit AdapterException(const std::string& _msg);
~AdapterException();
const char* what() const throw();
private:
std::string m_msg;
};// Adapter.cpp#include "Adapter.hpp"
#include "Tools.hpp"
// AdapterParams
AdapterNameParams* AdapterParams::addAdapterName()
{
AdapterNameParams* adapterName = new AdapterNameParams;
if (!adapterName)
{
throw AdapterException("addAdapterName() failed.");
}
adapterNameList.push_back(adapterName);
return adapterName;
}
AdapterAddressParams* AdapterParams::addApaterAddress(AdapterNameParams* _adapterName)
{
AdapterAddressParams* addressName = new AdapterAddressParams(_adapterName);
if (!addressName)
{
throw AdapterException("addApaterAddress() failed.");
}
addressNameList.push_back(addressName);
return addressName;
}
bool AdapterParams::getAdapterName(AdapterNameParams** _adapterName, const std::string& _name) const
{
for (auto* adapterName : adapterNameList)
{
if (adapterName->name == _name)
{
(*_adapterName) = adapterName;
return true;
}
}
return false;
}
AdapterAddressList AdapterParams::getAddressList(AdapterNameParams* _adapterName) const
{
AdapterAddressList addressNameNewList;
for (auto* addressName : addressNameList)
{
if (addressName->adapterName == _adapterName)
{
addressNameNewList.push_back(addressName);
}
}
return addressNameNewList;
}
// AdapterAddressParams
AdapterAddressParams::AdapterAddressParams(AdapterNameParams* _adapterName)
: adapterName(_adapterName)
{
}
AdapterAddressParams::~AdapterAddressParams()
{
}
// Adapter
Adapter::Adapter()
{
}
Adapter::~Adapter()
{
}
void Adapter::print(const AdapterParams& _params) const
{
const int margin = 13;
printf("---\n");
printf("AdapterNameParams:\n");
for (auto* adapterName : _params.adapterNameList)
{
printf("%*s:\n", margin, "---");
printf("%*s: %s\n", margin, "name", adapterName->name.c_str());
for (auto* addressName : _params.getAddressList(adapterName))
{
printf("%*s:\n", 2 * margin, "---");
printf("%*s: %s\n", 2 * margin, "family", addressName->family.c_str());
printf("%*s: %s\n", 2 * margin, "address", addressName->address.c_str());
}
}
}
void Adapter::printAdapters(const AdapterParams& _params) const
{
const int margin = 13;
printf("---\n");
printf("AdapterNameParams:\n");
for (auto* adapterName : _params.adapterNameList)
{
printf("---\n");
printf("Adapter name: %s\n", adapterName->name.c_str());
for (auto* addressName : _params.getAddressList(adapterName))
{
printf("\t%s\t%s\n", addressName->family.c_str(), addressName->address.c_str());
}
}
}
//AdapterClean
AdapterClean::AdapterClean()
{
}
AdapterClean::~AdapterClean()
{
}
// AdapterException
AdapterException::AdapterException(const std::string& _msg)
: m_msg(_msg)
{
}
AdapterException::~AdapterException()
{
}
const char* AdapterException::what() const throw()
{
return m_msg.c_str();
}// AdapterWin.cpp#include "Adapter.hpp"
#include "Tools.hpp"
// Config
namespace config::adapter
{
static const int memorySize = 20 * 1025; // 20ko
}
// AdapterParams
AdapterParams::AdapterParams()
:size(config::adapter::memorySize)
{
}
AdapterParams::~AdapterParams()
{
if (adapters)
{
free(adapters);
}
for (auto* adapterName : adapterNameList)
{
delete adapterName;
}
for (auto* addressName : addressNameList)
{
delete addressName;
}
}
// Adapter
bool Adapter::initSocket() const
{
WSADATA d;
if (WSAStartup(MAKEWORD(2, 2), &d))
{
printf("initSocket() failed.\n");
return false;
}
return true;
}
void Adapter::cleanSocket() const
{
WSACleanup();
}
bool Adapter::loadAdapters(AdapterParams& _params) const
{
_params.adapters = (PIP_ADAPTER_ADDRESSES)malloc(config::adapter::memorySize);
if (!_params.adapters)
{
fprintf(stderr, "loadAdapters(1) failed.|size=%ld\n", _params.size);
return false;
}
int adpaterResult = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, 0, _params.adapters, &_params.size);
if (adpaterResult == ERROR_BUFFER_OVERFLOW)
{
fprintf(stderr, "loadAdapters(2) failed.|size=%ld\n", _params.size);
return false;
}
PIP_ADAPTER_ADDRESSES adapter = _params.adapters;
while (adapter)
{
AdapterNameParams* adapterName = _params.addAdapterName();
adapterName->name = oTools.toString(adapter->FriendlyName);
PIP_ADAPTER_UNICAST_ADDRESS address = adapter->FirstUnicastAddress;
while (address)
{
int family = address->Address.lpSockaddr->sa_family;
if (family == AF_INET || family == AF_INET6)
{
AdapterAddressParams* addressName = _params.addApaterAddress(adapterName);
addressName->family = (family == AF_INET) ? "IPv4" : "IPv6";
char addressIP[100];
getnameinfo(address->Address.lpSockaddr,
address->Address.iSockaddrLength,
addressIP, sizeof(addressIP), 0, 0, NI_NUMERICHOST);
addressName->address = addressIP;
}
address = address->Next;
}
adapter = adapter->Next;
}
return true;
}// AdapterUnix.cpp#include "Adapter.hpp"
// AdapterParams
AdapterParams::AdapterParams()
{
}
AdapterParams::~AdapterParams()
{
if (addresses)
{
freeifaddrs(addresses);
}
for (auto* adapterName : adapterNameList)
{
delete adapterName;
}
for (auto* addressName : addressNameList)
{
delete addressName;
}
}
// Adapter
bool Adapter::initSocket() const
{
return true;
}
void Adapter::cleanSocket() const
{
}
bool Adapter::loadAdapters(AdapterParams& _params) const
{
if (getifaddrs(&_params.addresses) == -1)
{
fprintf(stderr, "loadAdapters() failed.\n");
return false;
}
struct ifaddrs* address = _params.addresses;
while (address)
{
if (address->ifa_addr == nullptr)
{
address = address->ifa_next;
continue;
}
int family = address->ifa_addr->sa_family;
if (family == AF_INET || family == AF_INET6)
{
AdapterNameParams* adapterName;
if (!_params.getAdapterName(&adapterName, address->ifa_name))
{
adapterName = _params.addAdapterName();
adapterName->name = address->ifa_name;
}
AdapterAddressParams* addressName = _params.addApaterAddress(adapterName);
addressName->family = (family == AF_INET) ? "IPv4" : "IPv6";
char ap[100];
const int family_size = (family == AF_INET) ?
sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
getnameinfo(
address->ifa_addr, family_size, ap, sizeof(ap), 0, 0, NI_NUMERICHOST);
addressName->address = ap;
}
address = address->ifa_next;
}
return true;
}Tools.hpp#pragma once
// Windows
#if defined (_WIN32)
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif
#include <windows.h>
#endif
// Common
#include <string>
#define oTools Tools::Instance()
class Tools
{
private:
explicit Tools();
public:
~Tools();
static Tools& Instance();
std::string toString(const wchar_t* _data) const;
};Tools.cpp#include "Tools.hpp"
Tools::Tools()
{
}
Tools::~Tools()
{
}
Tools& Tools::Instance()
{
static Tools instance;
return instance;
}
// string
std::string Tools::toString(const wchar_t* _data) const
{
std::wstring ws(_data);
return std::string(ws.begin(), ws.end());
}// CMakeLists.txtcmake_minimum_required(VERSION 3.10.0)
project(c01-adapter-list VERSION 0.1.0 LANGUAGES C CXX)
set(SRC_FILES
main.cpp
Adapter.hpp
Adapter.cpp
Tools.hpp
Tools.cpp
)
if(WIN32)
list(APPEND SRC_FILES
AdapterWin.cpp
)
else()
list(APPEND SRC_FILES
AdapterUnix.cpp
)
endif()
add_executable(${PROJECT_NAME}
${SRC_FILES}
)Nous avons pu afficher la liste des cartes réseau sur les systèmes Windows.
// TerminalNous avons pu afficher la liste des cartes réseau sur les systèmes Linux ou MacOS.
// Terminal