在TServerSokcet中调用SendBuf发送4000的数据包,在TClientSocket的ClientSocketRead事件中用Socket.ReceiveBuf接收数据包,然后用日志记录,在日志中看到发送的记录与接收的记录不一致,会有几个数据包接收不到,我用的是TCP方式,不应该会经常丢失(功能 :从一台电脑读文件到别一台电脑),请高手指教....
解决方案 »
- 怎么样实现将一个.ini 文件添加到.exe 文件的末尾?
- 紧急求助
- delphi程序可以运行,任务管理器中可以看到,但在任务栏中不显示也不生成exe.这是怎么回事啊?
- 一个自动处理消息的问题..
- 请问那位做过基于LDAP的项目,能不能讲解一下?
- delphi7安装和运行rave的问题
- 选择路径函数,有没有比SelectDirectory好些的这个函数太难看了!
- 很菜的问题:关于var。查HELP只给很少的信息所以只好来问
- 异常怎么用:delphi6中
- 如何绕过BDE/ODBC 对PARADOX操作,也就是找VCL,个头要小
- 完成了一个物业管理软件,想谈谈自己的一点体会。1.对delphi的看法 2.所谓的三层
- 超级简单新手问题,高分立刻结贴。
在调用该函数前,先通过Socket.ReceiveLength获取数据长度,然后检查ReceiveBuf函数的返回值,这个返回值表示该函数实际接收了的数据。如果返回值小于Socket.ReceiveLength获取的数据长度,则需要再次调用ReceiveBuf函数,直至所有数据全部获取。例如:
procedure TForm1.SocketRead(Sender: TObject; Socket: TCustomWinSocket);
var
iLength : Integer;
iReceived : Integer;
bBuffer : array of Byte;
begin
iLength := Socket.ReceiveLength;
while iLength > 0 do
begin
SetLength (bBuffer, iLength);
iReceived := Socket.ReceiveBuf(bBuffer[0], iLength);
//处理数据
......
iLength := iLength - iReceived;
end;
bBuffer := nil;
end;
一般服务器为threadblocking,客户端为blocking。
jadeluo(秀峰)的回答我感觉不是最佳的,因为ReceiveLength有时候并不准确,建议你最好先把文件的大小传过来,或者把每次发送的数据包大小先传过来。这样才能保证完整的收到发送的数据。
这段代码解决的问题是“ReceiveBuf并不一定能一次性接收完整的数据”,代码所给出的方法能够正确、完整地接收并处理已经到达的数据(不是通讯协议包)。
再把代码补充得更详细些:
procedure TForm1.SocketRead(Sender: TObject; Socket: TCustomWinSocket);
var
iLength : Integer;
iReceived : Integer;
bBuffer : array of Byte;
iLoop : Integer;
begin
iLength := Socket.ReceiveLength;
while iLength > 0 do
begin
SetLength (bBuffer, iLength);
iReceived := Socket.ReceiveBuf(bBuffer[0], iLength);
//处理数据(下面的代码需要按照通讯协议来对已经接收的数据进行处理)
for iLoop := 0 to iReceived - 1 do
begin
......
end;
......
iLength := iLength - iReceived;
end;
bBuffer := nil;
end;
我无意与你争论,只是想说明这个问题。如果想完整的接收到另一端发送的数据,用ReceiveLength是不可靠的
unit UniClient;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ScktComp, StdCtrls, shlobj, ActiveX;type
TForm1 = class(TForm)
Button1: TButton;
ClientSocket1: TClientSocket;
OpenDialog1: TOpenDialog;
Edit1: TEdit;
CSocket_Cmd: TClientSocket;
Memo1: TMemo;
Button2: TButton;
Memo2: TMemo;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;var
Form1: TForm1;
Path: string; //起始路径
IsClose: Boolean;
implementation{$R *.dfm}function BrowseCallbackProc(hwnd: HWND; uMsg: UINT; lParam: Cardinal; lpData: Cardinal): integer; stdcall;
begin
if uMsg = BFFM_INITIALIZED then
result := SendMessage(Hwnd, BFFM_SETSELECTION, Ord(TRUE), Longint(PChar(Path)))
else
result := 1
end;function SelDir(const Caption: string; const Root: WideString; out Directory: string): Boolean;
var
WindowList: Pointer;
BrowseInfo: TBrowseInfo;
Buffer: PChar;
RootItemIDList, ItemIDList: PItemIDList;
ShellMalloc: IMalloc;
IDesktopFolder: IShellFolder;
Eaten, Flags: LongWord;
begin
Result := False;
Directory := '';
FillChar(BrowseInfo, SizeOf(BrowseInfo), 0);
if (ShGetMalloc(ShellMalloc) = S_OK) and (ShellMalloc <> nil) then
begin
Buffer := ShellMalloc.Alloc(MAX_PATH);
try
RootItemIDList := nil;
if Root <> '' then begin
SHGetDesktopFolder(IDesktopFolder);
IDesktopFolder.ParseDisplayName(Application.Handle, nil, POleStr(Root), Eaten, RootItemIDList, Flags);
end;
with BrowseInfo do begin
hwndOwner := Application.Handle;
pidlRoot := RootItemIDList;
pszDisplayName := Buffer;
lpszTitle := PChar(Caption);
ulFlags := BIF_RETURNONLYFSDIRS;
lpfn := @BrowseCallbackProc;
lParam := BFFM_INITIALIZED;
end;
WindowList := DisableTaskWindows(0);
try
ItemIDList := ShBrowseForFolder(BrowseInfo);
finally
EnableTaskWindows(WindowList);
end;
Result := ItemIDList <> nil;
if Result then begin
ShGetPathFromIDList(ItemIDList, Buffer);
ShellMalloc.Free(ItemIDList);
Directory := Buffer;
end;
finally
ShellMalloc.Free(Buffer);
end;
end;
end;procedure Delay(dwMilliseconds: Longint);
var
iStart, iStop: DWORD;
begin
iStart := GetTickCount;
repeat
iStop := GetTickCount;
Application.ProcessMessages;
until (iStop - iStart) >= dwMilliseconds;
end;function SubStr(Str: string; FindStr: string; Index: Integer): string;
var
i, x: Integer;
begin
Result := '';
i := Pos(FindStr, Str);
x := 0;
while i > 0 do
begin
Inc(x);
if x = Index then
begin
Result := Trim(Copy(Str, 1, i - 1));
Exit;
end;
Delete(str, 1, i + length(FindStr) - 1);
i := Pos(FindStr, Str);
end;
if (x + 1 = Index) and (str <> '') then
Result := Trim(Str);end;function GetFileSize(const FileName: string): LongInt;
var
SearchRec: TSearchRec;
begin
try
if FindFirst(ExpandFileName(FileName), faAnyFile, SearchRec) = 0 then
Result := SearchRec.Size
else Result := -1;
finally
SysUtils.FindClose(SearchRec);
end;
end;procedure GetAllFile(FileList: TStrings; const Path: string; FileEx: string = '*.*');
var
f: TSearchRec;
Ret: Integer;
p: string;
begin
FileEx := UpperCase(FileEx);
Ret := FindFirst(Path + '\*.*', faAnyFile, f);
while Ret = 0 do
begin
if f.Attr = faDirectory then
begin
if (f.Name <> '.') and (f.Name <> '..') then
GetAllFile(FileList, Path + '\' + f.Name)
end
else
if (FileEx = '*.*') or (UpperCase(ExtractFileExt(f.Name)) = FileEx) then
begin
p := Path + '\' + f.Name;
FileList.Append(IntToStr(GetFileSize(p)) + '|' + p);
end;
Ret := FindNext(f)
end;
FindClose(f)
end;function ReadReceiveText(Socket: TCustomWinSocket; TimeOut: Longint = 60000): string;
var
StartTick, CurrentTick: Integer;
begin
StartTick := GetTickCount;
CurrentTick := GetTickCount;
while (CurrentTick - StartTick < TimeOut) and
(not IsClose) do
begin
Result := Socket.ReceiveText;
if Result <> '' then
break
else
begin
Delay(100);
CurrentTick := GetTickCount;
end;
end;end;procedure TForm1.Button1Click(Sender: TObject);
var
s: TWinSocketStream;
f: TFileStream;
i, x: integer;
m: TMemoryStream;
fileName, ReceiveText, str, str2: string;
FileList: TStringList;
begin
// if not OpenDialog1.Execute then Exit;
//去掉无用的路径,用于传送到接收端
FileList := TStringList.Create;
FileList.Assign(Memo1.Lines);
x := Length(IncludeTrailingBackslash(Path));
for i := 0 to FileList.Count - 1 do
begin
str := SubStr(FileList[i], '|', 1);
str2 := SubStr(FileList[i], '|', 2);
Delete(str2, 1, x);
FileList[i] := str + '|' + str2;
end;
ClientSocket1.Address := Edit1.Text;
ClientSocket1.Port := 1234;
ClientSocket1.Open;
s := TWinSocketStream.Create(ClientSocket1.Socket, 60000);
try
m := TMemoryStream.Create;
FileList.SaveToStream(m);
s.CopyFrom(m, 0);
str := #123#124'filelist_send_over'#123#124;
s.WriteBuffer(str[1], Length(str));
// sleep(2000);
ReceiveText := ReadReceiveText(ClientSocket1.Socket);
// showmessage(ReceiveText);//开始发送文件
FileList.Assign(Memo1.Lines);
i := 0;
while True do
// for i := 0 to Memo1.Lines.Count - 1 do
begin fileName := SubStr(FileList[i], '|', 2);
f := TFileStream.Create(FileName, fmShareDenyWrite);
s.CopyFrom(f, 0);
f.Free;
Memo2.Lines.Add(FileList[i]);
ReceiveText := ReadReceiveText(ClientSocket1.Socket);
inc(i);
if i >= FileList.Count then
Break;
end; finally
s.Free;
// f.Free;
ClientSocket1.Close;
end;
end;procedure TForm1.Button2Click(Sender: TObject);
var
Path1: string;
begin
Path := Edit1.Text;
if SelDir('SelectDirectory Sample', '', Path) then
begin
GetAllFile(Memo1.Lines, Path);
end;
end;procedure TForm1.FormCreate(Sender: TObject);
begin
IsClose := False;
end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
IsClose := True;
end;end.
unit Uniserver;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ScktComp;type
TClientThread = class(TServerClientThread)
private
public
procedure ClientExecute; override;
end;
type
TForm1 = class(TForm)
ServerSocket1: TServerSocket;
procedure ServerSocket1GetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var SocketThread: TServerClientThread);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;var
Form1: TForm1;implementation{$R *.dfm}{ TClientThread }function SubStr(Str: string; FindStr: string; Index: Integer): string;
var
i, x: Integer;
begin
Result := '';
i := Pos(FindStr, Str);
x := 0;
while i > 0 do
begin
Inc(x);
if x = Index then
begin
Result := Trim(Copy(Str, 1, i - 1));
Exit;
end;
Delete(str, 1, i + length(FindStr) - 1);
i := Pos(FindStr, Str);
end;
if (x + 1 = Index) and (str <> '') then
Result := Trim(Str);end;procedure TClientThread.ClientExecute;
type
TFileInf = record
Name: string;
Size: Longint;
end;
const
filelist_send_over = #123#124'filelist_send_over'#123#124;var
ReceiveBuffer: array[0..4095] of Char;
SocketStream: TWinSocketStream;
BytesRead: Integer;
F: TFileStream;
FileList: TStringList;
i: integer;
CurrentFile: TFileInf;
Path, CurrentPath, Str: string;
begin
while not Terminated and ClientSocket.Connected do
begin
try
SocketStream := TWinSocketStream.Create(ClientSocket, 60000);
FileList := TStringList.Create;// F := TFileStream.Create('c:\afile.exe', fmCreate);
try
FillChar(ReceiveBuffer, 10, 0);
if SocketStream.WaitForData(5000) then
repeat
BytesRead := SocketStream.Read(ReceiveBuffer, SizeOf(ReceiveBuffer));
if BytesRead = 0 then
begin
// ClientSocket.Close
break;
end
else
begin
FileList.Text := FileList.Text + ReceiveBuffer;
end;
until not SocketStream.WaitForData(2000); i := Pos(filelist_send_over, FileList.Text);
if i > 0 then
begin
ClientSocket.SendText('ok');
Str := FileList.Text;
Delete(Str, i, MaxInt);
FileList.Text := Str;
// break;
end; Path := 'c:\test\';
i := 0; while true do
begin
//当前文件的信息
CurrentFile.Name := Path + SubStr(FileList[i], '|', 2);
CurrentFile.Size := StrToInt64(SubStr(FileList[i], '|', 1));
//检查文件路径
CurrentPath := ExtractFilePath(CurrentFile.Name);
if not DirectoryExists(CurrentPath) then
ForceDirectories(CurrentPath); F := TFileStream.Create(CurrentFile.Name, fmCreate);
if SocketStream.WaitForData(5000) then
repeat
BytesRead := SocketStream.Read(ReceiveBuffer, SizeOf(ReceiveBuffer));
if BytesRead = 0 then
begin
ClientSocket.Close;
// break;
end
else
begin
F.WriteBuffer(ReceiveBuffer, BytesRead);
if f.Size = CurrentFile.Size then
begin
ClientSocket.SendText('ok');
f.Free;
break;
end;
end;
until not SocketStream.WaitForData(2000);
inc(i);
if i >= FileList.Count then
Break;
end; finally
SocketStream.Free;
ClientSocket.Close;
// f.Free;
end;
except
end;
end;
end;
procedure TForm1.ServerSocket1GetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket;
var SocketThread: TServerClientThread);
begin
SocketThread := TClientThread.Create(False, ClientSocket);end;procedure TForm1.FormCreate(Sender: TObject);
begin
ServerSocket1.Port := 1234;
ServerSocket1.Open;
end;end.支持多文件同线程传送!!
最好一次性发送较小的数据量,2000以下最好
不过象秀峰那样,把这个数值作为参考来分配空间读数据也行,
关键只在判断ReceiveBuf的返回值把握住...楼主丢数据失误关键就是这儿处理得不妥吧.