源码
library sdata;uses
windows,
SysUtils,
Classes,
uClient in 'uClient.pas' {FClient};exports
ClientSendFile name 'ClientSendFile',//dll入口
ClientLoadFile name 'ClientLoadFile',
begin
end.
***********************************************************
uClient.pas如下unit uClient;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, ComCtrls, StdCtrls, ExtCtrls, Registry;type
TFClient = class(TForm)
ClientSocket1: TClientSocket;{ 不用在设计时初始化相关属性,在程序中设置 }
ProgressBar1: TProgressBar;{ 用于显示数据传输进度 }
ButtonCancel: TButton;{ 取消数据传输 }
Label1: TLabel;{ 用于动态显示传输数据完成的百分比 }
procedure ButtonCancelClick(Sender: TObject);{ 事件:按“取消”按钮 }
procedure ClientSocket1Connect(Sender: TObject;{ 事件:建立连接 }
Socket: TCustomWinSocket);
procedure ClientSocket1Connecting(Sender: TObject;{ 事件:正在连接 }
Socket: TCustomWinSocket);
procedure ClientSocket1Disconnect(Sender: TObject;{ 事件:断开连接 }
Socket: TCustomWinSocket);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);{ 事件:读取数据}
procedure FormDestroy(Sender: TObject);{ 事件:销毁窗体 }
private
{ Private declarations }
procedure DoFirstVerb(AVerb: Integer); { 私有方法:发出第一个命令(动词) }
public
{ Public declarations }
end; function ClientSendFile(LocalSrcFileName: string;
IP: string = '127.0.0.1'): Boolean;export;implementationuses
uSocketCommon;{ 引用公共的单元,该单元中包括Socket的一些例程 }var
FPort: Integer = ServerSocketPort;{ 用于存取端口号,等号右边的常量在单元uSocketCommon 中有声明 }
FirstVerb: Integer; { 用于存取数据传输任务的第一个动词命令 }
NeedFree: Boolean = False; { 用于存取是否需要将进度窗体销毁 }{$R *.dfm}function ClientSendFile(LocalSrcFileName: string;
IP: string = '127.0.0.1'): Boolean;export;{ 向服务器端发送 }
var
P: PSocketData; { 存取Socket的相关信息,类型指针PSocketData在单元uSocketCommon中声明 }
begin
with TFClient.Create(Application) do try{ 即用即创建 }
NeedFree := False;{ 初始值,不用程序自动销毁 }
ClientSocket1.Address := IP;{ 设置服务器端的IP地址 }
ClientSocket1.Port := FPort;{ 设置端口号 }
P := NewSocketData; { 新建类型指针,函数NewSocketData在公共单元中 }
P^.ProgressBar := ProgressBar1;{ 为P指定进度条 }
P^.ALabel := Label1; { 为P指定标签 }
ClientSocket1.Socket.Data := P;{ 指定当前Socket的相关信息 }
try
ClientSocket1.Active := True;{ 试图建立连接 }
except
ShowError(Application.Handle, sConnectError);{ 连接失败,报告信息 }
NeedFree := True;{ 需要程序自动销毁进度窗体 }
end;
if not NeedFree then begin{ 连接成功则保存发送文件的相关信息 }
P := ClientSocket1.Socket.Data;
P^.SrcFileName := LocalSrcFileName;
P^.DstFileName := ExtractFileName(LocalSrcFileName);
FirstVerb := vcSave;{ 设置第一个动词是vcSave,动词声明在公共单元中 }
Result := ShowModal = mrOK;{ 显示数据传输进度窗体 }
end else begin{ 连接失败 }
Result := False;
end;
finally
Free;{ 用完即销毁 }
end;
end;procedure TFClient.ButtonCancelClick(Sender: TObject);{ 取消传输 }
var
SendBuf: TSendBuf;
P: PSocketData;
AVerb: TSocketVerb;
L: Integer;
begin
P := ClientSocket1.Socket.Data;{ 获得当前数据 }
if (P <> nil) and P^.OnLine then begin { 根据合适的状态进行取消的动作 }
AVerb := vcCancel;{ 动词是客户端请求取消 }
MakeVerbBuf(AVerb, SendBuf, L);{根据动词生成发送的数据}
ClientSocket1.Socket.SendBuf(SendBuf, LeadLen);{发送请求取消数据包}
end else begin { 否则直接销毁窗口 }
ModalResult := mrCancel;
end;
end;
procedure TFClient.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket); {连接成功时即刻发出第一个动词 }
var
P: PSocketData;
begin
Label1.Caption := ''; { 标签设计时的文本是“连接失败,请[取消]” }
Label1.Font.Color := clBlack;{ 设计时字体颜色是红色,连接成功后置为黑色 }
Caption := sConnectedOK; { 连接成功 }
P := ClientSocket1.Socket.Data; { 获得数据地址 }
if P <> nil then begin
P^.OnLine := True; { 置连接状态为True }
DoFirstVerb(FirstVerb);{ 发出第一个动词 }
if FirstVerb = vcSave then{ 根据第一个动词确定窗体的标题 }
Caption := sSaving
else
Caption := sLoading;
end;
end;
procedure TFClient.ClientSocket1Connecting(Sender: TObject;
Socket: TCustomWinSocket);{ 正在连接时的处理 }
begin
Caption := sConnecting;{ 显示正在连接的信息 }
end;procedure TFClient.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);{ 断开连接时的处理 }
var
P: PSocketData;
begin
Caption := sDisconnectedOK;{ 显示相关信息在标题 }
P := ClientSocket1.Socket.Data;{ 获取数据 }
ResetSocketData(P);{ 释放有关的资源,如关闭文件、流等 }
if P <> nil then P^.OnLine := False;{ 置连接状态为False }
end;{ 下面对事件OnRead的处理是客户端应用程序的关键所在,该事件与服务器程序中的相应
事件互相接收和发送,并且严格遵循“你一句我一句”的原则,这样的循环控制才使得
数据传输能够快速有序地进行。应答序列的开始是客户端应用程序在ClientSocket连接
成功时发出的第一个动词引起的。 }
procedure TFClient.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
LeadBuf: TLeadBuf;{具有固定长度的引导包,引导包的作用是正确识别合法用户}
DataBuf: TDataBuf;{数据包}
GetBuf, SendBuf: TSendBuf;{接收包和发送包}
Len, SendSize, Verb: Integer;
P: PSocketData;
begin
Len := ClientSocket1.Socket.ReceiveBuf(GetBuf, SendLen); { 将接收到的包读到
GetBuf中,并将实际读取的字节数存入Len中,这里的参数SendLen
用于指定最多读取的字节数,在公共单元中声明 }
Verb := vsNone;{初始得到的动词值}
if Len >= LeadLen then begin{如果实际字节数大于等于引导包的字节数,则初步认定
可以对该数据包进行分析}
ExtractBuf(GetBuf, Len, LeadBuf, DataBuf);{将得到的包分离出引导包和数据包}
Verb := ExtractVerb(LeadBuf);{根据引导包分析出包的动词}
ClientEchoForVerb(Verb, DataBuf, Len - LeadLen,
SendBuf, SendSize, ClientSocket1);{对服务器端动词的响应,
最后生成一个响应的包,即应答包}
case Verb of
vsEchoCancel: ModalResult := mrCancel;{如果动词是响应取消,则释放窗体}
vsFail,
vsEchoFail,
vsNone: ModalResult := mrAbort;{如果动词非法,则释放窗体}
vsSaveOK,
vsLoadOK: ModalResult := mrOK;{如果动词是成功结束,则释放窗体}
else
end;
end else begin{如果得到的包的字节数小于引导包指定的字节数,则认为是非法包}
SendSize := 0;{不需要响应}
end;
if SendSize > 0 then
ClientSocket1.Socket.SendBuf(SendBuf, SendSize);{发送应答包}
if IsTerminateVerb(Verb) then begin{如果是需要终止的动词,那么重置相关数据}
P := Socket.Data;
ResetSocketData(P);
end;
end;
procedure TFClient.FormDestroy(Sender: TObject);{ 销毁窗体时释放资源 }
var
P: PSocketData;
begin
P := ClientSocket1.Socket.Data;
ClientSocket1.Socket.Data := nil;
Dispose(P);
end;procedure TFClient.DoFirstVerb(AVerb: Integer);{私有方法}
var
SendBuf: TSendBuf;
L: Integer;
begin
MakeVerbBuf(AVerb, SendBuf, L);{生成第一个动词的数据包}
ClientSocket1.Socket.SendBuf(SendBuf, LeadLen);{发送该数据包}
end;
end.
library sdata;uses
windows,
SysUtils,
Classes,
uClient in 'uClient.pas' {FClient};exports
ClientSendFile name 'ClientSendFile',//dll入口
ClientLoadFile name 'ClientLoadFile',
begin
end.
***********************************************************
uClient.pas如下unit uClient;interfaceuses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, ComCtrls, StdCtrls, ExtCtrls, Registry;type
TFClient = class(TForm)
ClientSocket1: TClientSocket;{ 不用在设计时初始化相关属性,在程序中设置 }
ProgressBar1: TProgressBar;{ 用于显示数据传输进度 }
ButtonCancel: TButton;{ 取消数据传输 }
Label1: TLabel;{ 用于动态显示传输数据完成的百分比 }
procedure ButtonCancelClick(Sender: TObject);{ 事件:按“取消”按钮 }
procedure ClientSocket1Connect(Sender: TObject;{ 事件:建立连接 }
Socket: TCustomWinSocket);
procedure ClientSocket1Connecting(Sender: TObject;{ 事件:正在连接 }
Socket: TCustomWinSocket);
procedure ClientSocket1Disconnect(Sender: TObject;{ 事件:断开连接 }
Socket: TCustomWinSocket);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);{ 事件:读取数据}
procedure FormDestroy(Sender: TObject);{ 事件:销毁窗体 }
private
{ Private declarations }
procedure DoFirstVerb(AVerb: Integer); { 私有方法:发出第一个命令(动词) }
public
{ Public declarations }
end; function ClientSendFile(LocalSrcFileName: string;
IP: string = '127.0.0.1'): Boolean;export;implementationuses
uSocketCommon;{ 引用公共的单元,该单元中包括Socket的一些例程 }var
FPort: Integer = ServerSocketPort;{ 用于存取端口号,等号右边的常量在单元uSocketCommon 中有声明 }
FirstVerb: Integer; { 用于存取数据传输任务的第一个动词命令 }
NeedFree: Boolean = False; { 用于存取是否需要将进度窗体销毁 }{$R *.dfm}function ClientSendFile(LocalSrcFileName: string;
IP: string = '127.0.0.1'): Boolean;export;{ 向服务器端发送 }
var
P: PSocketData; { 存取Socket的相关信息,类型指针PSocketData在单元uSocketCommon中声明 }
begin
with TFClient.Create(Application) do try{ 即用即创建 }
NeedFree := False;{ 初始值,不用程序自动销毁 }
ClientSocket1.Address := IP;{ 设置服务器端的IP地址 }
ClientSocket1.Port := FPort;{ 设置端口号 }
P := NewSocketData; { 新建类型指针,函数NewSocketData在公共单元中 }
P^.ProgressBar := ProgressBar1;{ 为P指定进度条 }
P^.ALabel := Label1; { 为P指定标签 }
ClientSocket1.Socket.Data := P;{ 指定当前Socket的相关信息 }
try
ClientSocket1.Active := True;{ 试图建立连接 }
except
ShowError(Application.Handle, sConnectError);{ 连接失败,报告信息 }
NeedFree := True;{ 需要程序自动销毁进度窗体 }
end;
if not NeedFree then begin{ 连接成功则保存发送文件的相关信息 }
P := ClientSocket1.Socket.Data;
P^.SrcFileName := LocalSrcFileName;
P^.DstFileName := ExtractFileName(LocalSrcFileName);
FirstVerb := vcSave;{ 设置第一个动词是vcSave,动词声明在公共单元中 }
Result := ShowModal = mrOK;{ 显示数据传输进度窗体 }
end else begin{ 连接失败 }
Result := False;
end;
finally
Free;{ 用完即销毁 }
end;
end;procedure TFClient.ButtonCancelClick(Sender: TObject);{ 取消传输 }
var
SendBuf: TSendBuf;
P: PSocketData;
AVerb: TSocketVerb;
L: Integer;
begin
P := ClientSocket1.Socket.Data;{ 获得当前数据 }
if (P <> nil) and P^.OnLine then begin { 根据合适的状态进行取消的动作 }
AVerb := vcCancel;{ 动词是客户端请求取消 }
MakeVerbBuf(AVerb, SendBuf, L);{根据动词生成发送的数据}
ClientSocket1.Socket.SendBuf(SendBuf, LeadLen);{发送请求取消数据包}
end else begin { 否则直接销毁窗口 }
ModalResult := mrCancel;
end;
end;
procedure TFClient.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket); {连接成功时即刻发出第一个动词 }
var
P: PSocketData;
begin
Label1.Caption := ''; { 标签设计时的文本是“连接失败,请[取消]” }
Label1.Font.Color := clBlack;{ 设计时字体颜色是红色,连接成功后置为黑色 }
Caption := sConnectedOK; { 连接成功 }
P := ClientSocket1.Socket.Data; { 获得数据地址 }
if P <> nil then begin
P^.OnLine := True; { 置连接状态为True }
DoFirstVerb(FirstVerb);{ 发出第一个动词 }
if FirstVerb = vcSave then{ 根据第一个动词确定窗体的标题 }
Caption := sSaving
else
Caption := sLoading;
end;
end;
procedure TFClient.ClientSocket1Connecting(Sender: TObject;
Socket: TCustomWinSocket);{ 正在连接时的处理 }
begin
Caption := sConnecting;{ 显示正在连接的信息 }
end;procedure TFClient.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);{ 断开连接时的处理 }
var
P: PSocketData;
begin
Caption := sDisconnectedOK;{ 显示相关信息在标题 }
P := ClientSocket1.Socket.Data;{ 获取数据 }
ResetSocketData(P);{ 释放有关的资源,如关闭文件、流等 }
if P <> nil then P^.OnLine := False;{ 置连接状态为False }
end;{ 下面对事件OnRead的处理是客户端应用程序的关键所在,该事件与服务器程序中的相应
事件互相接收和发送,并且严格遵循“你一句我一句”的原则,这样的循环控制才使得
数据传输能够快速有序地进行。应答序列的开始是客户端应用程序在ClientSocket连接
成功时发出的第一个动词引起的。 }
procedure TFClient.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
LeadBuf: TLeadBuf;{具有固定长度的引导包,引导包的作用是正确识别合法用户}
DataBuf: TDataBuf;{数据包}
GetBuf, SendBuf: TSendBuf;{接收包和发送包}
Len, SendSize, Verb: Integer;
P: PSocketData;
begin
Len := ClientSocket1.Socket.ReceiveBuf(GetBuf, SendLen); { 将接收到的包读到
GetBuf中,并将实际读取的字节数存入Len中,这里的参数SendLen
用于指定最多读取的字节数,在公共单元中声明 }
Verb := vsNone;{初始得到的动词值}
if Len >= LeadLen then begin{如果实际字节数大于等于引导包的字节数,则初步认定
可以对该数据包进行分析}
ExtractBuf(GetBuf, Len, LeadBuf, DataBuf);{将得到的包分离出引导包和数据包}
Verb := ExtractVerb(LeadBuf);{根据引导包分析出包的动词}
ClientEchoForVerb(Verb, DataBuf, Len - LeadLen,
SendBuf, SendSize, ClientSocket1);{对服务器端动词的响应,
最后生成一个响应的包,即应答包}
case Verb of
vsEchoCancel: ModalResult := mrCancel;{如果动词是响应取消,则释放窗体}
vsFail,
vsEchoFail,
vsNone: ModalResult := mrAbort;{如果动词非法,则释放窗体}
vsSaveOK,
vsLoadOK: ModalResult := mrOK;{如果动词是成功结束,则释放窗体}
else
end;
end else begin{如果得到的包的字节数小于引导包指定的字节数,则认为是非法包}
SendSize := 0;{不需要响应}
end;
if SendSize > 0 then
ClientSocket1.Socket.SendBuf(SendBuf, SendSize);{发送应答包}
if IsTerminateVerb(Verb) then begin{如果是需要终止的动词,那么重置相关数据}
P := Socket.Data;
ResetSocketData(P);
end;
end;
procedure TFClient.FormDestroy(Sender: TObject);{ 销毁窗体时释放资源 }
var
P: PSocketData;
begin
P := ClientSocket1.Socket.Data;
ClientSocket1.Socket.Data := nil;
Dispose(P);
end;procedure TFClient.DoFirstVerb(AVerb: Integer);{私有方法}
var
SendBuf: TSendBuf;
L: Integer;
begin
MakeVerbBuf(AVerb, SendBuf, L);{生成第一个动词的数据包}
ClientSocket1.Socket.SendBuf(SendBuf, LeadLen);{发送该数据包}
end;
end.
函数采用Stdcall传递参数
cdecl:通常是C、C++所使用的参数传递方式,由右到左,而且当被调用的函数结束之后,将会调用函数本身来清除堆栈上的参数数据
Stdcall:通常是C、C++所使用的参数传递方式,由,右到左,而且当被调用的函数结束之后,将会由被调用函数来清除堆栈上的参数数据,Win32 API 所有的输出函数都采用此传递方式。