unit TransferThread
///////////////////////////////////////////////桐早/////////////////////////////////
// 模块说明: FTP传输核心模块类
// 功能: 指定一个下载(上传)的日期或文件名,系统执行传输功能(支持续传)
// 备注:该模块属于传输类的一个子线程模块.
////////////////////////////////////////////////////////////////////////////////
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs,ComCtrls,StdCtrls,IniFiles,IdIntercept, IdLogBase, IdLogEvent, IdAntiFreezeBase,
IdAntiFreeze, IdFTPList,IdBaseComponent,IdGlobal,IdComponent, IdTCPConnection, IdTCPClient,IdFTPCommon,
IdFTP
type
TTransferThread = class(TObject)
private
{ Private declarations }
//进度显示
FProgressbar:TProgressbar
//局仿雀上传核心组件
FFTP:TIdFTP
//上传列表内部类
FCombobox:TCombobox
//上传信息显示
FLabel:TLabel
//FTP地址
FFTP_STR_HOST:String
//FTP用户名
FFTP_STR_USN:String
//FTP用户密码
FFTP_STR_PWD:String
//FTP端口
FFTP_STR_PORT:String
//FTP上传标记
FFTP_STR_UTAG:String
//FTP下载标记
FFTP_STR_DTAG:String
//FTP指定的文件夹
FFTP_STR_FLODER:STring
//传输文件大小
FFTP_LWD_BYTES:LongWord
//传输开始时间
FFTP_DT_BEGINTIME:TDateTime
//传输速度
FFTP_DUB_SPEED:Double
//是否删除源文件.
FFTP_BOL_DEL:Boolean
//是否正在传输文件
FFTP_BOL_ISTRANSFERRING:Boolean
//类内部通用对话框函数
function MsgBox(Msg:stringiValue:integer):integer
//获取用户当前的Windows临时文件夹
function GetWinTempPath:String
//根据日期生成的日期文件名
function DateToFileName(DateTime:TDateTime):String
//大纯根据上传/下载标记生成完整的文件名
function GetFileFullName(sTag:StringDateTime:TDateTime):String
protected
//传输核心函数
function TransferKernel(iTag:IntegersFile:stringbDelSFile:boolean=False):boolean
//传输组件的WorkBegin事件
procedure FFTPOnWorkBegin(Sender: TObjectAWorkMode: TWorkModeconst AWorkCountMax: Integer)
//传输组件的WorkEnd事件
procedure FFTPOnWorkEnd(Sender: TObjectAWorkMode: TWorkMode)
//传输组件的Work事件
procedure FFTPOnWork(Sender: TObjectAWorkMode: TWorkModeconst AWorkCount: Integer)
public
//构造函数
constructor Create
//析构函数
destructor Destroy
//进度条控件属性
property Progressbar:TProgressbar read FProgressbar write FProgressbar default nil
//列表控件属性
property Combobox:TCombobox read FCombobox write FCombobox default nil
//只读的FTP核心组件
property FTP:TidFTP read FFTP
//标签控件
property oLabel:TLabel read FLabel write FLabel default nil
//列表方法(该方法需要指定Combobox,否则无效)
procedure List
//依据日期下载文件
procedure DownLoad(dDate:TDateTime)overload
//依据文件名下载文件
procedure DownLoad(sFileName:String)overload
//依据日期上传文件
procedure UpLoad(dDate:TDateTime)overload
//依据文件名上传文件
procedure UpLoad(sFileName:String)overload
// procedure Executeoverride
end
implementation
constructor TTransferThread.Create
var
FFini:TIniFile
FFilePath:String
begin
//完成FTP相关参数的读取.
FFTP_BOL_ISTRANSFERRING:=False
Try
FFilePath:=ExtractFilePath(APPlication.exeName)+'setup.ini'
FFini:=TIniFile.Create(FFilePath)
FFTP_STR_HOST:=FFini.ReadString('文件传输','服务器地址','')
FFTP_STR_PORT:=FFini.ReadString('文件传输','服务器端口','')
FFTP_STR_USN:=FFini.ReadString('文件传输','用户名','')
FFTP_STR_PWD:=FFini.ReadString('文件传输','密码','')
FFTP_STR_FLODER:=FFini.ReadString('文件传输','文件夹','')
FFTP_STR_UTAG:=FFini.ReadString('文件传输','上传标识码','')
FFTP_STR_DTAG:=FFini.ReadString('文件传输','上传标识码','')
FFTP_BOL_DEL:=FFini.ReadBool('文件传输','删源文件',FALSE)
FFIni.Free
Except
MsgBox('读取FTP连接配置信息失败!请检查您的Setup.ini文件.',MB_OK+MB_ICONERROR)
Exit
Abort
End
//设置FTP相关参数
Try
FFTP:=TIdFTP.Create(nil)
FFTP.Host:=FFTP_STR_HOST
FFTP.Port:=strtoint(FFTP_STR_PORT)
FFTP.UserName:=FFTP_STR_USN
FFTP.Password:=FFTP_STR_PWD
FFTP.TransferType:=ftASCII
//事件驱动
FFTP.OnWork:=FFTPOnWork
FFTP.OnWorkBegin:=FFTPOnWorkBegin
FFTP.OnWorkEnd:=FFTPOnWorkEnd
FFTP.Connect(True,-1)
Except
MsgBox('连接远程FTP服务器失败!'#10#13'1.服务器地址错误,或服务器不可用.'#10#13'2.用户名或密码不正确.'#10#13'3.FTP服务端口设置不正确.',MB_OK+MB_ICONERROR)
Exit
Abort
End
end
function TTransferThread.DateToFileName(DateTime: TDateTime): String
var
Year, Month, Day:Word
sYear,sMonth,sDay:String
begin
DecodeDate(DateTime, Year, Month, Day)//日期
sYear:=inttostr(Year)
sMonth:=inttostr(Month)
sDay:=inttostr(Day)
//年
case Length(sYear) of
4: sYear:=sYear
3: sYear:='0'+sYear
2: sYear:='00'+sYear
1: sYear:='000'+sYear
else
sYear:=''
end
//月
case Length(sMonth) of
2: sMonth:=sMonth
1: sMonth:='0'+sMonth
else
sMonth:=''
end
//日
case Length(sDay) of
2: sDay:=sDay
1: sDay:='0'+sDay
else
sDay:=''
end
if (sYear='') or (sMonth='') or (sDay='') then
begin
Result:=''
Exit
end
if (sYear<>'') and (sMonth<>'') and (sDay<>'') then
begin
Result:=sYear+sMOnth+sDay
end
end
destructor TTransferThread.Destroy
begin
FProgressbar:=nil
FCombobox:=nil
FLabel:=nil
FFTP.Quit
FFTP.Free
end
procedure TTransferThread.DownLoad(dDate: TDateTime)
begin
if Not FFTP_BOL_ISTRANSFERRING then
begin
TransferKernel(1,GetFileFullName(FFTP_STR_DTAG,dDate),FFTP_BOL_DEL)
end
end
procedure TTransferThread.DownLoad(sFileName: String)
begin
if Not FFTP_BOL_ISTRANSFERRING then
TransferKernel(1,sFileName,FFTP_BOL_DEL)
end
procedure TTransferThread.FFTPOnWork(Sender: TObjectAWorkMode: TWorkMode
const AWorkCount: Integer)
var
S,E: String
H, M, Sec, MS: Word
TotalTime: TDateTime
DLTime: Double
begin
TotalTime := Now - FFTP_DT_BEGINTIME//总用时
DecodeTime(TotalTime, H, M, Sec, MS)//取出时\分\秒\毫秒
Sec := Sec + M * 60 + H * 3600//转换成秒
DLTime := Sec + MS / 1000//最终的下载时间
E:= Format(' 使用时间:%2d:%2d:%2d', [Sec div 3600, (Sec div 60) mod 60, Sec mod 60])
if DLTime >0 then
//每秒的平均速度:XX K/s
FFTP_DUB_SPEED := {(AverageSpeed + }(AWorkCount / 1024) / DLTime{) / 2}
if FFTP_DUB_SPEED >0 then
begin
Sec := Trunc(((FFTP_LWD_BYTES - AWorkCount) / 1024) / FFTP_DUB_SPEED)
S := Format(' 剩余时间:%2d:%2d:%2d', [Sec div 3600, (Sec div 60) mod 60, Sec mod 60])
S:='速度: ' + FormatFloat('0.00 KB/秒',FFTP_DUB_SPEED) + S + E
end
else
S:=''
if (FLabel<>nil) and (assigned(FLabel)) then
begin
FLabel.AutoSize:=True
FLabel.Caption:=S
FLabel.Update
end
if (FProgressBar<>nil) and (assigned(FProgressBar)) then
begin
FProgressBar.Position:=AWorkCount//进度显示
FProgressBar.Update
end
end
procedure TTransferThread.FFTPOnWorkBegin(Sender: TObject
AWorkMode: TWorkModeconst AWorkCountMax: Integer)
begin
FFTP_BOL_ISTRANSFERRING:=True
FFTP_DT_BEGINTIME:=Now//开始时间
FFTP_DUB_SPEED:=0.0//初始化速率
if (FProgressBar<>nil) and (assigned(FProgressBar)) then
begin
if AWorkCountMax>0 then
begin
FProgressBar.Max:=AWorkCountMax
FFTP_LWD_BYTES:=FProgressBar.Max
end
else
FProgressBar.Max:=FFTP_LWD_BYTES
end
end
procedure TTransferThread.FFTPOnWorkEnd(Sender: TObject
AWorkMode: TWorkMode)
begin
FFTP_BOL_ISTRANSFERRING:=False
FFTP_DUB_SPEED:=0.00
if (FLabel<>nil) and (assigned(FLabel)) then
begin
FLabel.AutoSize:=True
FLabel.Caption:=''
FLabel.Update
end
if (FProgressBar<>nil) and (assigned(FProgressBar)) then
begin
FProgressBar.Position:=0
end
end
function TTransferThread.GetFileFullName(sTag:StringDateTime:TDateTime):String
begin
Result:=sTag+DateToFileName(DateTime)+'FD.HXD'
end
function TTransferThread.GetWinTempPath: String
var
TempDir:array [0..255] of char
begin
GetTempPath(255,@TempDir)
Result:=strPas(TempDir)
end
procedure TTransferThread.List
var
Dir_List:TStringList
FoundFolder:Boolean
iCount:Integer
begin
if (FCombobox=nil) or (Not Assigned(FCombobox)) then
begin
Exit
Abort
end
Dir_List:=TStringList.Create//创建字符串列表类
Try
if Not FFTP.Connected then FFTP.Connect
FFTP.ChangeDir('/')//根目录 //到服务器的根目录
FFTP.List(Dir_List,'',True)//获取目录列表
FoundFolder:=False
FFTP.TransferType:=ftASCII//更改传输类型(ASCII类型)
for iCount:=0 to Dir_List.Count-1 do
begin
if FFTP.DirectoryListing.Items[iCount].ItemType=ditDirectory then
begin
if Dir_List.IndexOf(FFTP_STR_FLODER)= -1 then //判断该文件夹不存在
begin
//如果不存继续循环查找.
Continue
end
else
begin
//如果存在,则直接退出循环
FoundFolder:=True
Break
end
end
end
if FoundFolder then //判断该文件夹不存在
begin
FFTP.MakeDir(FFTP_STR_FLODER)//不存在,则创建一个新的文件夹
end
FFTP.ChangeDir(FFTP_STR_FLODER)
FFTP.List(Dir_List,'*.HXD',False)
if Dir_List.Count>0 then
begin
FCombobox.Items:=Dir_List
end
Finally
Dir_List.Free
End
end
function TTransferThread.MsgBox(Msg: stringiValue: integer): integer
begin
Result:=MessageBox(application.Handle,pChar(Msg),'系统信息',iValue+MB_APPLMODAL)
end
function TTransferThread.TransferKernel(iTag: IntegersFile: string
bDelSFile: boolean): boolean
var
sTmpPath:String
Dir_List:TStringList
FoundFolder:Boolean
iCount:Integer
begin
sTmpPath:=GetWinTempPath//获取本地系统临时目录
Dir_List:=TStringList.Create//创建字符串列表类
Try
if Not FFTP.Connected then FFTP.Connect
FFTP.ChangeDir('/')//根目录 //到服务器的根目录
FFTP.TransferType:=ftASCII//更改传输类型(ASCII类型)
FFTP.List(Dir_List,'',True)//获取目录列表
FoundFolder:=False
for iCount:=0 to Dir_List.Count-1 do
begin
if FFTP.DirectoryListing.Items[iCount].ItemType=ditDirectory then //是目录
begin
if Dir_List.IndexOf(FFTP_STR_FLODER)= -1 then //判断该文件夹不存在
begin
//如果不存继续循环查找.
Continue
end
else
begin
//如果存在,则直接退出循环
FoundFolder:=True
Break
end
end
end
if FoundFolder then //判断该文件夹不存在
begin
FFTP.MakeDir(FFTP_STR_FLODER)//不存在,则创建一个新的文件夹
end
//更改传输类型
FFTP.TransferType:=ftBinary
Try
//找到相应的目录,则更换路径.
FFTP.ChangeDir(FFTP_STR_FLODER)
//0为上传
if iTag=0 then
begin
Try
FFTP.Put(sTmpPath+sFile,sFile)
Except
MsgBox('上传文件失败!原因如下:'#13#10'1.服务器没有开启写文件的权限!'#10#13'2.程序发生异常,请重新上传!',MB_OK+MB_ICONERROR)
Abort
End
FFTP_LWD_BYTES:=FFTP.Size(sFile)
if bDelSFile then //删除本地源文件
begin
DeleteFile(sTmpPath+sFile)
end
Result:=True
FFTP.Disconnect
end
//1为下载
if iTag=1 then
begin
//文件已经存在
Try
FFTP_LWD_BYTES:=FFTP.Size(sFile)
if FileExists(sTmpPath+sFile) then
begin
case MsgBox('文件已经存在,要续传吗?'#13#10'是--续传'#10#13'否--覆盖'#13#10'取消--取消 *** 作',MB_YESNOCANCEL+MB_ICONINFORMATION) of
IDYES: begin
FFTP_LWD_BYTES:=FFTP_LWD_BYTES-FileSizeByName(sTmpPath+sFile)
//参数说明: 源文件,目标文件,是否覆盖,是否触发异常(True为不触发)。
FFTP.Get(sFile,sTmpPath+sFile,False,True)
end
IDNO: begin
FFTP.Get(sFile,sTmpPath+sFile,True)
end
IDCANCEL:
begin
FFTP_BOL_ISTRANSFERRING:=False
end
end
end
else //文件不存在
begin
FFTP.Get(sFile,sTmpPath+sFile,True)
end
Except
MsgBox('上传文件失败!原因如下:'#13#10'1.服务器没有开启写文件的权限!'#10#13'2.程序发生异常,请重新上传!',MB_OK+MB_ICONERROR)
Abort
End
if bDelSFile then //删除远程源文件
begin
FFTP.Delete(sFile)
end
FFTP.Disconnect
end
Except
FFTP.Quit
Result:=False
End
Finally
Dir_List.Free
End
end
procedure TTransferThread.UpLoad(dDate: TDateTime)
begin
if Not FFTP_BOL_ISTRANSFERRING then
TransferKernel(0,GetFileFullName(FFTP_STR_DTAG,dDate),FFTP_BOL_DEL)
end
procedure TTransferThread.UpLoad(sFileName: String)
begin
if Not FFTP_BOL_ISTRANSFERRING then
TransferKernel(0,sFileName,FFTP_BOL_DEL)
end
end.
你的服务端返回数据时先发送数据包的长度,然后再发送数据包。这样你在接收时先调用ReadInteger得到将要接收的数据包的长度,然后再调用ReadString接收数据。含雹我不确定ReadString的用法族告,如果不行的话,兆老明改为ReadStream方法,但服务端也要改为发送1.IdTcpServer中有Connected和DisConnected事件,易于进行悄庆销管理,而且当Client非正常关闭也可以用.2.阻塞方式的通信方式虽然"笨"点,但当一个"笨"办法有效,那它就不是一个笨办法.
3.由于是Tcp方式的连接,可靠性高了很多,而且使得内网连接也可靠了许多.
一.关于组件的一般无错处理和应用
虽然IdTcpClient没有Execute事件,但可以用线程的办法解决,这个网上很多
TClientHandleThread = class(TThread)
private
servercmd:integer
procedure HandleInput
protected
procedure Executeoverride
end
再在主窗口定义一全局变量
ClientHandleThread: TClientHandleThread
两个过程:
procedure TClientHandleThread.Execute
begin
while not Terminated do
begin
if not fmclient.cTcpC.Connected then
Terminate
else
try
servercmd:=fmclient.cTcpC.Socket.ReadInteger
Synchronize(HandleInput)
except
end
end
end
procedure TClientHandleThread.HandleInput
begin
case servercmd of
....
else
end
end
在Client连接时创建线程
if ClientHandleThread<>nil then ClientHandleThread.Terminate
try
ctcpc.Connect
if ctcpc.Connected then begin
ClientHandleThread := TClientHandleThread.Create(True)
ClientHandleThread.FreeOnTerminate:=True
ClientHandleThread.Resume
end
except
ctcpc.Disconnect
end
在Client的DisConnected事件中释放线程
ClientHandleThread.Terminate
IdTcpServer在还有Client连接时要断开可不是件容易事,网上对它也作启游了些讨论,但没什么结果,在本人的使用过程中,可以用以下方法解决
1.当然是释放所有Connection
一般在Server中都会用一个ListView来登记登录进来的Client,我是这样来释放的
i:=clist.Items.Count-1
while i>=0 do begin
TIdContext(clist.Items[i].Data).Connection.Disconnect
dec(i)
end
try
ctcps.Contexts.UnlockList
ctcps.Active:=false
except
end
2.后续无错处理
这样还没有完,因为一定会在DisConnected事件中有若干代码,而关闭不成功的主要原因我猜测不是来自于IdTcpServer本身,而是这些后续代码引起的.所以这步很有必要.
例如:
在Form的Close事件中让IdTcpServer.Tag=-1
在DisConnected中
If IdTcpServer.Tag<0 then exit
1.Client对Server的数据传输
这个简单的很,基本上可以用一问一答的方式来作比方(阻塞差辩方式的优点啊).
Client用Socket来喊话,它的Write方法很有意思,可以Write多种类型的数据,当然,都是定长或可以判断出长度的类型,而服务器的Execute事件就能用AContext.Connection.IOHandler的各种Read方法来读出相应的内容.
由于是阻塞状态,所以所有的 *** 作都可以在一次Server的Execute事件中完成,哪怕是传输大文件.而在这个Execute的对话中,不会触发Client的监视线程!这个尤其重要.
2.Server对Client的数据传输
与其说是数据传输,事实上不如说是指令传输.这是在服务器主动的方式下进行的一次性指令传输.注意,与Client对Server交流不同,服务器的所有内容必须在这一次传输中进行完毕!也就是说,这次传输必须是:指令+数据大小+数据内容。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)