Delphi를 이용한 통신 및 데이터 교환 서버 구축 - 트랜시버 기술 분석(2부) 작성자: 火鸟 [email protected] 2. 트랜시버 서비스에 대한 자세한 설명 1. 트랜시버 서비스 분석 요약 트랜시버 서비스는 트랜시버 시스템의 핵심 구성 요소입니다. 트랜시버 커널은 시스템 구성 라이브러리에서 트랜시버 콘솔에 의해 설정된 포트 및 채널 정의와 매개 변수를 읽고 런타임 동안 통신 포트와 해당 관계를 동적으로 생성하고 제어하는 일을 담당합니다. , 데이터 수신, 전송 및 버퍼링 예약, 로그 및 대기열 관리 등을 수행합니다. 트랜시버 쉘은 데이터 전송 및 수신을 위해 지원되는 모든 유형의 포트를 구현합니다. 2. 트랜시버 서비스 설계 요약 트랜시버 서비스는 델파이의 서비스 애플리케이션에서 개발되었습니다. 서비스 애플리케이션은 사용자 상태가 아닌 시스템 상태에서 실행될 수 있습니다. 운영 체제 서비스 제어 관리자(SCM)는 프로그램의 운영 및 관리를 담당합니다. 서비스에는 사용자 인터페이스가 없으며 시스템 백그라운드 프로그램에 속합니다. Transceiver Kernel은 Transceiver Shell을 구축하고 제어하는 Transceiver 클래스의 일련의 메소드이며, Transceiver Shell은 통신을 담당하는 개체의 집합입니다. 참고: 성능 및 로드 고려사항으로 인해 트랜시버 커널은 아키텍처 다이어그램에서 기능 분할을 논리적으로만 구현하며 구성 모듈은 완전한 객체 기반 방식으로 구현되지 않습니다. 3. 트랜시버 서비스 구현 요약 i. 서비스 애플리케이션을 생성합니다. Delphi 메인 메뉴 File...에서 NEW|Service Application을 선택하면 생성된 프로그램을 볼 수 있습니다. 프레임워크는 다음과 같습니다: PROgram Project1; 'Unit1.pas'에서 SvcMgr, Unit1을 사용합니다. {Service1: TService};{$R *.RES}begin Application.Initialize(TService1, Service1); Application.Run;end.unit Unit1;인터페이스는 Windows, 메시지, SysUtils, 클래스, 그래픽, 컨트롤, SvcMgr, 대화 상자를 사용합니다. TService1 = class(TService) private { Private 선언 } public function GetServiceController: TServiceController override; end;var Service1: TService1;implementation{$R *.DFM}프로시저 ServiceController(CtrlCode: DWord); stdcall;begin Service1.Controller(CtrlCode);end;function TService1.GetServiceController: TServiceController;begin Result := ServiceController;end;end 사용 유닛에서 참조되는 서비스 관리에 사용되는 SvcMgr 외에도 TService1은 TForm 대신 TServiced에서 상속하고, 오버로드된 GetServiceController 함수와 stdcall 모드에서 호출되는 ServiceController 프로세스를 사용하여 생성되는 것을 볼 수 있습니다. 델파이 서비스 프로그램은 크게 특별할 것이 없지만, 델파이 팬들이 다시 응원하고 싶어진다는 것이 델파이 RAD의 강력한 매력이다. 또한 서비스 응용 프로그램은 런타임에 직접 디버깅할 수 없고 사용자 인터페이스가 없기 때문에 개발 중에 디버깅 및 문제 해결을 용이하게 하기 위해 디버깅 정보의 인터페이스 없는 출력을 고려해야 합니다. ii. 특정 요구 사항을 충족하는 포트 클래스를 생성하고 통합된 실행 및 처리 메커니즘으로 트랜시버 커널을 사용하려면 트랜시버 셸의 포트에 통합 처리 규칙이 필요합니다. 셸의 일부 포트는 Delphi의 기존 구성 요소 클래스입니다. 개발 환경(TCP, FTP 등)과 그렇지 않은 경우(MSMQ, File 등) 이때 필요에 맞는 클래스를 만들어야 합니다. 예: //사용자 인터페이스가 없으므로 TControl 대신 TComponent에서 상속됩니다. TFilePort=class(TComponent) private FilePath:string;//파일의 폴더 위치를 가져오거나 저장합니다. Prefix:string;//File prefix suffix:string; //파일 접미사 end; TFilePort 클래스를 설정한 후 트랜시버 커널은 FilePath가 지정한 폴더에서 특정 파일에 액세스하는 목적을 달성하기 위해 통합 클래스 처리 방법을 사용할 수 있습니다. Source로 사용하면 조건에 맞는 파일을 특정 폴더에서 가져옵니다. Target으로 사용하면 해당 Source에서 가져온 데이터가 지정된 파일에 기록됩니다. 실제로 각 Port 개체의 실제 매개 변수는 시스템 구성 라이브러리의 포트 테이블 정의). 또 다른 예: type TCOMPort=class(TComponent) private ComFace:string; //데이터를 얻거나 제출하기 위한 COM 인터페이스 end; TCOMPort는 지정된 COM 구성 요소 인터페이스에서 데이터를 얻거나 데이터를 제출하는 데 사용됩니다. 후속 처리를 수행합니다. Delphi에서 OleVariant 클래스는 COM 구성 요소 호출을 구현하는 방법 중 하나입니다. TCOMPort 클래스를 사용해야 하는 이유는 Transceiver가 필요한 데이터 액세스가 필요할 때만 TCOMPort에 의해 정의된 COM 인터페이스를 OleVariant 개체로 인스턴스화하고 개체를 인스턴스화한다는 것입니다. 사용 후 해제됩니다. 이는 트랜시버 및 COM 서버의 부하 압력을 줄일 수 있습니다. 다른 유사한 구성 요소에도 동일한 고려 사항이 적용됩니다. 여기서 작성자가 제공하는 클래스 예제는 단지 모델일 뿐이며, 필요할 경우 적절한 메소드와 이벤트를 추가해야 합니다. 개발 중에 작성자가 구현한 클래스에는 TCOMPort, TMSMQPort, TDBPort, TFilePort 등이 있습니다.iii. 다중 채널 지원 - 포트를 선언하는 객체의 배열입니다. 트랜시버는 통신 프로세스를 소스에서 타겟으로의 데이터 흐름 프로세스로 간주하며, 이 채널은 최소 2개로 구성됩니다. 포트(소스용 하나, 대상용 하나)이므로 소스와 대상이 자유롭게 결합된 여러 채널을 무한정 정의하려면 소스와 대상에 대해 각각 선언해야 합니다. 다양한 Port 클래스의 객체 배열(나중에 볼 수 있듯이 이에 대한 해당 관계 설정) 예를 들면 다음과 같습니다. private { Private 선언 }TCPSource:TServerSocket 배열;//TCP 소스용 개체 배열 TCPTarget:TClientSocket 배열;//TCP 대상용 개체 배열 MailSource:TIdPOP3 배열 //메일 소스 개체 배열 MailTarget:array; TIdSMTP; //메일 대상 파일 소스의 객체 배열:TFilePort의 배열; //파일 소스의 객체 배열 fileTarget:TFilePort의 배열; //파일 대상의 객체 배열 comSource:TCOMPort의 배열; //COM 소스의 객체 배열 comTarget:TCOMPort의 배열 참고: 이후 동일한 유형의 Source 및 Target에 대한 포트 작동 규칙은 완전히 다르므로 Transceiver 개념에서는 완전히 다르며 직접적인 관련 개체가 아닌 것으로 간주됩니다. 따라서 동일한 유형의 Port에 대해서도 Source와 Target에 따라 객체 배열이 별도로 생성됩니다. iv. 런타임에 객체 배열을 인스턴스화합니다. 각 객체 배열의 요소 수는 런타임에 포트 빌더에 의해 관리됩니다. 사용자가 트랜시버 콘솔을 통해 일부 포트 유형을 정의하면 포트 빌더는 해당 수에 따라 이를 인스턴스화합니다. 해당 매개변수입니다. 그렇지 않으면 개체 배열이 인스턴스화되지 않습니다. 소스 유형 포트 개체에서 이름 속성은 '수신'+포트 ID 형식으로 설정됩니다. 이는 후속 데이터 수신 트리거에서 데이터 디스패처가 개체를 찾고 다양한 유형의 포트 개체를 균일하게 예약하는 데 도움이 됩니다. Tag 속성은 채널 컨트롤러에 해당 채널의 대상 ID 정보를 제공하는 데 사용됩니다. 다음은 포트 빌더의 comSource 개체 배열의 인스턴스화 부분입니다. start //COM 만들기/ 포트 수신 itmp:=high(comSource)+1;// comSource의 현재 최대 수를 가져옵니다. itmp는 정수 변수 SetLength(comSource)입니다. ,itmp +1); // comSource 배열 멤버 추가 comSource [itmp]:=TCOMPort.Create(self) // comSource[itmp].Name:= 멤버 인스턴스화 'Receive'+inttostr(isource); //Name 속성을 'Receive'+Port ID로 설정하고 isource는 정수 유형 comSource의 현재 PortID입니다. [itmp].Tag:= itarget; NullTest :=rece.Fields['Address'].value;//시스템 구성 COMFace의 값을 가져옵니다. NullTest는 Variant 변수입니다. if (NullTest <>null) and (trim(NullTest)<>'') 그런 다음 startcomSource [itmp].ComFace:=NullTest; //ComFaceNullTest:=rece.Fields['interval'].value에 유효한 값을 할당합니다. //시스템 구성에서 데이터를 얻기 위한 COM 개체의 트리거 시간 간격을 가져옵니다. SetTimer(application.handle,isource,NullTest*60000 ,nil); //정기적으로 데이터를 수집하기 위해 현재 포트에 대한 트리거 시계를 설정합니다. isource는 포트 IDendelsecomSource입니다. [itmp].Tag:=-1;//초기화 실패, 잘못된 포트로 표시됨 end; comSource는 ComFace에 정의된 인터페이스를 호출하고 comTarget에 해당하는 특정 시간 간격 후에 데이터를 얻는 데 사용되는 소스 클래스 포트입니다. comTarget의 ComFace에 데이터를 제출하는 것이 실시간 프로세스이므로 트리거 간격을 사용할 필요가 없고 시계 설정을 위한 두 문을 생략할 수 있다는 점을 제외하면 유사합니다. 다른 유형의 Port 객체 생성 및 초기화도 비슷합니다. 예를 들어, 다른 MailTarget 구현 부분은 다음과 같습니다. start //Create SMTP/Send Port itmp:=high(MailTarget)+1; SetLength(MailTarget,itmp+1); itmp].Name:='send'+ inttostr(itarget); MailTarget[itmp].Tag:=3;//대상 포트 유형 식별로 설정 NullTest:=rece.Fields['Address'].value; //메일 서버 주소 if (NullTest <>null) and (trim(NullTest) <>'') then MailTarget[itmp].Host :=NullTest else bValid:=false; NullTest:=rece.Fields['Port'].value; //메일 서버 포트 if NullTest <>null then(if NullTest<>0 then MailTarget[itmp].Port :=NullTest)else bValid:=false; =rece.Fields['user'].value;//NullTest <>null인 경우 로그인 사용자 이름 thenMailTarget[itmp].UserId :=NullTest else bValid:=false; NullTest:=rece.Fields['password'].value;//로그인 비밀번호...... 혼란스럽습니다. 런타임 시 포트 빌더에 의해 수많은 트랜시버 셸 통신 구성 요소가 생성됩니다. 트랜시버 서비스의 성능이 높아질까요? 실제로, ServiceCreate 이벤트가 발생하면 포트 빌더의 임무가 한 번 완료됩니다. 쉘 포트 수는 트랜시버 서비스의 초기화 속도와 트랜시버 서비스의 전체 성능에만 영향을 미칩니다. 물론 시스템 리소스를 조금 더 차지할 수도 있습니다. v. 이벤트의 동적 할당 및 처리 Transceiver Shell이 지원하는 여러 통신 포트 중 TServerSocket을 사용하십시오(Indy의 통신 컴포넌트를 사용하는 것이 더 나을 수도 있지만 이는 Transceiver Service의 설계 아이디어에 위배되지 않으며 단지 수정일 뿐입니다). 쉘 수준에서 또는 방금 추가됨) 구현된 TCPSource는 소스 포트인 TServerSocket이 정기적으로 트리거되어야 하는 COM 또는 POP3와 같은 개체와 다르기 때문에 더 구별됩니다. 서비스는 시작된 후 항상 수신 상태에 있으며, ClientSocket이 연결되어 데이터를 보낼 때 해당 이벤트를 생성하는 구성 요소입니다. 다음은 TCPSource의 인스턴스화 부분입니다. start //TCP/수신 포트 생성 itmp:=high(TCPSource)+1;SetLength(TCPSource,itmp+1); TCPSource [itmp]:=TServerSocket.Create(self); [ itmp].OnClientRead:=TCPServersClientRead;//OnClientRead 이벤트 처리 프로세스를 TCPServersClientRead TCPSource에 할당합니다. [itmp].OnClientError:=TCPServerClientError;//OnClientError 이벤트의 처리 프로세스를 TCPServerClientErrorTCPSource에 할당합니다. [itmp].Name:= 'Receive'+inttostr(isource) //Name 속성을 'Receive'+Port ID로 설정합니다. TCPSource [itmp ].Tag:=itarget; //해당 채널의 대상 IDTCPSource를 설정합니다. [itmp].Socket.Data:=@ TCPSource [itmp].Tag;//이 포트 개체의 대상 ID를 소켓 개체에 포인터 데이터로 연결합니다........................ end; 다시 돌아와서 인스턴스화 중에 트리거를 설정합니다. 시계가 작동하지만 시계가 트리거될 때 이벤트를 처리하는 방법은 무엇입니까? 마찬가지로 이벤트 처리의 동적 할당이기도 합니다. comSource의 시계 처리 정의는 ServiceCreate 이벤트 처리에 추가될 수 있습니다. application.OnMessage:=Timer; 메시지 처리의 오버로드를 구현하려면 Timer 이벤트에서 타이머가 트리거됩니다. 시계에 의해 트리거된 WM_TIMER 메시지를 사용하여 포트 ID 및 유형에 따라 특정 소스 포트의 데이터 수집 방법을 호출할 수 있습니다. Procedure TCarrier.Timer(var Msg: TMsg; var Handled: Boolean);var stmp:string; Obj:TComponent;begin if Msg.message =WM_TIMER then//시계 메시지 처리 시작//메시지를 트리거한 포트 ID에 따라 이 메시지를 정의하는 개체 찾기:=FindComponent( 'Receive'+inttostr (Msg.WParam)); obj=nil이면 종료;//찾지 못하면 처리를 종료합니다. stmp:=obj.ClassName;//다음인 경우 이 Port 객체의 유형 정보를 얻기 위해 반영합니다. stmp='TIdPOP3' then GetPOP3(TIdPOP3(Obj)); if stmp='TIdFTP' then GetFTP(TIdFTP(obj)) if stmp='TFilePort' then GetFile(TFilePort(Obj)); then GetCOM(TCOMPort(Obj));//COMSource의 데이터 수집 프로세스를 호출합니다. end;end; vi. 데이터 가져오기 다음은 COMSource의 데이터 수집 처리 절차입니다. TCarrier.GetCOM(COMObj: TCOMPort);var stmp:string;begin try//값을 기반으로 COM 구성 요소 개체를 만듭니다. ComFace COMInterface:=CreateOleObject (COMObj.ComFace); stmp:=COMInterface.GetData; //stmp<>#0 동안 데이터를 가져오기 위해 합의된 인터페이스 메서드를 호출합니다. do // #0은 합의된 데이터 추출 종료 플래그입니다. start DataArrive(stmp, COMObj.Tag); // 통합 처리를 위해 데이터 Dispatcher에 전달됩니다. COMObj.Tag는 개체가 위치한 채널의 대상 포트 ID입니다. stmp:=COMInterface.GetData; end ; COMInterface:= 할당되지 않음; COMInterface 제외:= 할당되지 않음;// 데이터 추출 작업을 완료하고 다음 트리거 호출까지 구성 요소 개체를 해제합니다. 다음은 TCPSource의 데이터 수집 처리입니다. TCarrier.TCPServersClientRead(Sender: TObject; Socket:TCustomWinSocket);beginDataArrive(socket.ReceiveText,integer(TServerWinSocket( sender).data ^));//통합 처리를 위해 데이터 Dispatcher에 맡김, 두 번째 매개변수는 소켓 객체 송신자 end에 첨부된 대상 포트 ID 포인터 값입니다. 서로 다른 유형의 소스 포트 객체는 서로 다른 방식으로 데이터를 수신하지만 최종적으로 수신된 데이터는 데이터 디스패처로 전달됩니다. 구현 수준에서 데이터 수신 개체가 추가되고 해당 데이터 수신이 구현될 때마다 트랜시버 쉘에 대해 새로운 소스 포트가 구현됩니다. 참고: 여기서 작성자는 텍스트 데이터 수신만 구현합니다. 사용자는 메모리 개체, 데이터 스트림 또는 바이너리 데이터를 수신하고 수신 코드를 약간만 변경해야 할 수도 있습니다. vii 데이터 스케줄링 트랜시버 서비스의 데이터 스케줄링은 데이터 디스패처 논리 장치에 의해 완료됩니다. 데이터 디스패처의 주요 작업은 서로 다른 소스 포트에서 수신된 데이터를 균일하게 관리 및 제어하고 채널 컨트롤러와 함께 작동하는 것입니다. 채널 정의에 따라 다양한 대상 포트에 대한 데이터 배포에 따라 전송 결과가 성공적인지 모니터링하고 전송 결과 및 설정을 기반으로 버퍼링 및 로그 처리를 위해 데이터를 큐 관리자 및 로그 레코더에 제출해야 하는지 여부를 결정합니다. 시스템 구성 라이브러리의 다음으로, 데이터를 제출하는 소스 포트의 DataArrive 메소드를 살펴보십시오. Procedure TCarrier.DataArrive(sData:String;PortID:Integer);var dTime:Datetime:Integer; bSendSeccess:Boolean;begin if sData='' then exit; / 데이터가 비어 있으면 iLogID:=-1; dTime:= now //수신 시간 if sData[length(sdata)]=#0 then sdata:=copy(sdata,1,length(sdata)-1);//C 언어 호환성을 위한 문자열 형식 bSendSeccess:=DataSend(sdata,PortID);//디스패처 메서드를 보내기 위해 Data Dispatcher를 호출합니다. PortID는 대상 포트 ID입니다.if (TSCfg.LogOnlyError=false) 또는 (bSendSeccess=false) theniLogID:=writeLog(dTime, now,sData, PortID, bSendSeccess);//로그 처리 규칙에 따라 로그를 기록하고 결과를 시스템 구성 정보에 기록합니다. if (TSCfg.Queueing=True) 및 (bSendSeccess=false) then PutQueue(dTime, now,sData, PortID, bSendSeccess, iLogID) ; //위의 데이터는 패키지 시스템 구성 정보의 대기열 구성 정의를 기반으로 대기열 처리 종료를 결정합니다. 시스템 구성 정보 및 전송 상태에 따라 Queue 처리가 결정되는 Dispatcher의 DataArrive 방식도 필수 대기열 처리로 조정할 수 있습니다. 다음은 Target Port 유형에 따라 데이터를 배포하고 처리하는 데 사용되는 Data Dispatcher의 DataSend 메서드입니다. Function TCarrier.DataSend(sData:String;PortID:Integer):boolean;var Obj:TComponent;begin DataSend:=false ;Obj:=FindComponent ('Send'+inttostr(PortID)); //포트 ID를 기준으로 객체 찾기 if (obj=nil) or (obj.Tag =-1) 그런 다음 종료합니다.//초기화 실패로 인해 개체가 존재하지 않거나 유효하지 않은 것으로 표시되었습니다. 포트 케이스 obj.Tag of 1:DataSend:=PutTCP(TClientSocket(obj),sdata); (obj), sdata); 5:DataSend:=PutFTP(TIdFTP(obj),sdata); 7:DataSend:=PutHTTP(TIdHTTP(obj),sdata); 9:DataSend:=PutFile(TFilePort(obj),sdata); 11:DataSend:=PutMSMQ(TMSMQPort(obj),sdata); PutDB(TDBPort(obj),sdata); 15:DataSend:=PutCOM(TCOMPort (obj),sdata); …………… …………… end;end; 포트 유형 그렇다면 데이터 분산을 처리하는 더 좋은 방법은 콜백 함수를 사용하는 것이지만 이 경우 개체 배열의 어떤 멤버가 데이터를 처리해야 하는지 알 수 없게 됩니다. 또한, 현재 처리 방식은 Transceiver Kernel과 Transceiver Shell을 완전히 분리하지 못하므로, 보다 추상적이고 독립적인 처리 방식을 모색해야 합니다. viii.데이터 전송 다음은 TCP TCarrier.PutTCP(TCPOBJ:TClientSocket;sdata:string):Boolean;var itime:integer;begin PutTCP:=false; try TCPOBJ.Close; gettickcount; //(TCPOBJ.Active=true)까지 시작 시간 반복 application.ProcessMessages; (gettickcount-itime>5000); //연결이 성공하거나 TCPOBJ.Active가 TCPOBJ.Socket.SendText(sdata)를 시작하면 5초 시간 초과가 발생하면 루프에서 빠져나옵니다. //The 반환 값은 데이터가 성공적으로 전송되었을 때입니다. Trueend;TCPOBJ.Close end; 다음은 COM의 전송 기능입니다. TCarrier.PutCOM(COMOBJ:TCOMPort;sdata:string):Boolean;var Com:OleVariant;begin PutCOM:=false; try Com:=CreateOleObject(COMOBJ.ComFace);//미리 정의된 인터페이스 만들기 PutCOM:=Com.PutData( sdata);//미리 정의된 메서드 호출 Com:= 할당되지 않음; ExceptCom:= 할당되지 않음; end; 다른 유형의 포트 전송도 유사하므로 여기서는 반복하지 않습니다. 이제 Source와 Target의 기본적인 처리가 완료되었습니다. 다양한 종류의 Source와 Target을 자유롭게 매칭하여 기본적인 통신 기능을 구축한 후 완전히 다른 통신 기능을 구현할 수 있습니다. 여러 채널을 생성하면 다양한 기능에 대한 통신 처리를 중앙에서 구현할 수 있습니다. ix.큐 처리: 위의 DataArrive 메서드로 데이터가 전송된 후 Data Dispatcher는 데이터 로깅을 위해 writeLog를 호출하고 큐 처리를 위해 PutQueue 메서드를 호출합니다. 둘 다 데이터 정보를 처리하는 방식이 비슷합니다. 시스템 매개변수 스토리지는 이 문서의 초점이 아닙니다. 큐의 재시도 처리는 Timer 이벤트에서 포트 유형별로 처리를 분산시키는 원리와 유사합니다. 큐 타이머의 트리거를 사용하여 데이터베이스에서 버퍼링된 데이터를 읽고 대상 포트 ID에 따라 DataSend를 다시 호출합니다. 데이터 전송을 다시 시도합니다. 전송이 성공하면 이 데이터 전송의 트랜잭션이 완료됩니다. 그렇지 않으면 큐에 다시 들어가 전송이 성공하거나 설정된 최대 재시도 횟수에 도달할 때까지 재시도할 다음 트리거 시간을 기다립니다. 도달했습니다. 삼, 개발 경험 요약 이 기사의 초점은 Transceiver의 핵심 아이디어와 설계 개념을 설명하는 것이므로 Transceiver가 백그라운드 서비스로 고려해야 하는 멀티 스레딩 처리, 개체 풀링 및 트랜잭션 지원을 단순화하고 약화시키며, 더 복잡한 강력한 소스 및 대상 그룹 관리 및 Cha. 네트워크 통합, 메모리 개체, 데이터 스트림, 바이너리 데이터를 보내고 받는 기능, 시스템 구성 정보 읽기 및 캡슐화 클래스 구현, 시스템 및 데이터 보안 등. 독자들이 통찰력과 정보를 제공할 수 있기를 바랍니다. Transceiver의 디자인 아이디어를 이해하고 실제 개발 작업에 영감을 불어 넣어 더욱 우수하고 강력한 소프트웨어를 만듭니다. 작성자: Firebird [email protected] Delphi를 사용하여 통신 및 데이터 교환 서버 구축 - 트랜시버 기술 분석(1부) Delphi를 사용하여 통신 및 데이터 교환 서버 구축 - 트랜시버 기술 분석(2부) C#을 통해 컬렉션 클래스 구현 . NET 컬렉션 및 관련 오래된 기술적 인 것: 프로그램 바로가기/프로그램 삭제/EXE 자체 삭제 DIY 오래된 것: 어린 시절 프로그래밍 알고리즘 경험 노트