2008-08-31 21 views
9

¿Cuál es la forma normal en que las personas que escriben código de red en Delphi utilizan la E/S de socket asíncrona solapada al estilo de Windows?¿Cuál es la forma idiomática de hacer una programación de socket asíncrono en Delphi?

Aquí está mi investigación previa en esta cuestión:

Los Indy componentes parecen totalmente sincrónica. Por otro lado, aunque la unidad ScktComp usa WSAAsyncSelect, básicamente solo sincroniza una aplicación de socket multiplexada al estilo BSD. Te vuelcan en una devolución de llamada de evento único, como si acabaras de regresar de select() en un bucle, y tienes que hacer toda la navegación de máquina de estado por tu cuenta.

La situación de .NET es considerablemente mejor, con Socket.BeginRead/Socket.EndRead, donde la continuación se pasa directamente a Socket.BeginRead, y ahí es donde se realiza una copia de seguridad. Una continuación codificada como cierre obviamente tiene todo el contexto que necesita, y más.

Respuesta

2

He encontrado que Indy, aunque es un concepto más simple al principio, es incómodo de manejar debido a la necesidad de eliminar los sockets para liberar los hilos al finalizar la aplicación. Además, hice que la biblioteca de Indy dejara de funcionar después de una actualización del parche del sistema operativo. ScktComp funciona bien para mi aplicación.

+1

Si tiene que matar tomas manualmente, entonces no las está utilizando correctamente. Los servidores de Indy manejan esas cosas automáticamente. Debería saber que estoy en el equipo de desarrollo de Indy. En cuanto a la actualización del sistema operativo, ¿qué dejó de funcionar exactamente? –

+0

Para aclarar, esta es Indy 9, no Indy 10, lo que podría explicar la necesidad de cerrar manualmente las tomas al salir. El parche de seguridad del sistema operativo hizo que las llamadas a Write() se bloqueen y no vuelvan (entre XP SP2 y 3). La aplicación funcionó durante 2 años y falló después de un parche de seguridad. Pensando que era hardware, el equipo de implementación movió la aplicación a un segundo servidor con el mismo resultado. Como era un servidor de producción, tratamos de sustituir la interfaz ScktComp y todo funcionó. No tuve tiempo de usar un depurador de kernel para averiguar qué causó que bloqueara la llamada. – Mike

1

¿Cuál es la forma normal de personas que escriben código de red en uso Delphi al estilo de ventanas superpuestas asíncrono toma de E/S?

Bueno, Indy ha sido la biblioteca "estándar" para la E/S de socket durante mucho tiempo, y está basada en sockets de bloqueo. Esto significa que si desea un comportamiento asincrónico, use hilos adicionales para conectar/leer/escribir datos. En mi opinión, esta es una gran ventaja, ya que no es necesario administrar ningún tipo de navegación de máquina de estado, ni preocuparse por los procesos de devolución de llamada ni cosas similares. Encuentro que la lógica de mi hilo de lectura es menos abarrotada y mucho más portátil que las tomas sin bloqueo.

Indy ha sido principalmente a prueba de bombas, rápido y confiable para nosotros. Sin embargo, el cambio a Indy 10 para Tiburon me está causando una pequeña preocupación.

@ Mike: "... la necesidad de matar a tomas de subprocesos libres ...".

Esto hizo ir "¿eh?" hasta que recordé que nuestra biblioteca de threading utiliza una técnica basada en excepciones para matar hilos de 'espera' de forma segura. Llamamos al QueueUserAPC para poner en cola una función que genera una excepción de C++ (NO derivada de la excepción de la clase) que solo debe captar nuestro procedimiento de envoltura de subprocesos. Se llama a todos los destructores para que todos los hilos terminen de forma limpia y se arreglen en el camino de salida.

+1

Fue durante la fase de inestabilidad Indy9 a API Indy10 que comencé mi fase "get the high de la Indy". Solo uso ICS ahora y estoy muy contento con él. No fue por la sincronización/asincronía, sino por todos los fallos (especialmente cuando se apaga la aplicación) y por todo lo de IdAntifreeze que me cansé de Indy. –

