delphi下使用indy进件socket通信的简单实例介绍
????? 很久沒有專門用過delphi 了,大概有二三年了吧,最近接手一與銀行有關的項目,對方銀行使用的是unix操作系統,用socket與項目程序進行通信。而這邊項目肯定是要在windows下跑的,由于考慮到unix下socket采用的是阻塞式通信,由于項目緊張,思索再三,決定采用delphi的indy組件來編寫服務器端的通信部分。其中通信的格式如下定義:
| 信息段 | 類型 | 長度(單位:字節) | 說明 |
| 包長 | 字符 | 6 | 左對齊,右補空格,包長指報文體長度(不包含交易碼長度及目標保險公司代碼長度)。 |
| 交易碼 | 字符 | 7 | 左對齊,右補空格,參見下表 |
| 目標保險公司代碼 | 字符 | 6 | 默認為006 |
| 報文體 | 字符 | ? | XML報文,不定長 |
?
?
??? 首先,簡單用indy做了一個對端(客戶端)模擬程序,按照指定的通信協議,用socket發送數據包,
? procedure TForm1.Button1Click(Sender: TObject); varmsSend,msRecv:string;amp :TStream;iRet:integer;a:STring;ia:String; beginmsSend:= mmoinput.Text; //發送文本框中的內容//計算XML包文的長度,并按照協議保持六位數ia:=IntToStr(Length(msSend));if Length(ia) =5 then ia:=ia+' 'else if Length(ia) =4 then ia:=ia+' 'else if Length(ia) =3 then ia:=ia+' 'else if Length(ia) =2 then ia:=ia+' 'else if Length(ia) =1 then ia:=ia+' 'else if Length(ia) >6 then ia:=leftstr(ia,6);mssend:= ia + edt1.Text +'006 '+mssend; //按通信協議拼接需要通過socket發送的內容iRet:=SendStream(IdTCPClient1,trim(edit1.Text),strtoint(trim(edit2.Text)),msSend,msRecv); //調用socket通信函數mmoOutput.Text:='';mmoOutput.Lines.Add(msRecv); //把服務器返回的內容顯示出來 end;function TForm1.SendStream(const AIdTCPClient:TIdTCPClient;const Aip:string;const Aport:integer;var AsendStream,ARecvStream:string):integer; beginresult:=-1;if AIdTCPClient=nil then exit;if not AIdTCPClient.Connected thenbegintryAIdTCPClient.Host:=Aip ;AIdTCPClient.Port:=Aport;AIdTCPClient.Connect(200); //TCP建立連接exceptresult:=-2;AIdTCPClient.Disconnect;exit;end;end;tryaidtcpclient.Write(AsendStream); //直接把準備好發送的內容,寫入緩沖區即可exceptresult:=-3;AIdTCPClient.Disconnect;exit;end;try//接收ARecvStream:=aidtcpclient.CurrentReadBuffer(); //把服務器端返回的內容接收到。此處是直接把接收緩沖區的內容全部收完,沒有按通信協議進行指定接收result:=1;exceptresult:=-4;AIdTCPClient.Disconnect;exit;end; end;
?
?
?服務器端的接收程序如下。
?procedure TdmMain.IdTCPServer1Execute(AThread: TIdPeerThread); varstrlog,strcode:string;ileng :integer;tmpException: Exception; beginif Athread=nil then exit;if Not AThread.Connection.Connected then exit;if Athread.Terminated then exit;trytry//接收流trystrlog:=AThread.Connection.ReadString(19);//首先接收指定的19個字符,如前面協議中定義的if strlog <> '' thenbeginileng:=StrToIntDef(trim(LeftStr(strlog,6)),0); //取出前6個字符,即正式包文體的內容的長度strcode:=MidStr(strlog,7,7); //取出后7位,分析此數據包的含義end;(AThread.Data as TThreadData).FStrRevc:= athread.Connection.ReadString(ileng);//接收指定長度的報文體except//返回,解包錯誤MakeErrorXmlStream((AThread.Data as TThreadData).FStrSend);end;if ((AThread.Data as TThreadData).FStrRevc = '') or (Length((AThread.Data as TThreadData).FStrRevc) <> ileng) then //檢查收到的數據包是否完整beginMakeErrorXmlStream((AThread.Data as TThreadData).FStrSend);endelsebegin//處理接收到的流,并返回要發送的流KentDealStream(strcode,(AThread.Data as TThreadData).FStrRevc ,(AThread.Data as TThreadData).FStrSend);end;//按指定的通信格式,拼接包文strlog:=inttostr(Length((AThread.Data as TThreadData).FStrSend));if Length(strlog) =5 then strlog:=strlog+' 'else if Length(strlog) =4 then strlog:=strlog+' 'else if Length(strlog) =3 then strlog:=strlog+' 'else if Length(strlog) =2 then strlog:=strlog+' 'else if Length(strlog) =1 then strlog:=strlog+' 'else if Length(strlog) >6 then strlog:=leftstr(strlog,6);(AThread.Data as TThreadData).FStrSend:= strlog+ strcode+'006 '+ (AThread.Data as TThreadData).FStrSend;AThread.Connection.Write((AThread.Data as TThreadData).FStrSend);//發送數據finallyend;exceptAThread.Connection.Disconnect;tmpException := ExceptObject as Exception;oLogfile.addtolog('IdTCPServer1Execute 異常!'+' 原因:'+TmpException.Message);end; end;
??
?由于indy采取的是由unix相同的阻塞式通信,采用多線程實現,所以使用起來比較簡單。indy把socket已經封裝得比較完善,包括收發字符串、二進制流等。
?
附上indy 的 TIdTCPClient 的常用方法函數:
比方說這個讀取緩沖區的數據,就有很多種方法。相對于TTcpClient的幾種方法來說,TIdTCPClient確實提供了多種選擇,不仔細研究真的容易糊涂(其實我比較喜歡用CurrentReadBuffer):
1、ReadFromStack
原型:function ReadFromStack(const ARaiseExceptionIfDisconnected: boolean; const ATimeout: integer; const AUseBuffer: boolean; ADestStream: TIdBuffer): integer; virtual;
用于判斷緩沖區里是否還有數據可讀,返回值:Integer - Number of bytes read.
2、CurrentReadBuffer
原型:function CurrentReadBuffer: string;
用于讀取Socket數據到緩沖區,注意返回為String類型,如果直接顯示該String的數據,對于/0之后的數據可能看不到,因此要讀取所有的數據,還必須利用CurrentReadBufferSize()判斷該String的長度。
返回值:String - Contents of the Indy buffer.
3、GetResponse
原型:function GetResponse(const AAllowedResponses: Array of SmallInt): SmallInt; virtual;
對于簡單的命令應答可以使用這個方法獲取應答消息,返回值:SmallInt - The numeric response number.
4、ReadBuffer
原型:procedure ReadBuffer(var ABuffer; const AByteCount: Longint);
讀取指定數目的字節到緩沖區ABuffer,注意它會調用 ReadFromStack 以檢查緩沖區里的數據是否少于AByteCount
5、ReadInteger
原型:function ReadInteger(const AConvert: boolean): Integer;
從緩沖區中讀取整型數據,它會調用ReadBuffer
6、ReadLn
原型:function ReadLn(const ATerminator: string; const ATimeout: integer): string; virtual;
讀取移行記錄,帶有一個TimeOut屬性,以防止在讀不到新行時死循環。返回值:String - Line read from the buffer.
注意行分隔符可能是以下幾種:
#0 - Default Line Feed (#10)
LF - Line Feed (#10)
CR - Carriage Return (#13)
EOL - End-of-line (Carriage Return Line Feed)
7、ReadLnWait:
原型:function ReadLnWait: string;
很像ReadLn,但它會一直傻傻的等待
8、ReadSmallInt
原型:function ReadSmallInt(const AConvert: boolean): SmallInt;
9、ReadStream
原型:procedure ReadStream(AStream: TStream; AByteCount: LongInt; const AReadUntilDisconnect: boolean);
10、ReadString
原型:function ReadString(const ABytes: integer): string;
與CurrentReadBuffer的不同在于它讀取指定長度的字符串
IdTCPClient和IdTCPServer主要屬性
2008-10-13 12:40
IdTCPClient屬性
1 : IOHandler 如果有相應的輸入/輸出操作,那么IOHandler相對應的組件或
接口將提供一個虛擬/抽象的輸入/輸出接口給相應的網絡連接
2 : Intercept 如果有一個網絡連接正在使用,那么Intercept 提供的組件或接
口將可以攔截相應的網絡數據流中的數據
3 : BoundIP 指定使用IdTCPClient組件的計算機系統的IP地址,也就是說,其
中是空白那么什么樣的計算機都可以使用,但如果指定的IP地址為239.126.12.2,那么
就只有IP地址為239.126.12.2的計算機可以使用它.
4 : BoundPor 網絡端口的概念,指定使用IdTCPClient組件的計算機系統網絡
端口,也就是說,如果BoundPort中指定了以整數為端口的網絡端口,那么通訊時就只能
使用這個商品進行通訊
5 : Host 如果不是使用BoundIP中的地址起先通訊,那么Host就是詣將
要通訊的計算機系統的名稱或它的IP地址,可以是計算機名也可以是IP地址.
6 : Port 與BoundPort的概念是基本一樣的,只是它與HOst相配合來決
定IdTCPClient組件要與哪一個計算機系統中的什么樣的網絡通訊端口進行通訊
方法
IdTCPClient 主要使
用Write,WriteBuffer,WriteCardinal,WriteFile,WriteHeader,WriteInteger,WriteL
n,WriteRFCReply,WriteRFCStrings,WriteSmallInt,WriteStream,WriteStrings.
通過它們 IdTCPClient 可以發送非常多的類型的數據到相應的服務端,而這些都是非
常的簡單
如: Write的函數說明是:
procedure Write(Const Aout : String) ; Virtual;
它的目的很簡單,就是發送一個字符串到相應的服務端.其中Aout就是被發送的字符串.
而 WriteLn的函數說明是:
procedure WriteLn(Const Aout : String = ''); Virtual;
它的目的就是在Write的基礎上,在被發送的字符串后加入相應的回車控件符.
注意 : 雖然IdTCPClient可以直接使用,它不可以被單獨的使用,它必須與相應
的IdTCPServer組件相配合才能發揮特定的作用,當然,如果相應的網絡系統中具有這
樣的TCP服務器應用,那么IdTCPClient也可以直接與它們進行通訊.
IdTCPServer
屬性
1 : Bindings 包括了相應的服務端所允許的所有的Socket(套接字)的相關信
息,比如它們的IP地址和端口號
2 : ListenQueue 是規定在服務端失效之前最多可以允許的監聽網絡連接的純種
數目.默認情況下它的值是15,但這個值可以根據自己的需要和系統的最大承受能力來
進行改變
3 : MaxConnects 它表示的內容就是服務端可以承載的最大的網絡連接數目,如果
它的值是0,那么就表明它沒有最大的限制值,可以使用任意數目的連接.
4 : MaxConnectionReply 與MaxConnects相配合,當連接數目大
于MaxConnects的數目時,它將返回一個錯誤的信息,而這個信息是由TIdRFCReply
類所定義的
5 : ThreadMgr 就是使用相應的 ThreadMgr 組件來對于網絡連接線程進行管
理.
?
總結
以上是生活随笔為你收集整理的delphi下使用indy进件socket通信的简单实例介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【力扣题解】分式化简
- 下一篇: 记录部署过程