Bibliothèque réseau haute performance et facile à utiliser prenant en charge plus de 16 000 clients simultanés sur toutes les topologies fournies.
Conçu pour les applications simultanées distribuées en temps réel sur LAN ou Internet.
Fournit une infrastructure pour la transmission de messages à haut débit, P2P, Nat Traversal, Reliable Udp.
Ce référentiel se compose d'un assemblage de base principal et de plusieurs sous-assemblages spécifiques à la sérialisation.
Veuillez consulter la page Wiki pour une documentation détaillée.
Bibliothèque réseau, comprend toute la logique associée aux systèmes réseau, depuis les octets bruts jusqu'aux abstractions telles que les lobbys P2P. Il fournit des modèles génériques à utiliser avec tout type de sérialisation.
Modèles Plug&Play hautes performances, fonctionnant avec des octets bruts. Également utilisé comme base pour les modèles de niveau supérieur.
Tcp Server/Client model
avec sous-systèmes de mise en mémoire tampon dynamique et de file d'attente, les octets peuvent être fragmentés comme des sockets ordinaires.Tcp Byte Message Server/Client
où les octets sont envoyés avec un en-tête de 4 octets. Il garantit la livraison de messages atomiques sans fragmentation.Udp Server/Client
Udp où un serveur est émulé avec des performances optimisées.Reliable Udp Client/Server
où le protocole TCP moderne est implémenté sur Udp.Implique des modèles génériques qui peuvent fonctionner avec n’importe quel protocole de sérialisation.
Generic Message Server/Client
conçu pour envoyer et recevoir des messages sérialisés de manière atomique.Generic MessageProtocol Server/client
similaire à ci-dessus, mais avec l'ajout de la classe de support "MessageEnvelope", utilisée comme en-tête/métadonnées.P2P Relay Client/Server
où les pairs (clients) se découvrent via le serveur relais, peuvent utiliser Udp/Rudp/Tcp pour communiquer. Prend en charge TCP et UDP Holepunch.P2P Room/Lobby Server/Client
du modèle Relay où les pairs peuvent définir une salle, similaire aux serveurs de mise en relation de jeux.La bibliothèque est testée avec autant de clients que le système d'exploitation (Windows) prend en charge (environ 16 000 ports dynamiques). La fiabilité des données, y compris RUDP, est testée de manière approfondie sur Internet. La perforation Nat Traversal Udp est également testée avec succès sur Internet.
Remarque : Libary a des allocations de code et de mémoire de pile non sécurisées. Les sections dangereuses sont bien testées et ne sont pas sujettes à changement.
Les modèles génériques de l'assemblage principal sont implémentés avec le sérialiseur spécifique. La raison de cette division est d'éviter les dépendances inutiles. Toutes les signatures et utilisations de méthodes sont identiques. Il comprend :
Les packages Nuget sont disponibles :
Bibliothèque du réseau central | Protobuf | MessagePack | NetSéraliseur | Json |
---|---|---|---|---|
Les benchmarks Infinite Echo sont effectués en envoyant un ensemble de messages au serveur et en obtenant une réponse d'écho. Chaque réponse provoque une nouvelle demande. Chaque réponse du serveur compte pour 1 écho.
1 000 messages de départ (message de 32 octets + 4 en-têtes) chacun :
Ordinateur portable AMD Ryzen 7 5800H
Nombre de clients | Écho TCP par seconde | Écho SSL par seconde |
---|---|---|
100 | 53 400 000 | 41 600 000 |
1000 | 43 600 000 | 22 200 000 |
5000 | 43 400 000 | 21 800 000 |
10000 | 42 800 000 | 21 700 000 |
Ordinateur portable Intel i9 13980HX
Nombre de clients | Écho TCP par seconde | Écho SSL par seconde |
---|---|---|
100 | 128 800 000 | 79 400 000 |
1000 | 110 400 000 | 72 100 000 |
5000 | 102 100 000 | 67 500 000 |
10000 | 100 500 000 | 65 500 000 |
1 000 enveloppes de messages de départ (charge utile de 32 octets, 48 octets au total) :
Ordinateur portable AMD Ryzen 7 5800H
Nombre de clients | Écho Protobuf par seconde | Écho Protobuf sécurisé par seconde |
---|---|---|
100 | 9 440 000 | 8 050 000 |
1000 | 8 780 000 | 7 480 000 |
5000 | 8 360 000 | 7 390 000 |
10000 | 8 340 000 | 7 350 000 |
Ordinateur portable Intel i9 13980HX
Nombre de clients | Écho Protobuf par seconde | Écho Protobuf sécurisé par seconde |
---|---|---|
100 | 31 200 000 | 20 650 000 |
1000 | 30 500 000 | 19 500 000 |
5000 | 28 200 000 | 17 650 000 |
10000 | 26 400 000 | 16 000 000 |
Ce test n'envoie qu'une enveloppe de message avec une charge utile en octets bruts. Pour connaître les performances spécifiques à la sérialisation, veuillez vous référer à : SerializationBenchmarks
Pour des informations détaillées, consultez AsyncTcpClient/Server et ByteMessageTcpClient/Server
Tout morceau de tableau d'octets ou de segment de tableau atteindra la destination sans fragmentation.
private static void ExampleByteMessage ( )
{
ByteMessageTcpServer server = new ByteMessageTcpServer ( 20008 ) ;
server . OnBytesReceived += ServerBytesReceived ;
server . StartServer ( ) ;
ByteMessageTcpClient client = new ByteMessageTcpClient ( ) ;
client . OnBytesReceived += ClientBytesReceived ;
client . Connect ( "127.0.0.1" , 20008 ) ;
client . SendAsync ( Encoding . UTF8 . GetBytes ( "Hello I'm a client!" ) ) ;
void ServerBytesReceived ( Guid clientId , byte [ ] bytes , int offset , int count )
{
Console . WriteLine ( Encoding . UTF8 . GetString ( bytes , offset , count ) ) ;
server . SendBytesToClient ( clientId , Encoding . UTF8 . GetBytes ( "Hello I'm the server" ) ) ;
}
void ClientBytesReceived ( byte [ ] bytes , int offset , int count )
{
Console . WriteLine ( Encoding . UTF8 . GetString ( bytes , offset , count ) ) ;
}
}
output:
Hello I'm a client!
Hello I'm the server
Remarque : Le paramètre de performances important ici est de savoir s'il faut utiliser un tampon ou une file d'attente comme stratégie de mise en mémoire tampon. Utilisez Buffer si les messages sont principalement des régions d'octet[] telles que (buffer, offset,count). Utilisez Queue si les messages contiennent un octet complet [] (0 à la fin).
client . GatherConfig = ScatterGatherConfig . UseQueue ;
server . GatherConfig = ScatterGatherConfig . UseBuffer ;
Pour les variantes SSL, la différence est :
var ccert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
// null certificate or default constructor will generate self signed certificate
client = new SslByteMessageClient ( ccert ) ;
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
// null certificate or default constructor will generate self signed certificate
server = new SslByteMessageServer ( 8888 , scert ) ;
// You can override the SSL cerificate validation callback
server . RemoteCertificateValidationCallback += .. .
client . RemoteCertificateValidationCallback += .. .
Pour plus d'informations, consultez SSLClient/Server et SSLByteMessageClient/Server
Serveur/Client de base où les octets bruts sont transférés. Les signatures de méthode et de rappel sont identiques aux modèles de message d'octet. Aucun protocole n'est implémenté sur le serveur/client de base, par conséquent les octets peuvent être fragmentés en fonction de la taille de votre MTU.
AsyncTcpServer server = new AsyncTcpServer ( port : 20000 ) ;
AsyncTpcClient client = new AsyncTpcClient ( ) ;
// SSL variant
// null certificate or default constructor will generate self signed certificate
var ccert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
SslServer server = new SslServer ( 2000 , scert ) ;
SslClient client = new SslClient ( ccert ) ;
Les réseaux sérialisés sont des implémentations de classes génériques fournies par Core Library. Ils sont applicables à tous les protocoles de sérialisation.
Pour plus d'infos : Réseau sérialisé
Examples here is only given for Protobuf-net, but signature is identical for any other provided serialization protocol(MessagePack, Json etc)
.
Implémente un modèle client serveur dans lequel les messages sérialisés sont transférés de manière atomique. Déclarez votre type :
[ ProtoContract ]
class SampleMessage
{
[ ProtoMember ( 1 ) ]
public string sample ;
}
PureProtoServer server = new PureProtoServer ( 11234 ) ;
server . StartServer ( ) ;
server . BytesReceived += ( clientId , bytes , offset , count ) =>
{
SampleMessage msg = server . Serializer . Deserialize < SampleMessage > ( bytes , offset , count ) ;
Console . WriteLine ( msg . sample ) ;
msg . sample = "Jesse Lets cook" ;
server . SendAsync ( clientId , msg ) ;
} ;
PureProtoClient client = new PureProtoClient ( ) ;
client . Connect ( "127.0.0.1" , 11234 ) ;
client . BytesReceived += ( bytes , offset , count ) =>
{
SampleMessage msg = client . Serializer . Deserialize < SampleMessage > ( bytes , offset , count ) ;
Console . WriteLine ( msg . sample ) ;
} ;
client . SendAsync ( new SampleMessage ( ) { sample = "Yo! Mr White" } ) ;
Le protocole de message est implémenté pour encapsuler tous les types de messages dynamiques avec un en-tête standard. Veuillez vous référer au protocole de message pour une explication détaillée.
Vous pouvez déclarer vos types de charge utile, quel que soit le type sérialisable avec protobuf.
[ ProtoContract ]
class SamplePayload : IProtoMessage
{
[ ProtoMember ( 1 ) ]
public string sample ;
}
Exemple pour la variante sécurisée :
private static async Task ExampleProtoSecure ( )
{
// null certificate or default constructor will generate self signed certificate
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
var cert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
SecureProtoMessageServer server = new SecureProtoMessageServer ( 20008 , scert ) ;
server . StartServer ( ) ;
server . OnMessageReceived += ServerMessageReceived ;
var client = new SecureProtoMessageClient ( cert ) ;
client . OnMessageReceived += ClientMessageReceived ;
client . Connect ( "127.0.0.1" , 20008 ) ;
var Payload = new SamplePayload ( ) { sample = "Hello" } ;
var messageEnvelope = new MessageEnvelope ( ) ;
messageEnvelope . Header = "PayloadTest" ;
// You can just send a message, get replies on ClientMessageReceived.
client . SendAsyncMessage ( messageEnvelope ) ;
client . SendAsyncMessage ( messageEnvelope , Payload ) ;
// Or you can wait for a reply async.
MessageEnvelope result = await client . SendMessageAndWaitResponse ( messageEnvelope , Payload ) ;
var payload = result . UnpackPayload < SamplePayload > ( ) ;
Console . WriteLine ( $ "Client Got Response { payload . sample } " ) ;
void ServerMessageReceived ( Guid clientId , MessageEnvelope message )
{
Console . WriteLine ( $ "Server Received message { message . Header } " ) ;
server . SendAsyncMessage ( clientId , message ) ;
}
void ClientMessageReceived ( MessageEnvelope message )
{
}
}
ProtoMessageServer
et ProtoMessageClient
ont des signatures identiques sauf que les constructeurs ne prennent pas de certificat Ce modèle est ce que j'utilise personnellement sur mes autres projets tels que P2PVideocall et Multiplayer Starfighter Game. Fondamentalement, vous disposez d'un serveur relais quelque part dans votre réseau, qui peut agir comme un hub de réseau local sur le réseau local et/ou s'ouvrir aux connexions depuis Internet si la redirection de port est activée.
Les clients relais (pairs) se connectent au serveur relais et reçoivent des notifications sur l'existence d'autres pairs. Les pairs peuvent s'envoyer des messages via le serveur relais ou directement entre eux (Udp Holepunch).
Consultez les informations détaillées sur P2P Fore
Le serveur est complètement passif, permettant à d’autres pairs de se découvrir et de s’envoyer des messages. De plus, des méthodes de traversée NAT telles que la perforation UDP sont fournies pour permettre une communication directe via Internet ou LAN (UDP seulement jusqu'à présent, mais nous disposons d'un udp fiable).
Relay Server Is Serialization Agnostic
ce qui signifie que tous les pairs du réseau sérialisé (Protobuff, MessagePack, etc.) peuvent utiliser le même serveur relais.
Pour utiliser le serveur Relay, déclarez simplement votre serveur comme :
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
var server = new SecureProtoRelayServer ( 20010 , scert ) ;
server . StartServer ( ) ;
Le serveur relais est déjà préconfiguré.
Le client relais est l'endroit où la logique de votre application est implémentée. Vous pouvez utiliser vos applications client sur le Web pour vous découvrir et discuter entre elles.
Pour décréter un client :
// null certificate or default constructor will generate self signed certificate
var cert = new X509Certificate2 ( "client.pfx" , "greenpass" ) ;
var client = new RelayClient ( cert ) ;
client . OnPeerRegistered += ( Guid peerId ) => ..
client . OnPeerUnregistered += ( Guid peerId ) => ..
client . OnMessageReceived += ( MessageEnvelope message ) => ..
client . OnUdpMessageReceived += ( MessageEnvelope message ) => ..
client . OnDisconnected += ( ) => ..
client . Connect ( "127.0.0.1" , 20010 ) ;
Les signatures et les rappels de méthodes sont identiques au modèle proto client/serveur (également avec les charges utiles). La seule différence est que vous devez spécifier l’ID Guid du homologue de destination. Il provient de l'événement OnPeerRegistered, chaque fois qu'un nouveau homologue est connecté au serveur relais. Relay Server garantit la synchronisation de l'ensemble de pairs actuel avec une cohérence éventuelle entre tous les pairs. Ainsi, les nouveaux pairs recevront tous les autres pairs connectés à cet événement et les anciens pairs recevront une mise à jour.
client . SendAsyncMessage ( destinationPeerId , new MessageEnvelope ( ) { Header = "Hello" } ) ;
client . SendUdpMesssage ( destinationPeerId , new MessageEnvelope ( ) { Header = "Hello" } ) ;
// Or with an async reply
MessageEnvelope response = await client . SendRequestAndWaitResponse ( destinationPeerId ,
new MessageEnvelope ( ) { Header = "Who Are You?" } ) ;
Les messages Udp peuvent dépasser la limite de datagramme de 65 527 octets. Le système détecte les messages UDP volumineux en tant que messages Jumbo et les envoie par morceaux. La fin de la réception tentera de reconstruire le message. si toutes les pièces n'arrivent pas dans un délai d'attente, le message est supprimé. La taille maximale des messages pour UDP est de 16 256 000 octets.
Le protocole UDP fiable utilise comme algorithme TCP implémenté sur UDP.
client . SendRudpMessage ( peerId , envelope ) ;
client . SendRudpMessage ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope ) ;
Prise en charge Nat Traversal/Holepunch :
// Udp
bool result = await client . RequestHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
// Tcp
bool result = await client . RequestTcpHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
en cas de succès, cela vous permettra d'envoyer des messages UDP directs entre les pairs actuels et de destination pour le reste des messages UDP dans les deux sens.
Il s'agit d'une extension de Relay Server/Client. L'ajout est le système de salle où les pairs peuvent créer ou rejoindre des salles, interroger les salles disponibles, envoyer des messages aux salles (multidiffusion). En outre, conserver le même système de messagerie pour envoyer des messages 1-1 entre pairs.
Vous pouvez rejoindre plusieurs salles
Room Server Is Serialization Agnostic
ce qui signifie que tous les pairs du réseau sérialisé (Protobuf, MessagePack, etc.) peuvent utiliser le même serveur de salle.
Déclaration du serveur et du client
var server = new SecureProtoRoomServer ( 20010 , scert ) ;
server . StartServer ( ) ;
var client1 = new SecureProtoRoomClient ( cert ) ;
Pour créer/rejoindre et quitter des salles simplement :
client1 . CreateOrJoinRoom ( "Kitchen" ) ;
client1 . LeaveRoom ( "Kitchen" ) ;
Les rappels de salle sont les suivants. Ces rappels ne sont déclenchés que si vous êtes dans la même pièce.
client1 . OnPeerJoinedRoom += ( roomName , peerId ) => ..
client1 . OnPeerLeftRoom += ( roomName , peerId ) => ..
client1 . OnPeerDisconnected += ( peerId ) => ..
En plus du rappel standard par message 1-1, nous avons des rappels de messages de salle.
client1 . OnTcpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnUdpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnTcpMessageReceived += ( message ) => ..
client1 . OnUdpMessageReceived += ( message ) => ..
Toutes les variantes TCP sécurisées implémentent un socket SSL standard avec authentification/validation TLS. Pour plus d'informations, consultez : Sécurité