Estoy en la fase de diseño de escribir una nueva aplicación de servicio de Windows que acepte conexiones TCP / IP para conexiones de larga ejecución (es decir, esto no es como HTTP donde hay muchas conexiones cortas, sino que un cliente se conecta y permanece conectado durante horas o días o incluso semanas).
Estoy buscando ideas sobre la mejor manera de diseñar la arquitectura de red. Voy a necesitar iniciar al menos un hilo para el servicio. Estoy considerando usar la API de Asynch (BeginRecieve, etc.) ya que no sé cuántos clientes me habré conectado en un momento dado (posiblemente cientos). Definitivamente no quiero iniciar un hilo para cada conexión.
Los datos fluirán principalmente a los clientes desde mi servidor, pero ocasionalmente se enviarán algunos comandos desde los clientes. Esta es principalmente una aplicación de monitoreo en la cual mi servidor envía periódicamente datos de estado a los clientes.
¿Alguna sugerencia sobre la mejor manera de hacer esto lo más escalable posible? Flujo de trabajo básico? Gracias.
EDITAR: Para ser claros, estoy buscando soluciones basadas en .net (C # si es posible, pero cualquier lenguaje .net funcionará)
NOTA DE BONIFICACIÓN: Para recibir la recompensa, espero más que una simple respuesta. Necesitaría un ejemplo funcional de una solución, ya sea como un puntero a algo que podría descargar o un breve ejemplo en línea. Y debe estar basado en .net y Windows (cualquier lenguaje .net es aceptable)
EDITAR: Quiero agradecer a todos los que dieron buenas respuestas. Desafortunadamente, solo pude aceptar uno, y elegí aceptar el método Begin / End más conocido. La solución de Esac puede ser mejor, pero sigue siendo lo suficientemente nueva como para no estar seguro de cómo funcionará.
He votado a favor de todas las respuestas que pensé que eran buenas, desearía poder hacer más por ustedes. Gracias de nuevo.
fuente
Respuestas:
He escrito algo similar a esto en el pasado. Desde mi investigación hace años, demostré que escribir su propia implementación de socket era la mejor opción, utilizando los sockets asíncronos. Esto significaba que los clientes que realmente no hacían nada realmente requerían relativamente pocos recursos. Todo lo que ocurre es manejado por el grupo de hilos .net.
Lo escribí como una clase que gestiona todas las conexiones para los servidores.
Simplemente utilicé una lista para mantener todas las conexiones del cliente, pero si necesita búsquedas más rápidas para listas más grandes, puede escribirla como quiera.
También necesita el socket realmente escuchando conexiones entrantes.
El método de inicio realmente inicia el socket del servidor y comienza a escuchar cualquier conexión entrante.
Solo me gustaría señalar que el código de manejo de excepciones se ve mal, pero la razón es que tenía un código de supresión de excepciones allí para que cualquier excepción se suprima y regrese
false
si se configuró una opción de configuración, pero quería eliminarlo por brevedad sake.El _serverSocket.BeginAccept (nuevo AsyncCallback (acceptCallback)), _serverSocket) anterior configura esencialmente nuestro socket de servidor para llamar al método acceptCallback cada vez que un usuario se conecta. Este método se ejecuta desde el conjunto de hilos .Net, que maneja automáticamente la creación de hilos de trabajo adicionales si tiene muchas operaciones de bloqueo. Esto debería manejar de manera óptima cualquier carga en el servidor.
El código anterior esencialmente acaba de terminar de aceptar la conexión que entra, las colas,
BeginReceive
que es una devolución de llamada que se ejecutará cuando el cliente envíe datos, y luegoacceptCallback
pone en cola la siguiente que aceptará la próxima conexión del cliente que ingrese.La
BeginReceive
llamada al método es lo que le dice al socket qué hacer cuando recibe datos del cliente. ParaBeginReceive
, debe darle una matriz de bytes, que es donde copiará los datos cuando el cliente envíe datos. SeReceiveCallback
llamará al método, que es cómo manejamos la recepción de datos.EDITAR: en este patrón olvidé mencionar que en esta área de código:
Lo que generalmente haría es en el código que desee, volver a ensamblar los paquetes en mensajes y luego crearlos como trabajos en el grupo de subprocesos. De esta manera, BeginReceive del siguiente bloque del cliente no se retrasa mientras se ejecuta el código de procesamiento de mensajes.
La devolución de llamada de aceptación finaliza la lectura del socket de datos llamando al final de la recepción. Esto llena el búfer proporcionado en la función de recepción de inicio. Una vez que haga lo que quiera donde dejé el comentario, llamaremos al siguiente
BeginReceive
método que ejecutará la devolución de llamada nuevamente si el cliente envía más datos. Ahora aquí está la parte realmente complicada, cuando el cliente envía datos, su devolución de llamada de recepción solo se puede llamar con parte del mensaje. El reensamblaje puede volverse muy, muy complicado. Utilicé mi propio método y creé una especie de protocolo propietario para hacer esto. Lo dejé fuera, pero si lo solicita, puedo agregarlo. Este controlador fue en realidad el código más complicado que jamás haya escrito.El método de envío anterior en realidad usa una
Send
llamada síncrona , para mí eso estuvo bien debido al tamaño de los mensajes y la naturaleza multiproceso de mi aplicación. Si desea enviar a cada cliente, simplemente necesita recorrer la lista _sockets.La clase xConnection que ves mencionada anteriormente es básicamente un contenedor simple para que un socket incluya el búfer de bytes, y en mi implementación, algunos extras.
También para referencia aquí están los
using
mensajes que incluyo, ya que siempre me molesto cuando no están incluidos.Espero que sea útil, puede que no sea el código más limpio, pero funciona. También hay algunos matices en el código que debería estar cansado de cambiar. Por un lado, solo tiene una sola
BeginAccept
llamada en cualquier momento. Solía haber un error .net muy molesto alrededor de esto, que fue hace años, así que no recuerdo los detalles.Además, en el
ReceiveCallback
código, procesamos todo lo recibido del socket antes de poner en cola la próxima recepción. Esto significa que para un solo socket, en realidad solo estamosReceiveCallback
una vez en cualquier momento, y no necesitamos usar sincronización de subprocesos. Sin embargo, si reordena esto para llamar a la próxima recepción inmediatamente después de extraer los datos, lo que puede ser un poco más rápido, deberá asegurarse de sincronizar correctamente los hilos.Además, corté mucho mi código, pero dejé la esencia de lo que está sucediendo en su lugar. Este debería ser un buen comienzo para su diseño. Deja un comentario si tienes más preguntas sobre esto.
fuente
Send
métodos se bloquearán indefinidamente en ambos lados, porque no hay nadie que lea los datos de entrada.Hay muchas formas de realizar operaciones de red en C #. Todos ellos utilizan diferentes mecanismos bajo el capó, y por lo tanto sufren grandes problemas de rendimiento con una alta concurrencia. Las operaciones Begin * son una de estas que muchas personas a menudo confunden con ser la forma más rápida de hacer redes.
Para resolver estos problemas, presentaron el conjunto de métodos * Async: Desde MSDN http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
La clase SocketAsyncEventArgs es parte de un conjunto de mejoras a la clase System.Net.Sockets .. ::. Socket que proporciona un patrón asíncrono alternativo que puede ser utilizado por aplicaciones especializadas de socket de alto rendimiento. Esta clase fue diseñada específicamente para aplicaciones de servidor de red que requieren un alto rendimiento. Una aplicación puede usar el patrón asincrónico mejorado exclusivamente o solo en áreas calientes específicas (por ejemplo, cuando recibe grandes cantidades de datos).
La característica principal de estas mejoras es evitar la asignación y sincronización repetidas de objetos durante la E / S de socket asíncrono de alto volumen. El patrón de diseño Begin / End implementado actualmente por la clase System.Net.Sockets .. ::. Socket requiere que se asigne un objeto System .. ::. IAsyncResult para cada operación de socket asíncrono.
Debajo de las cubiertas, la API * Async utiliza puertos de finalización de E / S, que es la forma más rápida de realizar operaciones de red, consulte http://msdn.microsoft.com/en-us/magazine/cc302334.aspx
Y solo para ayudarlo, incluyo el código fuente de un servidor telnet que escribí usando la API * Async. Solo incluyo las porciones relevantes. También para tener en cuenta, en lugar de procesar los datos en línea, en su lugar opto por empujarlos a una cola sin bloqueo (sin esperar) que se procesa en un hilo separado. Tenga en cuenta que no incluyo la clase de grupo correspondiente, que es solo un grupo simple que creará un nuevo objeto si está vacío, y la clase de búfer, que es solo un búfer autoexpandible que realmente no es necesario a menos que esté recibiendo un indeterminista la cantidad de datos. Si desea más información, no dude en enviarme un PM.
fuente
Solía haber una muy buena discusión sobre TCP / IP escalable usando .NET escrito por Chris Mullins de Coversant, desafortunadamente parece que su blog ha desaparecido de su ubicación anterior, por lo que intentaré juntar sus consejos de memoria (algunos comentarios útiles de sus aparecen en este hilo: C ++ vs. C #: Desarrollando un servidor IOCP altamente escalable )
En primer lugar, tenga en cuenta que tanto el uso
Begin/End
como losAsync
métodos en laSocket
clase utilizan los puertos de finalización de E / S (IOCP) para proporcionar escalabilidad. Esto hace una diferencia mucho mayor (cuando se usa correctamente; ver más abajo) en la escalabilidad que cuál de los dos métodos que realmente elige para implementar su solución.Las publicaciones de Chris Mullins se basaron en el uso
Begin/End
, que es con lo que personalmente tengo experiencia. Tenga en cuenta que Chris armó una solución basada en esto que amplió hasta 10,000s de conexiones de clientes concurrentes en una máquina de 32 bits con 2GB de memoria, y hasta 100,000s en una plataforma de 64 bits con suficiente memoria. Desde mi propia experiencia con esta técnica (aunque ni mucho menos cerca de este tipo de carga) no tengo motivos para dudar de estas cifras indicativas.IOCP versus hilo por conexión o primitivas 'select'
La razón por la que desea usar un mecanismo que usa IOCP debajo del capó es que usa un grupo de subprocesos de Windows de muy bajo nivel que no activa ningún subproceso hasta que haya datos reales en el canal IO del que está tratando de leer ( tenga en cuenta que IOCP también se puede usar para el archivo IO). El beneficio de esto es que Windows no tiene que cambiar a un subproceso solo para descubrir que aún no hay datos, por lo que esto reduce la cantidad de cambios de contexto que su servidor tendrá que hacer al mínimo requerido.
El cambio de contexto es lo que definitivamente matará el mecanismo de 'hilo por conexión', aunque esta es una solución viable si solo se trata de unas pocas docenas de conexiones. Sin embargo, este mecanismo no es "escalable" de ninguna manera.
Consideraciones importantes al usar IOCP
Memoria
En primer lugar, es fundamental comprender que IOCP puede provocar fácilmente problemas de memoria en .NET si su implementación es demasiado ingenua. Cada
BeginReceive
llamada de IOCP dará como resultado "fijación" del búfer en el que está leyendo. Para obtener una buena explicación de por qué esto es un problema, consulte: Weblog de Yun Jin: OutOfMemoryException y Pinning .Afortunadamente, este problema puede evitarse, pero requiere un poco de compensación. La solución sugerida es asignar un gran
byte[]
búfer al inicio de la aplicación (o cerca de ella), de al menos 90 KB más o menos (a partir de .NET 2, el tamaño requerido puede ser mayor en versiones posteriores). La razón para hacer esto es que las asignaciones de memoria grandes terminan automáticamente en un segmento de memoria no compactante (The Large Object Heap) que efectivamente se fija automáticamente. Al asignar un búfer grande al inicio, se asegura de que este bloque de memoria inamovible se encuentre en una 'dirección relativamente baja' donde no se interpondrá y causará fragmentación.Luego puede usar compensaciones para segmentar este gran búfer en áreas separadas para cada conexión que necesita leer algunos datos. Aquí es donde entra en juego una compensación; Como este búfer debe asignarse previamente, tendrá que decidir cuánto espacio de búfer necesita por conexión y qué límite superior desea establecer en la cantidad de conexiones a las que desea escalar (o puede implementar una abstracción que puede asignar buffers fijados adicionales una vez que los necesite).
La solución más simple sería asignar a cada conexión un solo byte en un desplazamiento único dentro de este búfer. Luego puede hacer una
BeginReceive
llamada para que se lea un solo byte y realizar el resto de la lectura como resultado de la devolución de llamada que recibe.Procesando
Cuando recibe la devolución de llamada de la
Begin
llamada que realizó, es muy importante darse cuenta de que el código en la devolución de llamada se ejecutará en el subproceso IOCP de bajo nivel. Es absolutamente esencial que evite operaciones largas en esta devolución de llamada. El uso de estos subprocesos para un procesamiento complejo eliminará su escalabilidad con la misma eficacia que el uso de 'subproceso por conexión'.La solución sugerida es usar la devolución de llamada solo para poner en cola un elemento de trabajo para procesar los datos entrantes, que se ejecutarán en algún otro hilo. Evite cualquier operación de bloqueo potencial dentro de la devolución de llamada para que el subproceso IOCP pueda volver a su grupo lo más rápido posible. En .NET 4.0, sugeriría que la solución más fácil es generar un
Task
, dándole una referencia al socket del cliente y una copia del primer byte que ya fue leído por laBeginReceive
llamada. Esta tarea es responsable de leer todos los datos del socket que representan la solicitud que está procesando, ejecutarla y luego realizar una nuevaBeginReceive
llamada para poner en cola el socket para IOCP una vez más. Antes de .NET 4.0, puede usar ThreadPool o crear su propia implementación de cola de trabajo con subprocesos.Resumen
Básicamente, sugeriría usar el código de muestra de Kevin para esta solución, con las siguientes advertencias agregadas:
BeginReceive
ya esté "anclado"BeginReceive
no haga más que poner en cola una tarea para manejar el procesamiento real de los datos entrantesCuando haga eso, no tengo dudas de que podría replicar los resultados de Chris para aumentar potencialmente a cientos de miles de clientes simultáneos (dado el hardware adecuado y una implementación eficiente de su propio código de procesamiento por supuesto;)
fuente
Ya obtuvo la mayor parte de la respuesta a través de los ejemplos de código anteriores. Usar la operación asincrónica de E / S es absolutamente el camino a seguir aquí. Async IO es la forma en que Win32 está diseñado internamente para escalar. El mejor rendimiento posible que puede obtener se logra utilizando Puertos de finalización, vinculando sus sockets a puertos de finalización y tiene un grupo de subprocesos esperando la finalización del puerto de finalización. La sabiduría común es tener 2-4 subprocesos por CPU (núcleo) esperando su finalización. Recomiendo leer estos tres artículos de Rick Vicik del equipo de rendimiento de Windows:
Dichos artículos cubren principalmente la API nativa de Windows, pero son una lectura obligada para cualquiera que intente comprender la escalabilidad y el rendimiento. También tienen algunos informes sobre el lado administrado de las cosas.
Lo segundo que debe hacer es asegurarse de revisar el libro Mejora del rendimiento y escalabilidad de aplicaciones .NET , que está disponible en línea. Encontrará consejos pertinentes y válidos sobre el uso de subprocesos, llamadas asincrónicas y bloqueos en el Capítulo 5. Pero las gemas reales se encuentran en el Capítulo 17, donde encontrará cosas buenas como orientación práctica para ajustar su grupo de subprocesos. Mis aplicaciones tuvieron algunos problemas serios hasta que ajusté maxIothreads / maxWorkerThreads según las recomendaciones de este capítulo.
Dices que quieres hacer un servidor TCP puro, así que mi siguiente punto es falso. Sin embargo , si te encuentras acorralado y utilizas la clase WebRequest y sus derivados, ten en cuenta que hay un dragón vigilando esa puerta: el ServicePointManager . Esta es una clase de configuración que tiene un propósito en la vida: arruinar su rendimiento. Asegúrese de liberar su servidor del ServicePoint.ConnectionLimit impuesto artificialmente o su aplicación nunca escalará (le permito descubrir cuál es el valor predeterminado ...). También puede reconsiderar la política predeterminada de enviar un encabezado Expect100Continue en las solicitudes http.
Ahora, sobre la API gestionada por socket central, las cosas son bastante fáciles en el lado de envío, pero son significativamente más complejas en el lado de recepción. Para lograr un alto rendimiento y escala, debe asegurarse de que el socket no esté controlado por el flujo porque no tiene un búfer publicado para recibir. Idealmente para un alto rendimiento, debe publicar con anticipación 3-4 buffers y publicar nuevos buffers tan pronto como recupere uno ( antes de procesar el que regresó) para asegurarse de que el socket siempre tenga un lugar para depositar los datos provenientes de la red. Verá por qué probablemente no podrá lograr esto en breve.
Una vez que haya terminado de jugar con la API BeginRead / BeginWrite y comience el trabajo serio, se dará cuenta de que necesita seguridad en su tráfico, es decir. La autenticación NTLM / Kerberos y el cifrado del tráfico, o al menos la protección contra la manipulación del tráfico. La forma de hacerlo es utilizar el System.Net.Security.NegotiateStream integrado (o SslStream si necesita cruzar dominios dispares). Esto significa que, en lugar de confiar en operaciones asincrónicas de socket recto, dependerá de las operaciones asincrónicas AuthenticatedStream. Tan pronto como obtenga un socket (ya sea desde connect en el cliente o desde accept en el servidor), cree un flujo en el socket y lo envíe para autenticación, llamando a BeginAuthenticateAsClient o BeginAuthenticateAsServer. Después de que se complete la autenticación (al menos su caja fuerte de la locura nativa de InitiateSecurityContext / AcceptSecurityContext ...) hará su autorización verificando la propiedad RemoteIdentity de su transmisión autenticada y haciendo cualquier verificación de ACL que su producto debe admitir. Después de eso, enviará mensajes utilizando BeginWrite y los recibirá con BeginRead. Este es el problema del que estaba hablando antes: no podrás publicar múltiples buffers de recepción, porque las clases AuthenticateStream no admiten esto. La operación BeginRead administra internamente todas las E / S hasta que haya recibido una trama completa, de lo contrario no podría manejar la autenticación del mensaje (descifrar la trama y validar la firma en la trama). Aunque en mi experiencia, el trabajo realizado por las clases AuthenticatedStream es bastante bueno y no debería tener ningún problema. Es decir. debería poder saturar la red GB con solo un 4-5% de CPU. Las clases AuthenticatedStream también le impondrán las limitaciones de tamaño de trama específicas del protocolo (16k para SSL, 12k para Kerberos).
Esto debería ayudarlo a comenzar en el camino correcto. No voy a publicar código aquí, hay un ejemplo perfectamente bueno en MSDN . He realizado muchos proyectos como este y pude escalar a unos 1000 usuarios conectados sin problemas. Por encima de eso, deberá modificar las claves de registro para permitir que el núcleo tenga más identificadores de socket. y asegúrese de implementar en un sistema operativo del servidor , que es W2K3 no XP o Vista (es decir, el sistema operativo del cliente), hace una gran diferencia.
Por cierto, asegúrate de que si tienes operaciones de bases de datos en el servidor o archivo IO, también usas el sabor asíncrono para ellas, o agotarás el grupo de subprocesos en poco tiempo. Para las conexiones de SQL Server, asegúrese de agregar el 'Procesamiento asincrónico = verdadero' a la cadena de conexión.
fuente
Tengo un servidor en ejecución en algunas de mis soluciones. Aquí hay una explicación muy detallada de las diferentes formas de hacerlo en .net: acérquese al cable con sockets de alto rendimiento en .NET
Últimamente he estado buscando formas de mejorar nuestro código y lo investigaré: " Mejoras de rendimiento de socket en la versión 3.5 " que se incluyó específicamente "para uso de aplicaciones que usan E / S de red asíncrona para lograr el mayor rendimiento".
"La característica principal de estas mejoras es evitar la asignación y sincronización repetidas de objetos durante la E / S de socket asíncrono de alto volumen. El patrón de diseño de inicio / fin implementado actualmente por la clase Socket para E / S de socket asíncrono requiere un sistema". El objeto IAsyncResult se asignará para cada operación de socket asíncrono ".
Puedes seguir leyendo si sigues el enlace. Personalmente, probaré su código de muestra mañana para compararlo con lo que tengo.
Editar: Aquí puede encontrar el código de trabajo tanto para el cliente como para el servidor utilizando el nuevo 3.5 SocketAsyncEventArgs para que pueda probarlo en un par de minutos e ir a través del código. Es un enfoque simple, pero es la base para comenzar una implementación mucho más grande. También este artículo de hace casi dos años en MSDN Magazine fue una lectura interesante.
fuente
¿Ha considerado usar un enlace TCP de red WCF y un patrón de publicación / suscripción? WCF le permitiría concentrarse [principalmente] en su dominio en lugar de fontanería.
Hay muchas muestras de WCF e incluso un marco de publicación / suscripción disponible en la sección de descargas de IDesign que puede ser útil: http://www.idesign.net
fuente
Me pregunto una cosa:
¿Porqué es eso? Windows podría manejar cientos de subprocesos en una aplicación desde al menos Windows 2000. Lo he hecho, es realmente fácil trabajar con ellos si no es necesario sincronizarlos. Especialmente dado que está haciendo muchas E / S (por lo que no está vinculado a la CPU y muchos hilos se bloquearían en el disco o la comunicación de red), no entiendo esta restricción.
¿Has probado la forma de subprocesos múltiples y descubriste que le falta algo? ¿Pretende tener también una conexión de base de datos para cada subproceso (eso mataría al servidor de la base de datos, por lo que es una mala idea, pero se resuelve fácilmente con un diseño de 3 niveles). ¿Te preocupa tener miles de clientes en lugar de cientos y luego realmente tendrás problemas? (Aunque probaría mil hilos o incluso diez mil si tuviera más de 32 GB de RAM, una vez más, dado que no está vinculado a la CPU, el tiempo de cambio de hilo debería ser absolutamente irrelevante).
Aquí está el código: para ver cómo se ve esto, vaya a http://mdpopescu.blogspot.com/2009/05/multi-threaded-server.html y haga clic en la imagen.
Clase de servidor:
Programa principal del servidor:
Clase de cliente:
Programa principal del cliente:
fuente
Usar Async IO (.et) integrado de .NET
BeginRead
es una buena idea si puede obtener todos los detalles correctos. Cuando configure correctamente sus manejadores de socket / archivo, usará la implementación IOCP subyacente del sistema operativo, permitiendo que sus operaciones se completen sin usar ningún subproceso (o, en el peor de los casos, usar un subproceso que creo que proviene del grupo de subprocesos IO del kernel en su lugar del grupo de subprocesos de .NET, que ayuda a aliviar la congestión del conjunto de subprocesos).El problema principal es asegurarse de abrir sus sockets / archivos en modo sin bloqueo. La mayoría de las funciones de conveniencia predeterminadas (como
File.OpenRead
) no hacen esto, por lo que deberá escribir las suyas propias.Una de las otras preocupaciones principales es el manejo de errores: manejar adecuadamente los errores al escribir código de E / S asíncrono es mucho, mucho más difícil que hacerlo en código síncrono. También es muy fácil terminar con condiciones de carrera y puntos muertos a pesar de que no estés usando hilos directamente, por lo que debes estar al tanto de esto.
Si es posible, debe intentar usar una biblioteca de conveniencia para facilitar el proceso de hacer E / S asíncronas escalables.
El tiempo de ejecución de coordinación de concurrencia de Microsoft es un ejemplo de una biblioteca .NET diseñada para aliviar la dificultad de hacer este tipo de programación. Se ve muy bien, pero como no lo he usado, no puedo comentar qué tan bien se escalaría.
Para mis proyectos personales que necesitan hacer una red asíncrona o E / S de disco, utilizo un conjunto de herramientas de concurrencia / IO de .NET que he creado durante el año pasado, llamado Squared.Task . Está inspirado en bibliotecas como imvu.task y twisted , y he incluido algunos ejemplos de trabajo en el repositorio que hacen E / S de red. También lo he usado en algunas aplicaciones que he escrito, la más grande lanzada públicamente es NDexer (que lo usa para E / S de disco sin hilos). La biblioteca se escribió en base a mi experiencia con imvu.task y tiene un conjunto de pruebas unitarias bastante completas, por lo que le recomiendo que lo pruebe. Si tiene algún problema, me complacerá ofrecerle ayuda.
En mi opinión, según mi experiencia en el uso de E / S asíncronas / sin hilos en lugar de hilos, es un esfuerzo valioso en la plataforma .NET, siempre y cuando esté listo para lidiar con la curva de aprendizaje. Le permite evitar las molestias de escalabilidad impuestas por el costo de los objetos Thread, y en muchos casos, puede evitar por completo el uso de bloqueos y mutexes haciendo un uso cuidadoso de primitivas de concurrencia como Futures / Promises.
fuente
Usé la solución de Kevin, pero él dice que la solución carece de código para el reensamblado de mensajes. Los desarrolladores pueden usar este código para reensamblar mensajes:
fuente
Puede encontrar una buena descripción de las técnicas en la página del problema C10k .
fuente
Podría intentar usar un marco llamado ACE (Adaptive Communications Environment) que es un marco genérico de C ++ para servidores de red. Es un producto muy sólido y maduro, y está diseñado para admitir aplicaciones de gran confiabilidad y alto volumen hasta grado telco.
El marco trata con una amplia gama de modelos de concurrencia y probablemente tenga uno adecuado para su aplicación lista para usar. Esto debería facilitar la depuración del sistema, ya que la mayoría de los desagradables problemas de concurrencia ya se han resuelto. La desventaja aquí es que el marco está escrito en C ++ y no es la base de código más cálida y esponjosa. Por otro lado, obtiene una infraestructura de red de grado industrial probada y una arquitectura altamente escalable lista para usar.
fuente
Me gustaría utilizar SEDA o una librería de hilos ligero (Erlang o posterior Linux ver escalabilidad NTPL en el lado del servidor ). La codificación asíncrona es muy engorrosa si su comunicación no lo es :)
fuente
Bueno, los sockets .NET parecen proporcionar select () , que es lo mejor para manejar la entrada. Para la salida, tendría un grupo de hilos de escritura de socket que escucha en una cola de trabajo, aceptando descriptor / objeto de socket como parte del elemento de trabajo, por lo que no necesita un hilo por socket.
fuente
Usaría los métodos AcceptAsync / ConnectAsync / ReceiveAsync / SendAsync que se agregaron en .Net 3.5. He hecho un punto de referencia y son aproximadamente un 35% más rápidos (tiempo de respuesta y tasa de bits) con 100 usuarios que envían y reciben datos constantemente.
fuente
para que la gente copie y pegue la respuesta aceptada, puede volver a escribir el método acceptCallback, eliminando todas las llamadas de _serverSocket.BeginAccept (nuevo AsyncCallback (acceptCallback), _serverSocket); y ponerlo en una cláusula finalmente {}, de esta manera:
incluso podría eliminar la primera captura, ya que su contenido es el mismo, pero es un método de plantilla y debe usar la excepción escrita para manejar mejor las excepciones y comprender qué causó el error, así que simplemente implemente esas capturas con algún código útil
fuente
Recomendaría leer estos libros en ACE
para obtener ideas sobre patrones que le permitan crear un servidor eficiente.
Aunque ACE se implementa en C ++, los libros cubren muchos patrones útiles que se pueden usar en cualquier lenguaje de programación.
fuente
No obtendrá el nivel más alto de escalabilidad si utiliza puramente .NET. Las pausas de GC pueden dificultar la latencia.
La IO superpuesta generalmente se considera la API más rápida de Windows para la comunicación de red. No sé si esto es lo mismo que su API de Asynch. No use select, ya que cada llamada necesita verificar cada socket que está abierto en lugar de tener devoluciones de llamada en sockets activos.
fuente
Puede usar el marco de código abierto Push Framework para el desarrollo de servidores de alto rendimiento. Está construido en IOCP y es adecuado para escenarios push y difusión de mensajes.
http://www.pushframework.com
fuente