+0

+1 para mencionar claridad de código mejorada con sockets de bloqueo – mjn

2

@Roddy - Las tomas sincrónicas no son lo que busco. Grabar un hilo completo por una conexión posiblemente de larga duración significa que limita la cantidad de conexiones simultáneas al número de subprocesos que su proceso puede contener. Como los subprocesos usan muchos recursos (espacio de direcciones de pila reservadas, memoria de pila comprometida y transiciones de kernel para conmutadores de contexto), no se amplían cuando necesita admitir cientos de conexiones, mucho menos miles o más.

+0

Por otro lado, los sockets no bloqueantes (asincrónicos) involucran la cola de mensajes de Windows, que también es un cuello de botella si se trata de cientos o miles de conexiones. + Encontré este artículo de marzo de 2011 muy interesante: [Incremento diez veces mayor en el rendimiento del servidor con el procesamiento asíncrono Servlet 3.0] (http://nurkiewicz.blogspot.com/2011/03/tenfold-increase-in-server-throughput.html) – mjn

1

"Los enchufes sincrónicos no son lo que busco."

Entendido - pero creo que en ese caso la respuesta a su pregunta original es que no sólo no es un Delphi idioma para el socket asincrónico io porque en realidad es un requisito altamente especializado y poco común

.

Como un problema secundario, puede encontrar estos enlaces interesantes. Ambos son un poco viejos, y más * nxy que Windows. El segundo implica que, en el entorno correcto, los hilos podrían no ser tan malos como cree.

The C10K problem

Why Events Are A Bad Idea (for High-concurrency Servers)

0

Indy utiliza enchufes síncronos porque es una forma más sencilla de programación. El bloqueo de socket asíncrono fue algo agregado a la pila winsock en los días de Windows 3.x. Windows 3.x no admitía subprocesos y allí no podía hacer E/S de socket sin subprocesos. Para obtener más información acerca de por qué Indy usa el modelo de bloqueo, consulte this article.

Las llamadas .NET Socket.BeginRead/EndRead están utilizando subprocesos, solo es administrado por el Framework en lugar de usted.

@Roddy, Indy 10 se ha incluido con Delphi desde Delphi 2006. Descubrí que la migración de Indy 9 a Indy 10 es una tarea sencilla.

1

@Chris Miller - Lo que ha indicado en su respuesta es de hecho inexacto.

Windows estilo de mensaje async, como está disponible a través de WSAAsyncSelect, es en gran medida una solución para la falta de un modelo de subprocesamiento adecuado en Win 3.x días.

.NET Begin/End, sin embargo, es no usando hilos adicionales. En su lugar, usa E/S superpuestas, utilizando el argumento adicional en WSASend/WSARecv, específicamente la rutina de finalización superpuesta, para especificar la continuación.

Esto significa que el estilo .NET aprovecha soporte asíncrono de E/S del sistema operativo de Windows para evitar quema de un hilo mediante el bloqueo de un conector.

Dado que los subprocesos en general son caros (a menos que especifique un tamaño de pila muy pequeño para CreateThread), tener subprocesos bloqueados en sockets le impedirá escalar a 10.000 de conexiones simultáneas.

Es por esto que es importante que asíncrono de E/S puede utilizar si desea cambiar la escala, y también por qué .NET es no, repito, es no, simplemente "el uso de hilos, [...] solo administrado por el Marco ".

+2

Y dado que INDY es un conjunto de componentes del que desconfío, e ICS es un conjunto de componentes de terceros que es asincrónico sin el soporte de IOCP, creo que Embarcadero debería intensificar y construir una capa de red async + iocp adecuada. El hecho de que INDY sea la capa debajo de la capa de red de la base de datos empresarial actual de VCL es horrible, y es la razón por la que no tocaré el montón de cosas con un poste de diez pies. –

1

@Roddy - Ya he leído los enlaces que señala, ambos se mencionan en la presentación de Paul Tyma "Miles de hilos y E/S de bloqueo - La antigua forma de escribir servidores Java es nueva nuevamente".

Algunas de las cosas que no necesariamente salen de la presentación de Paul, sin embargo, es que especificó -Xss: 48k en la JVM en el inicio, y que está asumiendo que la implementación de NIO de la JVM es eficiente para que ser una comparación válida

Indy does not especifique un tamaño de pila igualmente reducido y estrechamente restringido.No hay llamadas a BeginThread (la rutina de creación de subprocesos de Delphi RTL, que debe usar para tales situaciones) o CreateThread (la llamada de WinAPI sin formato) en la base de código de Indy.

El tamaño de pila predeterminado se almacena en el PE, y para el compilador Delphi predeterminado es 1MB de espacio de direcciones reservado (el OS almacena página por página en fragmentos 4K; de hecho, el compilador necesita generar código para tocar páginas si hay> 4K de locales en una función, porque la extensión está controlada por fallas de página, pero solo para la página más baja (de guardia) en la pila). Eso significa que se va a quedar sin espacio de direcciones después de un máximo de 2.000 subprocesos simultáneos que manejan conexiones.

Ahora, puede cambiar el tamaño de pila predeterminado en el PE utilizando el {$ M minStackSize [, maxStackSize]} Directiva, pero que afectará a todos los hilos, incluyendo el hilo principal. Espero que no hagas mucha recursión, porque 48K o (similar) no es mucho espacio.

Ahora, si Paul tiene razón sobre la falta de rendimiento de la E/S asincrónica para Windows en particular, no estoy 100% seguro, tendría que medirlo para estar seguro. Lo que sé, sin embargo, es que los argumentos sobre la programación de subprocesos son más fáciles que la programación basada en eventos asincrónicos, presentan una dicotomía falsa.

Código asíncrono no necesita basado en eventos; puede basarse en la continuación, como en .NET, y si especifica un cierre como su continuación, se le mantiene el estado de forma gratuita. Además, la conversión del código de estilo de subproceso lineal al código asincrónico de estilo de paso de continuación puede hacerse mecánica por un compilador (la transformación de CPS es mecánica), por lo que tampoco debe haber ningún costo en la claridad del código.

+0

Supongo que todavía tendrá que preocuparse por la sincronización de acceso a datos mientras usa cierres o ¿se serializaron todas las comunicaciones? ¿Cómo se combina esto con f.x. asynch. respuestas de base de datos que proporcionan datos a la gran cantidad de solicitudes entrantes? Use grupos de hilos de trabajo? Deberías bloguear esto. –

+0

Los subprocesos de Indy se basan en la clase TThread del VCL, que no le permite especificar un tamaño de pila. –

0

Con las clases ScktComp, es necesario utilizar un servidor ThreadBlocking en lugar de un tipo de un servidor sin bloqueo. Utilice el evento OnGetThread para transferir el parámetro ClientSocket a un nuevo hilo de su diseño. Una vez que hayas instanciado una instancia heredada de TServerClientThread, crearás una instancia de TWinSocketStream (dentro del hilo) que puedes usar para leer y escribir en el socket. Este método lo aleja de tratar de procesar datos en el controlador de eventos. Estos subprocesos pueden existir solo por el corto período necesario para leer o escribir, o aguantar durante el período de tiempo para ser reutilizados.

El tema de escribir un servidor de socket es bastante amplio. Existen muchas técnicas y prácticas que puede elegir implementar. El método de lectura y escritura en el mismo socket con TServerClientThread es directo y correcto para aplicaciones simples. Si necesita un modelo de alta disponibilidad y alta concurrencia, entonces debe observar patrones como el patrón de Proactor.

¡Buena suerte!

1

Hay gratis los componentes IOCP (puertos de finalización) del zócalo: http://www.torry.net/authorsmore.php?id=7131 (código fuente incluido)

"por los altos servidor de socket rendimiento Naberegnyh Sergey N .. basados ​​en de Windows puerto de finalización y con el uso de Extensiones de socket de Windows. IPv6 compatibles. "

lo he encontrado mientras mira mejores componentes/biblioteca para rearquitectura mi pequeño servidor de mensajería instantánea. No lo he probado todavía, pero se ve bien codificado como una primera impresión.

Cuestiones relacionadas