Carambolas est une bibliothèque de support à usage général en constante évolution composée de plusieurs assemblages .NET Standard 2.0 . En particulier, Il dispose d'une implémentation de protocole UDP multi-canal personnalisée destinées aux applications de réseau de produits à faible latence / faible bande passante avec des contraintes en temps réel. .
La première version est un noyau minimal avec un module de réseau entièrement fonctionnel. Ce référentiel est structuré autour d'une seule solution car je prévois de développer en ajoutant de nouveaux modules à l'avenir.
La couverture du test est toujours minime, donc aucun niveau d'exactitude ne doit être implicite sans une inspection étroite du code source. Il s'agit d'un projet en cours dans ses premiers stades.
Les binaires devront bientôt être disponibles dans la section des archives ou sous la forme de forfaits NuGet.
L' hôte local est représenté par une instance de carambolas.net.host . Il doit être utilisé pour se connecter à un hôte distant ou accepter les connexions entrantes.
Chaque hôte distant est représenté par une instance de carambolas.net.peer .
Des événements tels que la connexion, la déconnexion et les données sont reçus via l'objet hôte tandis que les objets homologues peuvent être utilisés pour envoyer des données ou se déconnecter activement.
À ce stade, les opérations de connexion, de déconnexion, d'envoi et de réception ne sont pas bloquantes; Open et Close bloquent (pour des raisons évidentes).
Notez que le même objet hôte peut être utilisé pour demander activement des connexions et accepter les connexions entrantes qui le rendent tout de même, ce qui le rend utilisable dans les topologies P2P. Les rôles clients / serveur ne sont pas appliqués et émergent simplement par la configuration d'un hôte. Un hôte peut même envoyer des demandes de connexion à plusieurs hôtes distants simultanément.
Exemples :
L'objet hôte instancié pour se connecter à un homologue distant. La boucle intérieure est responsable de s'assurer que les événements n'accumulent pas à la prochaine itération.
using ( var host = new Host ( "MyHost" )
{
host . Open ( IPEndPoint . Any , new Host . Settings ( 0 ) ) ;
host . Connect ( new IPEndPoint ( IPAddress . Loopback , 1313 ) , out Peer peer ) ;
.. .
while ( true )
{
while ( host . TryGetEvent ( out Event e ) )
{
if ( e . EventType == EventType . Data )
Console . WriteLine ( $ "DATA: { e . Peer } { e . Data } " ) ;
else if ( e . EventType == EventType . Connection )
Console . WriteLine ( $ "CONNECTED: { e . Peer } " ) ;
else if ( e . EventType == EventType . Disconnection )
{
Console . WriteLine ( $ "DISCONNECTED: { e . Peer } { e . Reason } " ) ;
return ;
}
}
Thread . Sleep ( 33 ) ;
}
}L'objet hôte a instancié pour attendre jusqu'à 10 connexions entrantes. La boucle intérieure est responsable de s'assurer que les événements n'accumulent pas à la prochaine itération.
using ( var host = new Host ( "MyHost" )
{
host . Open ( new IPEndPoint ( IPAddress . Loopback , 1313 ) , new Host . Settings ( 10 ) ) ;
.. .
while ( true )
{
while ( host . TryGetEvent ( out Event e ) )
{
if ( e . EventType == EventType . Data )
Console . WriteLine ( $ "DATA: { e . Peer } { e . Data } " ) ;
else if ( e . EventType == EventType . Connection )
Console . WriteLine ( $ "CONNECTED: { e . Peer } " ) ;
else if ( e . EventType == EventType . Disconnection )
{
Console . WriteLine ( $ "DISCONNECTED: { e . Peer } { e . Reason } " ) ;
return ;
}
}
Thread . Sleep ( 33 ) ;
}
} Ce projet remonte à 2015 lorsque je suis venu au Canada pour étudier la conception et le développement de jeux vidéo à la Toronto Film School. La motivation originale était de créer une compilation de classes accessoires qui pourraient être réutilisées dans plusieurs projets Unity3D. Après un certain temps, j'ai commencé à rechercher des solutions de réseau pour un jeu multijoueur de prospect et l'attention s'est déplacée vers la conception d'un module réseau réutilisable. Initialement, j'ai abordé le problème comme une simple question d'intégrer l'UNET ou toute autre bibliothèque tierce appropriée que je pouvais trouver à l'époque. Peu de temps après, j'ai commencé à me heurter à toutes sortes de problèmes, des hypothèses brisées aux compromis de mise en œuvre cachés. Il n'était pas rare de trouver des listes de fonctionnalités gonflées (presque trompeuses), des incompatibilités de conception ou des implémentations brisées simples. En particulier, ce qui m'a le plus dérangé, c'est que de nombreux aspects des solutions semblaient arbitraires au hasard avec peu ou pas d'explication de la raison pour laquelle cette approche a été préférée ou une certaine limite imposée. Je passais des heures à inspecter la source d'un projet à prendre des notes pour comprendre pourquoi quelque chose était la façon dont il s'agissait seulement de réaliser plus tard qu'une autre partie du code était en contradiction directe.
Tout cela m'a conduit dans plus de travail et j'ai finalement décidé de créer moi-même une bibliothèque de réseau légère avec une liste de fonctionnalités raisonnable que je pouvais implémenter et vérifier. Pas de ruée, pas de délais. Juste une véritable tentative de mise en œuvre de la meilleure solution technique que je puisse concevoir.
Pendant ce temps, j'ai obtenu mon diplôme, je suis revenu à un emploi à temps plein et j'ai dû mettre ce projet de côté. Il y a un an, après avoir trouvé de vieilles notes, j'ai restauré mes archives de prototypes et décidé de créer une version complète avec toutes les informations que j'ai recueillies afin que non seulement les autres puissent l'expérimenter, mais aussi comprendre la façon dont cela fonctionnait et pourquoi.
Les assemblages gérés peuvent être construits dans n'importe quelle plate-forme avec un compilateur qui prend en charge C # 7.3 ou plus. Les tests et les applications accessoires nécessitent Netcore 2.2.
Les bibliothèques natives peuvent être construites à l'aide de CMake avec GCC ou Visual Studio.
Versions du système d'exploitation pris en charge:
Pour toute autre plate-forme, ou en l'absence d'une bibliothèque native requise, le code de secours existe qui, bien qu'en général, peut être moins efficace doit être entièrement fonctionnel et transparent.
Tous les projets C # et les scripts de construction sont configurés pour stocker des fichiers et des binaires intermédiaires dans un dossier de build situé sur la racine du projet afin que les builds puissent être facilement inspectés, vérifiés et nettoyés.
Le code utilise Dllimport pour lier les bibliothèques natives. Dllimport peut toujours utiliser les noms de la bibliothèque Windows et ajoutera automatiquement les préfixes / suffixes d'autres plates-formes selon les besoins. Par exemple, carambolas.net.native.dll, le nom de la bibliothèque native Net sur Windows, devient libcarambolas.net.native.dll.so sur Linux et Libcarambolas.net.native.dll.dynlib sur macOS. La construction de scripts créent déjà les bibliothèques sous les noms propres.
Une solution Visual Studio est incluse pour plus de commodité, donc aucune étape de construction supplémentaire ne devrait être requise pour Windows. Assurez-vous uniquement de sélectionner la plate-forme correspondant à votre système d'exploitation hôte (X86 ou X64). Ceci est nécessaire pour créer les applications de test et pour les tests unitaires. Tous les assemblages .NET sont conçus pour AnyCPU , quelle que soit la plate-forme de solution sélectionnée, mais Visual Studio doit savoir quelles bibliothèques natives à construire pour les tests, car ils devraient être déployés côte à côte avec leurs assemblages associés.
Utilisez Nugetpack.bat pour compiler la bibliothèque native et les assemblages portables et créer des packages NuGet en une seule action.
Utilisez Build.bat pour créer tous les projets pour la sortie sans utiliser Visual Studio.
Visual Studio pour Mac n'a pas été testé et n'est pas pris en charge, alors ne vous attendez pas à ce que cela fonctionne.
Assurez-vous d'avoir Cmake (> = 2.8) et GCC pour pouvoir compiler la bibliothèque native. Dotnet Core SDK 2.1 est nécessaire pour compiler les assemblages et générer des packages NuGet.
Utilisez Nugetpack.sh pour compiler la bibliothèque native et les assemblages portables et créer des packages NuGet en une seule action.
Utilisez Build.sh pour construire tous les projets pour la version sans utiliser Visual Studio.
Assurez-vous que Cmake (> = 2,8), l'essentiel et le GCC-Multilib installés pour pouvoir compiler la bibliothèque native pour X86 et X64.
Sur Ubuntu Run: $ sudo apt-get install build-essentiel gcc-multilib g ++ - multibs cmake
Dotnet Core SDK 2.1 est nécessaire pour compiler des assemblages et générer des packages NuGet.
Sur Ubuntu Run:
$ wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
$ sudo dpkg -i packages-microsoft-prod.deb
$ sudo apt-get update;
sudo apt-get install -y apt-transport-https &&
sudo apt-get update &&
sudo apt-get install -y dotnet-sdk-2.1
Utilisez Nugetpack.sh pour compiler la bibliothèque native et les assemblages portables et créer des packages NuGet en une seule action.
Utilisez Build.sh pour construire tous les projets pour la version sans utiliser Visual Studio.
Un ensemble minimum de tests unitaires est implémenté autour des caractéristiques clés à l'aide de projets Xunit.
Carambolas.net.tests.host est une application de console simple utilisée pour vérifier manuellement les fonctionnalités de base du réseau. C'est particulièrement utile lorsqu'il est partisan avec Wireshark et Clumsy
Carambolas.net.tests.Integration est un ensemble de tests d'intégration pour carambolas.net également implémenté avec Xunit. Les tests s'exécutent séquentiellement, chacun commençant deux threads séparés (un serveur et un client), qui communiquent sur l'interface de bouclage pendant une durée spécifique. Le bouclage représente un réseau idéal où le temps aller-retour est minimum, les paquets n'arrivent jamais en panne et ne sont jamais perdus à moins qu'il y ait un débordement de bufffer. Ces caractéristiques sont utiles pour valider les chemins d'exécution normaux.
Wireshark est un outil de débogage inestimable qui peut être utilisé pour surveiller l'activité du réseau et inspecter les paquets. De plus, Wireshark prend en charge une classe spéciale de plugins appelés dissecteurs qui peuvent être utilisés pour analyser les protocoles personnalisés.
Ce projet comprend un dissecteur de base de Wireshark pour Carambolas. Pour l'utiliser, assurez-vous que Wireshark est déjà installé.
Clumsy est un programme de capture de paquets réseau qui s'exécute en mode utilisateur et est capable d'intercepter des paquets pour simuler des conditions de réseau dégradées en temps réel.
Ajoutez une ligne de filtre prédéfinie comme ce qui suit dans le fichier config.txt pour affecter les hôtes connectés par l'interface de bouclage sur le même port utilisé dans les tests d'intégration (1313):
carambolas: udp and outbound and loopback and (udp.DstPort == 1313 or udp.SrcPort == 1313)
Notez qu'il y a quelques mises en garde lors de l'utilisation de Clumsy avec l'interface de bouclage. Du manuel d'utilisation maladroite:
- Les paquets entrants en boucle ne peuvent pas être capturés ou réinjectés. Lorsque vous y réfléchissez, il est vraiment difficile de dire que c'est un paquet entrant ou sortant lorsque vous envoyez des paquets de l'ordinateur à lui-même. En fait, la plate-forme de filtrage Windows sous-jacente semble classer tous les paquets de bouclage comme sortants. La chose à retenir est que lorsque vous traitez sur des paquets de bouclage, vous ne pouvez pas avoir "entrant" dans votre filtre. Il est important de savoir que votre ordinateur peut avoir des IP autres que 127.0.0.1, comme une IP intranet allouée par votre routeur. Ceux-ci sont également considérés comme des paquets de bouclage.
- Les paquets de bouclage sont capturés deux fois. Étant donné que nous n'avons pas de paquets de boucles entrants, tous les paquets de bouclage sont considérés comme sortants. Donc, Clumsy les traitera deux fois: la première fois, c'est lors de l'envoi et la deuxième fois lors de la réception. Un exemple simple est que lorsque le filtre est simplement «sortant» et appliquez un décalage de 500 ms. Lorsque vous pingz localhost, ce serait un décalage de 1000 ms. Vous pouvez le contourner en spécifiant le port de destination et des choses comme celle-ci. Mais il serait plus facile de garder cela à l'esprit et de faire attention lors de la définition des paramètres.
- La capture des paquets entrants ne fonctionne pas tout le temps. Comme indiqué précédemment, les paquets entrants en boucle ne peuvent pas être réinjectés. Le problème est qu'à l'occasion, certains paquets peuvent être classés comme paquets entrants, même si l'IP de destination n'est pas de votre ordinateur. Cela n'affecte que les paquets sans boucle. Si vous ne travaillez que sur localhost, ça ira bien. Le but de la libération future est de diagnostiquer ce qui a causé cela et de fournir une solution.
- Impossible de filtrer en fonction du système de processus La capture de réseau à l'échelle est répertoriée comme une fonctionnalité. Mais c'est vraiment qu'il n'y a pas de moyen facile de fournir une solution robuste.
Je suis toujours ouvert aux contributions, soit sous forme de rapports de bogues, de corrections de bogues (encore mieux!) Ou de couverture de test améliorée.
Les demandes de fonctionnalités sont les bienvenues, mais peuvent être conservées dans un arriéré en fonction de leur étendue, de la manière réalisable ou souhaitable. Si une demande de fonctionnalité est trop complexe, elle peut dépendre du parrainage car j'ai des ressources limitées (temps et argent) à consacrer.
Si vous souhaitez soutenir ce projet, je pourrais être intéressé à vous entendre, alors contactez!
En portugais, Carambolas est la forme plurielle de Carambola (= étoile). Le terme est également utilisé familièrement dans certaines régions du Brésil pour exprimer un étonnement ou une impatience.
Avant Carambolas, j'ai fait au moins une demi-douzaine de tentatives d'organiser mes idées dans un projet utilisable. Avec Carambolas, j'ai décidé de construire une série de prototypes afin de se renseigner sur les problèmes de conception et de tester différentes approches. Chaque prototype avait un nom de code formé par une lettre et un nombre à partir de A1. Le code source qui a été initialement importé dans ce référentiel était la 75e itération du 9e prototype, d'où A9.
Les bibliothèques natives sont principalement fournies pour des raisons de performance, elles sont donc totalement facultatives. Il aurait été déraisonnable d'essayer de fournir une implémentation native pour chaque plate-forme possible (pensez à tous les ordinateurs de bureau, mobiles, console, intégrés ...) et compter exclusivement sur les bibliothèques natives réduirait les plates-formes cibles à seulement une poignée, peut-être de bureau (Windows, Linux et MacOS). Ainsi, en règle générale, il doit toujours y avoir une implémentation de secours dans le code géré pour toute fonctionnalité implémentée par une bibliothèque native.
Parce que les bibliothèques natives sont facultatives, le programme ne peut pas dire si un fichier manquant était censé être là ou non, d'où la raison pour laquelle il n'y a pas d'erreur lancée ou enregistrée pour une bibliothèque native manquante. Par définition, une bibliothèque native manquante n'est jamais une erreur.
En général, vous ne pouvez pas. Et vous ne devriez pas, du moins pas du point de vue de l'API. Cela ne devrait pas avoir d'importance pour l'utilisateur (ou le programmeur d'applications) quelle stratégie de mise en œuvre sous-jacente est utilisée par une dépendance, en l'occurrence Carambolas. Cependant, ces informations peuvent être pertinentes pour le déploiement, donc, chaque fois qu'un objet Interop est créé qui a également une secours automatique, le code produit des informations de journal indicatives. Par exemple, Carambolas.net.socket produira une information de journal similaire à "Utilisation de carambolas.net.sockts.native.socket" lorsqu'une bibliothèque native est trouvée pour l'implémentation de socket sous-jacente. De cette façon, si vous déployez avec des bibliothèques natives à l'esprit, vous pouvez déterminer si elles sont réellement utilisées.
Cela signifie que vous déployez une bibliothèque native qui est corrompue ou compilée pour la mauvaise architecture du CPU.
Les bibliothèques indigènes doivent aller côte à côte avec leurs assemblages d'interopt correspondants et bien que les assemblages puissent être compilés une fois pour une architecture CPU, les bibliothèques natives ne le peuvent pas. Ils doivent correspondre à l'architecture CPU du système d'exploitation en cours d'exécution, sinon ils sont traités comme des fichiers corrompus et .NET lève un système.badimageFormatexception. Notez que ce n'est pas la même chose que d'essayer de charger une bibliothèque qui n'est pas trouvée, ce qui n'est pas une erreur.
Le produit de retard de bande passante (BDP) est le produit de la capacité de transmission d'une liaison réseau (en bits par seconde) et de son temps de retard aller-retour (en quelques secondes). Il représente la quantité très maximale de données qu'un réseau peut conserver avant l'arrivée de toute accusé de réception.
Le BDP peut être utilisé pour classer les réseaux en fonction de savoir s'il est supérieur ou inférieur à un certain seuil. Les réseaux avec un grand BDP sont appelés réseaux de gros (LFN). Les LFN peuvent être des réseaux avec un temps aller-retour moyen très important (regadless de bande passante, comme dans les liaisons satellites) ou un réseau large (bande passante élevée) qui affiche des temps aller-retour considérablement petits (comme dans les liens Ethernet Gigabit).
Vérifiez le Wikipedia pour plus d'informations à ce sujet.
Un objet carammbolas.net.socket sert de façade à une implémentation de socket native ou à une implémentation de secours qui repose sur System.net.sockets.socket. Il aide à découpler et à réduire la complexité des objets hôtes et homologues. Reportez-vous à Doc / Readme-Carambolas.net pour plus d'informations.
System.net.ipaddress et System.net.PendPoint sont des objets mutables qui favorisent un certain nombre d'allocations peu tarines dans toutes les impléments de courant de .NET Core et .NET Framework. Carambolas.net.ipaddress et carambolas.net.pendpoint sont des types de valeur immuables qui contribuent à réduire la pression GC. Reportez-vous à Doc / Readme-Carambolas.net pour plus d'informations.
AEAD avec Chacha20 et Poly1305 est pris en charge à l'extérieur de la boîte. Des stratégies personnalisées peuvent être mises en œuvre en fournissant à l'hôte des implémentations de carambolas.net.iccipher et carambolas.net.ICIPherfactory interfaces. Les seules exigences sont:
Une application utilisateur est libre de compresser ses données avant l'envoi, mais il n'y a actuellement aucun mécanisme pour fournir une compression / décompression automatique des messages individuels ou des paquets complets.
Tout le code source et tous les binaires produits pour être déployés aux côtés d'une application utilisateur sont sous licence MIT.
Carambolas.CommandLineArguments était basé sur un article de Griffonrl avec le code source publié sous une licence MIT avec des idées supplémentaires d'un autre article de Jake Ginnivan qui a étendu la source originale.
Carambolas.security.criptography.crc32c était basé sur CRC32.net par force sous une licence MIT.
Carambolas.security.criptography.nacl était basé et élargi sur nacl.core par David de Smet sous une licence du MIT.
Le dissecteur de protocole écrit en LUA pour Wireshark est disponible sous une licence GPLV3. Il n'est censé être utilisé que comme fichier d'entrée pour Wireshark afin d'étendre ses capacités et de lui permettre d'afficher plus d'informations sur les paquets UDP formatés en fonction du protocole réseau Carambolas. Par conséquent, il est complètement séparé et n'interagit, ne dépend ni ne contribue de quelque manière que ce soit à des fichiers source, des assemblages ou des bibliothèques natives.