如何分离通信物理接口和应用程序<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

         上一篇,简单讲了如何理解通信,以及应用层通信编程的框架和简单范例代码。今天,我们来谈一下,如何使应用程序和不同的物理接口无关,也就是说,如果程序原使用串口,需要换用TCP/IP, 只需要换一个传输的控件而不需要对应用程序大量修改。
一、   我们在使用通信控件时,为什么需要隔离物理传输和应用程序?

我们常用的数据控件如:串口,TCP/IPUDP等,封装了Windows API去处理不同的传输特性,例如:不同的属性,不同的错误,不同的发送和接收数据流程,如果我们在应用程序中,直接使用此类控件,就会造成应用程序依赖于这些物理特性,而导致我们要换用不同的物理传输时(例如:从串口换到TCP/IP),应用程序进行大量修改。所以,为了避免这种情况,我们需要找到一种办法,去隔离物理传输对应用程序的影响。

 

二、   我们如何来实现隔离物理传输对应用程序的影响

  2.1设计思路

     实现我们看一下,应用程序关心什么?它关心收到数据和发送数据,以及各种状态(例如:发送前,发送后,记录Log和错误信息等。
所以,我们的设计思路是,提供一个通用的中间类,来隔离物理传输和应用程序。

 

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />
应用程序Application
中间类TwyqDataLink
物理传输控件
(SerialTCP/IPUDP)
中间类的后代

 

 

 


                  

 

例如:对TCP/IP我们定义类TwyqTCPServer:TwyqDataLinkTwyqTCPClient:TwyqDataLink,对串口,我们使用TwyqSerial:TwyqDataLink,在应用程序中,不管我们使用串口或TCP/IP,我们使用MyDataLink:TwyqDataLink; 如果使用串口,则MyDataLink=TwyqSerial的实例,如果使用TCP/IP,则MyDataLink=TwyqTCPSevrer的实例或MyDataLink=TwyqTCPClient的实例。这样,应用程序中,使用MyDataLink的代码,就不会被具体的物理控件所影响。

 

2.2 具体实现

 2.2.1我们可以定义如下的abstract的数据链路类:

<code>
TLinkEvent = procedure (Sender: TObject;MSG:String) of object;
 TwyqDataLink = class (TComponent)
  private
    FONAfterSend: TLinkEvent;
    FONBeforeSend: TLinkEvent;
    FONConnect: TNotifyEvent;
    FONError: TLinkEvent;
    FONLog: TLinkEvent;
    FONReceive: TLinkEvent;
    FONSyncReceive: TLinkEvent;
  protected
    function GetActive: Boolean; virtual; abstract;
    procedure SetActive(Value: Boolean); virtual; abstract;
  public
    function Close: Boolean; virtual; abstract;
    function Open: Boolean; virtual; abstract;
    function Read(Var Text:String;Count:cardinal;var rCount:cardinal): Boolean;
            virtual; abstract;
    function Send(CMD:String;Var rCount:cardinal): Boolean; virtual; abstract;
    property Active: Boolean read GetActive write SetActive;
  published
    property ONAfterSend: TLinkEvent read FONAfterSend write FONAfterSend;
    property ONBeforeSend: TLinkEvent read FONBeforeSend write FONBeforeSend;
    property ONConnect: TNotifyEvent read FONConnect write FONConnect;
    property ONError: TLinkEvent read FONError write FONError;
    property ONLog: TLinkEvent read FONLog write FONLog;
    property ONReceive: TLinkEvent read FONReceive write FONReceive;
    property ONSyncReceive: TLinkEvent read FONSyncReceive write FONSyncReceive;
  end;
</code>

 

2.2.2 我们再来看一下实现TCPClient的类定义

TBufferKind=(skNormal,skBufferIsOne,skNoBuffer);
TwyqClientSocket = class (TwyqDataLink)
  private
    FPort: TClientSocket;
    FWaitOpenTimer:integer;
    FOnConnecting:TSocketNotifyEvent;
    FOnError:TSocketErrorEvent;
    FAsyncConnect:Boolean;
    FBufferKind:TBufferKind;
    FIsThreadMode:Boolean;
    FBuf:TSyncBuf;
    FThread:TwyqMsgToThread;
    procedure FSetPort(Value: TClientSocket);
  protected
    function GetActive: Boolean; override;
    procedure impONRead(Sender:TObject;Socket: TCustomWinSocket);
    procedure SetActive(Value: Boolean); override;
    procedure csError(Sender: TObject; Socket: TCustomWinSocket;
  ErrorEvent: TErrorEvent; var ErrorCode: Integer);
    Procedure csConnecting(Sender:TObject;Socket:TCustomWinSocket);
  public
    property WaitOpenTimer:Integer read FWaitOpenTimer write FWaitOpenTimer;
    function Close: Boolean; override;
    function Open: Boolean; override;
    function Read(Var Text:String;Count:cardinal;var rCount:cardinal): Boolean;
            override;
    function Send(CMD:String;Var rCount:cardinal): Boolean; override;
    constructor Create(owner:TComponent);Override;
    destructor Destroy;override;
  published
    Property BufferKind:TBufferKind read FBufferKind write FBufferKind;
    property Socket: TClientSocket read FPort write FSetPort;
    Property IsThreadMode:Boolean read FIsThreadMode write FIsThreadMode;
  end;

 

2.2.3        上面代码中,我们看到两个新类TSyncBufTwyqMessgaeToThread和一个IsThreadMode的属性,下面解释一下,它们用于什么:

 

a)      如果IsThreadMode=True,那么,我们将会在创建的TwyqMessgaeToThread线程中,去调用ONReceive事件,否则,直接调用ONReceive事件。

 

b)      TSyncBuf是为了实现一个线程安全的先进先出的String队列,它的定义如下:
<code>
TSyncBuf = class (TPersistent)
  private
    FCriticalSection: TRTLCriticalSection;
    FStringList: TStringList;
  protected
    procedure lock;
    procedure unlock;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Inbuf(Const S:string);
    Procedure InMultiBuf(Consts:Array of String);
    function OutBuf: string;
    Function IsNotEmpty:Boolean;
  end;
</code>

 

c)      TwyqMessgaeToThread的定义如下:
<code>
TwyqMsgToThread=class(TThread)
 Private
   FBuf:TSyncBuf;
   FReceiver:TwyqDataLink;
 protected
  procedure Execute;override;
 public
  Constructor Create(Receiver:TwyqDataLink;buf:TsyncBuf);
 end;
</code>

 

三、   通过上述的设计,我们就可以做到分离物理传输和应用程序,具体实现大家可以尝试去做,如果有任何困难或需要,请在评论中留言,或发e-mail:TigerII@vip.sina.com.cn

 

    希望此篇文章,可以帮助大家设计出一个具有良好结构,分离物理传输和应用程序的通信程序。