Uso de Delphi para establecer servidores de comunicación e intercambio de datos: análisis de la tecnología transceptor (Parte 2) Autor: 火鸟 [email protected] 2. Explicación detallada del servicio transceptor 1. Resumen del análisis del servicio Transceiver El servicio Transceiver es el componente principal del sistema Transceiver Kernel y es responsable de leer las definiciones y parámetros de puerto y canal establecidos por la consola Transceiver desde la biblioteca de configuración del sistema, creando y controlando dinámicamente los puertos de comunicación y sus relaciones durante el tiempo de ejecución. y controlar datos. Programación de recepción, envío y almacenamiento en búfer, gestión de registros y colas, etc. Transceiver Shell es la implementación de todo tipo de puertos admitidos para el envío y recepción de datos. 2. Resumen del diseño del servicio transceptor El servicio transceptor se desarrolla a partir de la aplicación de servicio en Delphi. La aplicación de servicio puede ejecutarse en modo sistema en lugar de modo usuario. El administrador de control de servicios (SCM) del sistema operativo es responsable de la gestión de operaciones del programa. y pertenece al programa en segundo plano. Transceiver Kernel es una serie de métodos de la clase Transceiver que establece y controla Transceiver Shell, y Transceiver Shell es una colección de objetos responsables de la comunicación. Nota: Debido a consideraciones de rendimiento y carga, Transceiver Kernel solo implementa lógicamente la división funcional en el diagrama de arquitectura, y los módulos constituyentes no se implementan de una manera completamente basada en objetos. 3. Resumen de implementación del servicio transceptor i. Cree una aplicación de servicio Seleccione NUEVO|Otro... en el menú principal de Delphi Archivo... y seleccione NUEVO|Aplicación de servicio en el cuadro de diálogo emergente Nuevos elementos. Puede ver el programa generado. El marco es el siguiente: PRogram Project1; usa SvcMgr, Unit1 en 'Unit1.pas' {Service1: TService};{$R *.RES}begin Application.Initialize Application.CreateForm(TService1, Service1); Application.Run;end.unit Unit1;interfaceuses Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs;type TService1 = class(TService) private { Declaraciones privadas } función pública GetServiceController: TServiceController override; end;var Servicio1: TService1;procedimiento de implementación{$R *.DFM} ServiceController (CtrlCode: DWord); stdcall; comenzar Service1.Controller (CtrlCode); finalizar; función TService1.GetServiceController: TServiceController; comenzar Resultado: = ServiceController;end;end Puede ver que además del SvcMgr utilizado para la gestión de servicios al que se hace referencia en la unidad de usos, TService1 hereda de TServiced en lugar de TForm, una función GetServiceController sobrecargada y el proceso ServiceController llamado en modo stdcall, se crea con. Delphi No hay mucho de especial en un programa de servicio, los fanáticos de Delphi pueden querer animar nuevamente, este es el poderoso encanto de Delphi RAD. Además, dado que la aplicación de servicio no se puede depurar directamente en tiempo de ejecución y no tiene una interfaz de usuario, se debe considerar la salida sin interfaz de la información de depuración durante el desarrollo para facilitar la depuración y la resolución de problemas. ii. Para crear una clase de Puerto que satisfaga necesidades específicas y utilice el Transceiver Kernel con un mecanismo de ejecución y procesamiento unificado, se requiere que los Puertos en el Transceiver Shell tengan reglas de procesamiento unificadas. Algunos Puertos en el Shell son clases de componentes existentes en Delphi. entorno de desarrollo (como TCP, FTP, etc.), y algunos no (como MSMQ, Archivo, etc.). En este momento, debe crear una clase que pueda satisfacer sus necesidades. Por ejemplo: type//Dado que no hay una interfaz de usuario, se hereda de TComponent en lugar de TControl TFilePort=class(TComponent) private FilePath:string;//Obtener o guardar la ubicación de la carpeta del archivo Prefix:string;//File prefix suffix:string; // Fin del sufijo del archivo; Después de establecer la clase TFilePort, Transceiver Kernel puede usar un método de procesamiento de clases unificado para hacer referencia y administrar objetos para lograr el propósito de acceder a archivos específicos desde la carpeta especificada por FilePath. Si se usa como Fuente, los archivos que cumplan las condiciones se obtendrán de una carpeta específica. Si se usa como Destino, los datos obtenidos de la Fuente correspondiente se escribirán en el archivo especificado (de hecho, los parámetros reales de cada objeto de Puerto provienen del archivo). definición de la tabla Puerto en la biblioteca de configuración del sistema). Otro ejemplo: escriba TCOMPort=class(TComponent) private ComFace:string;// Interfaz COM para obtener o enviar datosend; TCOMPort se utilizará para obtener o enviar datos a la interfaz del componente COM especificada. En Delphi, la clase OleVariant es una de las formas de implementar llamadas a componentes COM. La necesidad de usar la clase TCOMPort es que Transceiver creará una instancia de la interfaz COM definida por TCOMPort en un objeto OleVariant solo cuando se requiera acceso a los datos necesarios y al objeto. se liberará después de su uso. Esto puede reducir la presión de carga en el transceptor y el servidor COM. Las mismas consideraciones se aplican a otros componentes similares. El ejemplo de clase proporcionado por el autor aquí es solo un modelo, y se deben agregar métodos y eventos apropiados cuando sea necesario. Las clases implementadas por el autor durante el desarrollo incluyen: TCOMPort, TMSMQPort, TDBPort, TFilePort, etc.iii. Soporte para múltiples canales: una matriz de objetos que declara Puertos. El Transceptor considera un proceso de comunicación como un proceso de flujo de datos desde el origen al destino. Dicho proceso es un Canal en el Transceptor, y este Canal se compone de al menos dos. puertos (uno para Origen y otro para Destino), por lo que si desea definir un número indefinido de canales que se pueden combinar libremente con Origen y Destino, debe declararlos por separado para Origen y Destino. Matrices de objetos de varias clases de Puerto (y establecer relaciones correspondientes para ellos, como verá más adelante). Por ejemplo: private { Declaraciones privadas }TCPSource:matriz de TServerSocket;//Matriz de objetos para TCP Source TCPTarget:matriz de TClientSocket;//Matriz de objetos para TCP Target MailSource:matriz de TIdPOP3 //Para matriz de objetos de fuente de correo MailTarget:matriz; de TIdSMTP // Matriz de objetos para el archivo de destino de correo Fuente:matriz de TFilePort; // Matriz de objetos para el origen del archivo fileTarget: matriz de TFilePort; // Matriz de objetos para el destino del archivo comSource: matriz de TCOMPort; // Matriz de objetos para el origen COM comTarget: matriz de TCOMPort; // Para la matriz de objetos de destino COM Nota: Desde el Las reglas de operación de puertos para Origen y Destino del mismo tipo son completamente diferentes, se consideran objetos completamente diferentes sin relación directa en el concepto de Transceptor. Por lo tanto, para el mismo tipo de Puerto, las matrices de objetos también se crean por separado según Origen y Destino. iv. Crear una instancia de la matriz de objetos en tiempo de ejecución. El número de elementos en cada matriz de objetos es administrado por Port Builder en tiempo de ejecución. Si el usuario define algunos puertos de un determinado tipo a través de Transceiver Console, Port Builder los creará de acuerdo con su número. y parámetros respectivos. La matriz de objetos. De lo contrario, no se creará una instancia de la matriz de objetos. En el objeto Puerto de tipo fuente, la propiedad Nombre se establece en el formato 'Recibir'+ID de puerto. En los activadores de recepción de datos posteriores, esto ayudará al Distribuidor de datos a localizar el objeto y programar de manera uniforme diferentes tipos de objetos Puerto. El atributo Etiqueta se utiliza para proporcionar al Controlador de canal la información de ID de destino del Canal en el que se encuentra. La siguiente es la parte de creación de instancias de la matriz de objetos comSource en Port Builder. comenzar //Crear COM/Recibir puerto itmp:=high(comSource)+1;// Obtenga el número máximo actual de comSource, itmp es la variable entera SetLength(comSource ,itmp +1); // Agregar un miembro de la matriz comSource comSource [itmp]:=TCOMPort.Create(self); crear una instancia del miembro comSource[itmp].Name:= 'Receive'+inttostr(isource); //Establece el atributo Nombre en 'Receive'+ID de puerto, e isource es el ID de puerto actual del tipo entero comSource [itmp].Tag:= itarget; ID del canal donde se encuentra NullTest :=rece.Fields['Address'].value;//Obtiene el valor de la configuración del sistema COMFace, NullTest es una variable variante si (NullTest <>null) y (trim(NullTest)<>'') luego comienzacomSource [itmp].ComFace:=NullTest; //Asignar valores válidos a ComFaceNullTest:=rece.Fields['interval'].value; //Obtener el intervalo de tiempo de activación para que los objetos COM obtengan datos en la configuración del sistema SetTimer(application.handle,isource,NullTest*60000 ,nil); //Establece un reloj de activación para el puerto actual para recopilar datos periódicamente, isource es Port IDendelsecomSource [itmp].Tag:=-1;// La inicialización falló, marcada como no válida Puerto final; comSource es el puerto de clase de origen utilizado para llamar a la interfaz definida en ComFace y obtener datos después de un cierto intervalo de tiempo, correspondiente a comTarget La implementación es similar, excepto que enviar datos a ComFace de comTarget es un proceso en tiempo real, por lo que no es necesario utilizar el intervalo de activación y se pueden omitir las dos declaraciones para establecer el reloj. La creación e inicialización de otros tipos de objetos de puerto son similares. Por ejemplo, otro fragmento de implementación de MailTarget: comenzar //Crear SMTP/Puerto de envío itmp:=high(MailTarget)+1; SetLength(MailTarget,itmp+1); itmp].Nombre:='enviar'+ inttostr(itarget); MailTarget[itmp].Tag:=3;//Establecer como identificación del tipo de puerto de destino NullTest:=rece.Fields['Address'].value //Dirección del servidor de correo if (NullTest <>null) y (trim(NullTest) <>'') luego MailTarget[itmp].Host :=NullTest else bValid:=false; NullTest:=rece.Fields['Port'].value; // Puerto del servidor de correo si NullTest <>null entonces(si NullTest<>0 entonces MailTarget[itmp].Port :=NullTest)else bValid:=false; =rece.Fields['user'].value;//Inicie sesión con el nombre de usuario si NullTest <>null thenMailTarget[itmp].UserId :=NullTest else bValid:=NullTest:=rece.Fields['password'].value;//Contraseña de inicio de sesión………………………end;Tal vez tengas algo como Tengo algunas dudas. Port Builder crea una gran cantidad de componentes de comunicación de Transceiver Shell en tiempo de ejecución. ¿Será alto el rendimiento de Transceiver Service? De hecho, la misión de Port Builder se completa una vez cuando ocurre el evento ServiceCreate. La cantidad de puertos Shell solo afectará la velocidad de inicialización del servicio transceptor. La velocidad de comunicación del puerto Shell y el rendimiento general del servicio transceptor. Por supuesto, los recursos del sistema podrían ocupar un poco más. v. Asignación dinámica y procesamiento de eventos Entre varios puertos de comunicación admitidos por Transceiver Shell, use TServerSocket (es posible que prefiera usar el componente de comunicación de Indy, pero esto no viola la idea de diseño de Transceiver Service, es solo una modificación en el A nivel de Shell (o recién agregado), el TCPSource implementado es más distintivo, porque TServerSocket, como puerto de origen, es diferente de objetos como COM o POP3 que deben activarse regularmente. El Servicio siempre está en estado de escucha después de iniciarse. Es un componente que genera los eventos correspondientes cuando un ClientSocket se conecta y envía datos. El siguiente es el fragmento de creación de instancias de TCPSource: comenzar //Crear TCP/Puerto de recepción itmp:=high(TCPSource)+1;SetLength(TCPSource,itmp+1); [ itmp].OnClientRead:=TCPServersClientRead;//Asignar el proceso de procesamiento del evento OnClientRead a TCPServersClientRead TCPSource [itmp].OnClientError:=TCPServerClientError;//Asigne el proceso de procesamiento del evento OnClientError a TCPServerClientErrorTCPSource [itmp].Name:= 'Receive'+inttostr(isource); //Establezca la propiedad Nombre en 'Recibir'+ID de puerto; TCPSource [itmp ].Tag:=itarget //Establece el IDTCPSource objetivo de su canal [itmp].Socket.Data:=@ TCPSource; [itmp].Tag;// Adjunte el ID de destino de este objeto Port al objeto Socket como datos de puntero…………………………end Regrese y observe el procesamiento de nuestro comSource. Establece un reloj de activación, pero ¿cómo manejar los eventos cuando se activa el reloj? Del mismo modo, también es la asignación dinámica del procesamiento de eventos. La definición de procesamiento del reloj de comSource se puede agregar al procesamiento de eventos ServiceCreate: application.OnMessage:=Timer; para implementar la sobrecarga del procesamiento de mensajes, cuando se genera un mensaje de la aplicación, se activará el temporizador. filtrar el procesamiento Con el mensaje WM_TIMER activado por el reloj, puede llamar al método de adquisición de datos de un puerto de origen específico de acuerdo con el ID del puerto y escribir: Procedimiento TCarrier.Timer(var Msg: TMsg; var Handled: Boolean);var stmp:string; Obj:TComponent;begin if Msg.message =WM_TIMER then//Procesando el mensaje del reloj comenzar//Buscar el objeto que define este mensaje según el ID del puerto que activó el messageObj:=FindComponent(' Recibir'+inttostr (Msg.WParam)); si obj=nil entonces salir;//Salir del procesamiento si no se encuentra stmp:=obj.ClassName;//Reflexionar para obtener la información de tipo de este objeto Puerto si stmp='TIdPOP3' entonces GetPOP3(TIdPOP3(Obj)); si stmp='TIdFTP' entonces GetFTP(TIdFTP(obj)); si stmp='TFilePort' entonces GetFile(TFilePort(Obj)); luego GetCOM(TCOMPort(Obj));//Llame al proceso de adquisición de datos de COMSource…………………… end;end; vi. Obtener datos El siguiente es el procedimiento de procesamiento de adquisición de datos de COMSource TCarrier.GetCOM(COMObj: TCOMPort);var stmp:string; COMInterface:OleVariant;begin try//Crear un objeto de componente COM basado en el valor de ComFace COMInterface:=CreateOleObject (COMObj.ComFace); stmp:=COMInterface.GetData //Llame al método de interfaz acordado para obtener datos mientras stmp<>#0; do // #0 es el indicador de finalización de extracción de datos acordado. start DataArrive(stmp, COMObj.Tag); // entregado al despachador de datos para el procesamiento unificado, COMObj.Tag es el ID del puerto de destino del canal donde se encuentra el objeto. stmp:=COMInterface.GetData; final; COMInterface:= Sin asignar excepto COMInterface:= Sin asignar; Complete la operación de extracción de datos y libere el objeto componente hasta la siguiente llamada de activación. El siguiente es el procesamiento de adquisición de datos de TCPSource: procedimiento TCarrier.TCPServersClientRead(Sender: TObject; Socket:TCustomWinSocket);beginDataArrive(socket.ReceiveText,integer(TServerWinSocket(). sender).data ^));//Dejado al despachador de datos para procesamiento unificado, El segundo parámetro es el valor del puntero de ID del puerto de destino adjunto al remitente del objeto Socket; los diferentes tipos de objetos del puerto de origen reciben datos de diferentes maneras, pero finalmente los datos recibidos se entregan al despachador de datos. Desde un nivel de implementación, cada vez que se agrega un objeto de recepción de datos y se implementa su recepción de datos, se implementa un nuevo Puerto de Origen para Transceiver Shell. Nota: El autor aquí solo implementa la recepción de datos de texto. Es posible que los usuarios necesiten recibir objetos de memoria, flujos de datos o datos binarios, y simplemente realizar pequeños cambios en el código de recepción. vii Programación de datos La programación de datos del Servicio Transceptor se completa mediante la unidad lógica del Despachador de datos. La tarea principal del Despachador de datos es administrar y controlar de manera uniforme los datos recibidos desde diferentes puertos de origen y trabajar en conjunto con el Controlador de canal. De acuerdo con el canal, defina la distribución de datos a diferentes puertos de destino, supervise si los resultados del envío son exitosos y decida si los datos deben enviarse al administrador de colas y al registrador de registros para el almacenamiento en búfer y el procesamiento de registros en función de los resultados del envío y la configuración. de la biblioteca de configuración del sistema. A continuación, observe el método DataArrive del puerto de origen que envía datos: procedimiento TCarrier.DataArrive(sData:String;PortID:Integer);var dTime:Datetime; bSendSeccess:Boolean;begin if sData='' luego salga;/ / Si los datos están vacíos, salte iLogID:=-1; dTime:= ahora //Reciba la hora si sData[length(sdata)]=#0 entonces; sdata:=copy(sdata,1,length(sdata)-1);//Formato de cadena para compatibilidad con el lenguaje C bSendSeccess:=DataSend(sdata,PortID);//Llame a Data Dispatcher para enviar el método de envío, PortID es Target Port IDif (TSCfg.LogOnlyError=false) o (bSendSeccess=false) theniLogID:=writeLog(dTime, now,sData, PortID, bSendSeccess);// Registre registros de acuerdo con las reglas de procesamiento de registros y envíe los resultados en la información de configuración del sistema si (TSCfg.Queueing=True) y (bSendSeccess=false) luego PutQueue(dTime, now,sData, PortID, bSendSeccess, iLogID) // Determinar el final del procesamiento de la cola según la definición de configuración de la cola en la información de configuración del sistema del paquete, lo anterior es Datos; El método DataArrive de Dispatcher, en el que el procesamiento de cola se determina según la información de configuración del sistema y el estado de envío, también se puede ajustar al procesamiento de cola obligatorio. El siguiente es el método DataSend de Data Dispatcher, que se utiliza para distribuir y procesar datos según el tipo de puerto de destino: Función TCarrier.DataSend(sData:String;PortID:Integer):boolean;var Obj:TComponent;begin DataSend:= false;Obj:=FindComponent ('Send'+inttostr(PortID)); //Busca el objeto según el ID del puerto si (obj=nil) o (obj.Tag =-1) luego salga;//El objeto no existe o se ha marcado como no válido debido a un error de inicialización. Port case obj.Tag of 1:DataSend:=PutTCP(TClientSocket(obj),sdata 3:DataSend:=PutSMTP(TIdSMTP); (obj), sdata); 5:EnviarDatos:=PutFTP(TIdFTP(obj),sdata); 7:Envío de datos:=PutHTTP(TIdHTTP(obj),sdata); 9:Envío de datos:=PutFile(TFilePort(obj),sdata); 11:Envío de datos:=PutMSMQ(TMSMQPort (obj),sdata); PutDB(TDBPort(obj),sdata); 15:DataSend:=PutCOM(TCOMPort (obj),sdata); …………… …………… end;end Vale la pena señalar que si no se utiliza una matriz de objetos, pero solo hay una instancia de cada uno; tipo de puerto Si es así, una mejor manera de manejar la distribución de datos sería usar una función de devolución de llamada, pero en el caso actual, eso conduciría a no saber qué miembro de la matriz de objetos debe manejar los datos. Además, el método de procesamiento actual no separa completamente Transceiver Kernel y Transceiver Shell, y deberíamos buscar un método de procesamiento más abstracto e independiente. viii. Envío de datos La siguiente es la función de envío de TCP TCarrier.PutTCP(TCPOBJ:TClientSocket;sdata:string):Boolean;var itime:integer;begin PutTCP:=false; try TCPOBJ.Close; gettickcount; // Hora de inicio de repetición de la aplicación.ProcessMessages hasta (TCPOBJ.Active=true) o (gettickcount-itime>5000); // Salga del bucle si la conexión es exitosa o se produce un tiempo de espera de 5 segundos si TCPOBJ.Active luego comienza TCPOBJ.Socket.SendText(sdata):=true;//The); el valor de retorno es solo cuando los datos se envían correctamente. Trueend;TCPOBJ.Close; exceptoTCPOBJ.Close end;La siguiente es la función de envío de COM. TCarrier.PutCOM(COMOBJ:TCOMPort;sdata:string):Boolean;var Com:OleVariant;begin PutCOM:=false; intente Com:=CreateOleObject(COMOBJ.ComFace);//Crear una interfaz predefinida PutCOM:=Com.PutData ( sdata);//Llamar al método predefinido Com:= Unassigned exceptoCom:= Unassigned; end; Otros tipos de envío de puerto son similares y no se repetirán aquí. Hasta ahora, se ha completado el procesamiento básico de Origen y Destino. Se ha establecido una función de comunicación básica después de hacer coincidir libremente diferentes tipos de Fuente y Destino, se pueden realizar funciones de comunicación completamente diferentes. Al crear múltiples canales, puede implementar de manera centralizada el procesamiento de comunicaciones para múltiples funciones diferentes. ix. Procesamiento de cola: después de enviar los datos en el método DataArrive anterior, Data Dispatcher llamará a writeLog para el registro de datos y al método PutQueue para el procesamiento de cola. Ambos procesan la información de datos de acuerdo con el. Los parámetros del sistema. El almacenamiento no es el tema central de este artículo. El procesamiento de reintento de la cola es similar al principio de distribución del procesamiento por tipo de puerto en el evento del temporizador. Se basa en el activador del temporizador de cola para leer los datos almacenados en el búfer de la base de datos y llamar a DataSend nuevamente de acuerdo con el ID del puerto de destino. reintente enviar los datos, si la transmisión es exitosa, la transacción de esta transmisión de datos se completa; de lo contrario, volverá a ingresar a la cola y esperará el siguiente momento de activación para volver a intentar hasta que la transmisión sea exitosa o se alcance el número máximo de reintentos establecido. se alcanza. tres, Resumen de la experiencia de desarrollo Dado que el objetivo de este artículo es explicar las ideas centrales y los conceptos de diseño de Transceiver, simplifica y debilita el procesamiento de subprocesos múltiples, la agrupación de objetos y el soporte de transacciones que Transceiver debería considerar como un servicio en segundo plano, y los más complejos. y poderosos grupos fuente y objetivo y Cha. integración de canal, la capacidad de enviar y recibir objetos de memoria, flujos de datos, datos binarios, la lectura de información de configuración del sistema y la implementación de sus clases de encapsulación, la seguridad del sistema y los datos, etc. Espero que los lectores puedan proporcionar algunas ideas y Comprenda las ideas de diseño de Transceiver. Inspire chispas de inspiración en el trabajo de desarrollo real y cree un software más destacado y potente. Autor: Firebird [email protected] Utilice Delphi para establecer servidores de comunicación e intercambio de datos: análisis técnico del transceptor (Parte 1) Utilice Delphi para establecer servidores de comunicación e intercambio de datos: análisis técnico del transceptor (Parte 2) Implemente clases de recopilación a través de C# Descripción general de . NET y cosas técnicas antiguas relacionadas : accesos directos a programas/eliminación de programas/EXE autoeliminado Cosas antiguas de bricolaje: notas de experiencia sobre algoritmos de programación infantil