Biblioteca de red de alto rendimiento y fácil de usar que admite más de 16.000 clientes simultáneos en todas las topologías proporcionadas.
Diseñado para aplicaciones concurrentes distribuidas en tiempo real a través de LAN o Internet.
Proporciona infraestructura para el paso de mensajes de alto rendimiento, P2P, Nat Traversal, Udp confiable.
Este repositorio consta de un ensamblaje central principal y varios subconjuntos específicos de serialización.
Consulte la página Wiki para obtener documentación detallada.
Biblioteca de red: incluye toda la lógica asociada con los sistemas de red, desde bytes sin formato hasta abstracciones como vestíbulos P2P. Proporciona plantillas genéricas para usar con cualquier tipo de serialización.
Modelos Plug&Play de alto rendimiento, trabajando con bytes sin formato. También se utiliza como base para modelos de nivel superior.
Tcp Server/Client model
con subsistemas de cola y almacenamiento en búfer dinámicos, los bytes pueden fragmentarse como sockets normales.Tcp Byte Message Server/Client
donde se envían los bytes con un encabezado de 4 bytes de longitud. Garantiza la entrega de mensajes atómicos sin fragmentación.Udp Server/Client
Sistema udp donde se emula un servidor optimizado para el rendimiento.Reliable Udp Client/Server
donde se implementa el protocolo TCP moderno sobre Udp.Implica modelos genéricos que pueden funcionar con cualquier protocolo de serialización.
Generic Message Server/Client
diseñado para enviar y recibir mensajes serializados de forma atómica.Generic MessageProtocol Server/client
similar al anterior, pero con la adición de la clase portadora "MessageEnvelope", utilizada como encabezado/metadatos.P2P Relay Client/Server
donde los pares (clientes) se descubren entre sí a través del servidor de retransmisión, pueden utilizar Udp/Rudp/Tcp para comunicarse. Admite perforación Tcp y Udp.P2P Room/Lobby Server/Client
del modelo de retransmisión donde los pares pueden definir una sala, similar a los servidores de emparejamiento de juegos.La biblioteca se prueba con tantos clientes como admita el sistema operativo (Windows) (alrededor de 16k puertos dinámicos). La confiabilidad de los datos, incluido RUDP, se prueba exhaustivamente en Internet. La perforación Nat Traversal Udp también se prueba con éxito en Internet.
Nota: Libary tiene código inseguro y asignaciones de memoria de pila. Las secciones inseguras están bien probadas y no están sujetas a cambios.
Los modelos genéricos del ensamblaje principal se implementan con el serializador específico. El motivo de esta división es evitar dependencias innecesarias. Todas las firmas y usos de los métodos son idénticos. Incluye:
Los paquetes Nuget están disponibles:
Biblioteca de red central | Protobuf | Paquete de mensajes | NetSeralizador | json |
---|---|---|---|---|
Las pruebas comparativas de Infinite Echo se realizan enviando un conjunto de mensajes al servidor y obteniendo una respuesta de eco. Cada respuesta provoca una nueva solicitud. Cada respuesta del servidor se cuenta como 1 eco.
1000 mensajes semilla (mensaje de 32 bytes + 4 encabezados) cada uno:
Portátil AMD Ryzen 7 5800H
Número de clientes | Eco TCP por segundo | Eco SSL por segundo |
---|---|---|
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 |
Portátil Intel i9 13980HX
Número de clientes | Eco TCP por segundo | Eco SSL por segundo |
---|---|---|
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 |
1000 sobres de mensajes semilla (carga útil de 32 bytes, 48 bytes en total):
Portátil AMD Ryzen 7 5800H
Número de clientes | Eco de Protobuf por segundo | Eco Protobuf seguro por segundo |
---|---|---|
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 |
Portátil Intel i9 13980HX
Número de clientes | Eco de Protobuf por segundo | Eco seguro de Protobuf por segundo |
---|---|---|
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 |
Este punto de referencia solo envía un sobre de mensaje con una carga útil de bytes sin formato. Para conocer el rendimiento específico de la serialización, consulte: SerializationBenchmarks
Para obtener información detallada, consulte AsyncTcpClient/Server y ByteMessageTcpClient/Server
Cualquier fragmento de matriz de bytes o segmento de matriz llegará al destino sin fragmentación.
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
Nota: Una configuración de rendimiento importante aquí es si se utiliza un búfer o una cola como política de almacenamiento en búfer. Utilice Buffer si los mensajes son principalmente regiones de byte[] como (buffer, offset,count). Utilice la cola si los mensajes tienen un byte completo [] (0 hasta el final).
client . GatherConfig = ScatterGatherConfig . UseQueue ;
server . GatherConfig = ScatterGatherConfig . UseBuffer ;
Para las variantes SSL la diferencia es:
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 += .. .
Para obtener más información, consulte SSLClient/Server y SSLByteMessageClient/Server.
Servidor/Cliente base donde se transfieren los bytes sin procesar. Las firmas de método y devolución de llamada son idénticas a los modelos de mensajes de bytes. No hay ningún protocolo implementado en el servidor/cliente base, por lo que los bytes pueden estar fragmentados según el tamaño de su 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 ) ;
Las redes serializadas son implementaciones de clases genéricas proporcionadas por Core Library. Es aplicable a todos los protocolos de serialización.
Para más información: Red Serializada
Examples here is only given for Protobuf-net, but signature is identical for any other provided serialization protocol(MessagePack, Json etc)
.
Implementa un modelo de cliente de servidor donde los mensajes serializados se transfieren de forma atómica. Declara tu tipo:
[ 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" } ) ;
El protocolo de mensajes se implementa para envolver todos los tipos de mensajes dinámicos con un encabezado estándar. Consulte el Protocolo de mensajes para obtener una explicación detallada.
Puede declarar sus tipos de carga útil, cualquier tipo que sea serializable con protobuf.
[ ProtoContract ]
class SamplePayload : IProtoMessage
{
[ ProtoMember ( 1 ) ]
public string sample ;
}
Ejemplo de variante segura:
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
y ProtoMessageClient
tienen firmas idénticas excepto que los constructores no requieren un certificado. Este modelo es el que uso personalmente en mis otros proyectos, como P2PVideocall y Multiplayer Starfighter Game. Básicamente, tiene un servidor de retransmisión en algún lugar de su red, que puede actuar como un centro de red local en LAN y/o abrir conexiones desde Internet si el reenvío de puertos está habilitado.
Los clientes de retransmisión (peers) se conectan al servidor de retransmisión y reciben notificaciones sobre la existencia de otros pares. Los pares pueden enviarse mensajes entre sí a través del servidor de retransmisión o directamente entre sí (Udpholepunch).
Consulte la información detallada de P2P Fore
El servidor es completamente pasivo, lo que permite que otros pares se descubran y se envíen mensajes entre sí. Además, se proporcionan métodos transversales de NAT, como la perforación UDP, para permitir la comunicación directa a través de Internet o LAN (hasta ahora solo UDP, pero tenemos udp confiable).
Relay Server Is Serialization Agnostic
, lo que significa que cualquier par de red serializada (Protobuff, MessagePack, etc.) puede utilizar el mismo servidor de retransmisión.
Para utilizar el servidor de retransmisión, simplemente cancele su servidor como:
var scert = new X509Certificate2 ( "server.pfx" , "greenpass" ) ;
var server = new SecureProtoRelayServer ( 20010 , scert ) ;
server . StartServer ( ) ;
El servidor de retransmisión ya está preconfigurado.
El cliente de retransmisión es donde se implementa la lógica de su aplicación. Puede consultar las aplicaciones de sus clientes en la Web para descubrirse y hablar entre sí.
Para dar de baja a un cliente:
// 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 ) ;
Las firmas de métodos y las devoluciones de llamadas son idénticas al modelo proto cliente/servidor (también con cargas útiles). La única diferencia es que debe especificar el ID de Guid del par de destino. Proviene del evento OnPeerRegistered, cada vez que se conecta un nuevo par al servidor de retransmisión. Relay Server garantiza la sincronización del conjunto de pares actual con una eventual coherencia entre todos los pares. Por lo tanto, los nuevos pares recibirán a todos los demás pares conectados de este evento y los antiguos recibirán una actualización.
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?" } ) ;
Los mensajes udp pueden superar el límite de datagramas de 65.527 bytes. El sistema detecta mensajes udp grandes como mensajes Jumbo y los envía en fragmentos. El final de recepción intentará reconstruir el mensaje. si todas las piezas no llegan dentro del plazo de tiempo de espera, se elimina el mensaje. El tamaño máximo de mensaje para udp es 16.256.000 bytes.
El protocolo udp confiable se utiliza como algoritmo TCP implementado sobre UDP.
client . SendRudpMessage ( peerId , envelope ) ;
client . SendRudpMessage ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope , innerMessage ) ;
client . SendRudpMessageAndWaitResponse ( peerId , envelope ) ;
Soporte Nat Traversal/Holepunch:
// Udp
bool result = await client . RequestHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
// Tcp
bool result = await client . RequestTcpHolePunchAsync ( destinationPeerId , timeOut : 10000 ) ;
Si tiene éxito, le permitirá enviar mensajes udp directos entre los pares actuales y de destino para el resto de los mensajes udp en ambas direcciones.
Esta es una extensión de Relay Server/Cliente. La adición es el sistema de salas donde los compañeros pueden crear o unirse a salas, consultar salas disponibles y enviar mensajes a las salas (multidifusión). Además, se mantiene el mismo sistema de mensajes para enviar mensajes 1-1 entre pares.
Puedes unirte a varias salas
Room Server Is Serialization Agnostic
, lo que significa que cualquier par de red serializada (Protobuf, MessagePack, etc.) puede usar el mismo servidor de Room.
Declaración de servidor y cliente.
var server = new SecureProtoRoomServer ( 20010 , scert ) ;
server . StartServer ( ) ;
var client1 = new SecureProtoRoomClient ( cert ) ;
Para crear/unirse y salir de habitaciones simplemente:
client1 . CreateOrJoinRoom ( "Kitchen" ) ;
client1 . LeaveRoom ( "Kitchen" ) ;
Las devoluciones de llamadas de sala son las siguientes. Estas devoluciones de llamada solo se activan si estás en la misma habitación.
client1 . OnPeerJoinedRoom += ( roomName , peerId ) => ..
client1 . OnPeerLeftRoom += ( roomName , peerId ) => ..
client1 . OnPeerDisconnected += ( peerId ) => ..
Además de la devolución de llamada de mensaje 1-1 estándar, tenemos devoluciones de llamada de mensaje de sala.
client1 . OnTcpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnUdpRoomMesssageReceived += ( roomName , message ) => ..
client1 . OnTcpMessageReceived += ( message ) => ..
client1 . OnUdpMessageReceived += ( message ) => ..
Todas las variantes de TCP seguro implementan un socket SSL estándar con autenticación/validación TLS. Para más información consulte: Seguridad