我在socket传输文件为什么会在最后出现丢包.改变代码却重复些垃圾数据.(100分)

  • 主题发起人 主题发起人 jimtan
  • 开始时间 开始时间
J

jimtan

Unregistered / Unconfirmed
GUEST, unregistred user!
才用的方法是服务器向客户端送数据
服务器的核心代码:
try
GetMem(bufRecv,1048); //2000 must >iBYTESEND
iRecvLength := Socket.ReceiveBuf(bufRecv^,1048);
fsRecv.Seek(0,2);
fsRecv.Position;
fsRecv.Write(bufRecv^,iRecvLength);
fsRecv.Position;
finally
FreeMem(bufRecv,1048);
end; //of try

客户端的核心代码:
begin
try
//普通的发送
begin
if (fsSend.Position + iBYTEPERSEND < fsSend.Size) then
begin
GetMem(bufSend,iBYTEPERSEND);
fsSend.Read(bufSend^,iBYTEPERSEND);
Socket.SendBuf(bufSend^,iBYTEPERSEND);
fsSend.Position;
end
else
begin
GetMem(bufSend,fsSend.Size); //这最后一段文件执行出现了重复的垃圾数据
fsSend.Read(bufSend^,fsSend.Size);
Socket.SendBuf(bufSend^,fsSend.Size);
end;
end;
finally
FreeMem(bufSend,iBYTEPERSEND + 1);
end;

如果把这最后一段文件执行出现了重复的垃圾数据的那段从begin到end
更换成
begin
GetMem(bufSend,fsSend.Size-fsSend.Position);
fsSend.Read(bufSend^,sizeof(bufSend));
Socket.SendBuf(bufSend^,sizeof(bufSend));
end
就出现了丢失数据的现象.
那位大虾指点下小弟哦.
 
要检查Socket.SendBuf(bufSend^,iBYTEPERSEND);
的返回值,TCP掉包是你控制不好,不是他本身的问题[:D]
 
是啊,如何控制最后一段的数据包?
在上段代码,一种是写全段的fsSend.Size
一种是通过定位找文件流的位置移动.fsSend.Size-fsSend.Position
fsRecv,fsRecv是TfileStream类,分别是发,送.
bufSend,bufRecv是pointer,缓冲区的.分别是发,送.
 
客户端的核心代码有问题,
(fsSend.Position + iBYTEPERSEND < fsSend.Size) = false时,文件已经全部发送完了
这时候fsSend.Read(bufSend^,fsSend.Size);什么都不会读到,bufSend里的东西当然是
一堆垃圾啦,去掉Else里面的代码
 
sofox
你的方法不行,我用if来限制了两种传输,true时是完整的1024数据包.这传递没问题.
false时传递也就是最后一段小于1024的数据包.在帮忙想想
 
sofox没有看懂代码,
问题出在server端的接收问题,接收时,判断一下长度,如果<1048,那么就是最后一个包
长度就没有1048了。所以解决这个问题很简单。看你上面的代码,你应该有解决这个问题的
能力。从这段代码中我学会了一招:断点续传。
 
断点续传?请教
 
<1048是最后一个包,不一定哦,!
 
我是菜鸟,不好意思,很多是从别人的改写的 。什么是断点续传?
到底这段代码为什么最后会丢包,那位大侠解决下赛。
我忘了说发送的时候是1024为一个包的。1048为接受端。
所以我想<1024为最后一个包。但如何保证传递不丢包?
 
不是说了么,没有丢包,是你接收最后一个包的长度定错了。你每个包的长度都定的是1048(代码上来的),也许你实际上是1024。
 
我也曾经遇到过你这样的问题,现在已经解决(能完整发送像 Delphi7.rar 这样的大文件)。
由两个地方要注意的:
1.客户端发送完文件要 Disconnect
2.接受缓冲区最好和发送缓冲区一样大

这是我的一个能正常工作的:

unit uMain002;

interface

uses
Windows, Messages, SysUtils, Classes, Controls, Forms,
Dialogs, StdCtrls, ScktComp, ComCtrls, IdGlobal, ExtCtrls;

type
TForm1 = class(TForm)
csClient: TClientSocket;
ssServer: TServerSocket;
gbState: TGroupBox;
pbSend: TProgressBar;
pbReceive: TProgressBar;
Label1: TLabel;
Label2: TLabel;
gbControl: TGroupBox;
btnSend: TButton;
btnChooseFile: TButton;
edtFileName: TEdit;
btnConnect: TButton;
Label3: TLabel;
edtServer: TEdit;
Label4: TLabel;
Bevel1: TBevel;
procedure btnSendClick(Sender: TObject);
procedure ssServerClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure ssServerClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure btnChooseFileClick(Sender: TObject);
procedure btnConnectClick(Sender: TObject);
procedure csClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure csClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure csClientRead(Sender: TObject; Socket: TCustomWinSocket);
procedure ssServerClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
private
procedure SendFile;
{ Private declarations }
public
{ Public declarations }
end;

TSendState = (ssCommand, ssSendingFile);
PSendRec = ^TSendRec;
TSendRec = packed record
FileName: String;
FileSize: Integer;
State: TSendState;
end;

const
B_SIZE = 32768;
RCV_SIZE = 32768;
var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.btnSendClick(Sender: TObject);
begin
pbSend.Max := FileSizeByName(edtFileName.Text);
csClient.Socket.SendText(Format('FILE@%s@%d', [edtFileName.Text, pbSend.Max]));
end;

procedure TForm1.SendFile;
var
fs: TFileStream;
buf: PByte;
snd: Integer;
begin
GetMem(buf, B_SIZE);
fs := TFileStream.Create(edtFileName.Text, fmOpenRead);
fs.Position := 0;

try
pbSend.Max := fs.Size;
{ 发送数据 }
repeat
snd := fs.Read(buf^, B_SIZE);
csClient.Socket.SendBuf(buf^, snd);
pbSend.Position := pbSend.Position + snd;
Application.ProcessMessages;
until snd <= 0;
csClient.Active := False; // 必须,否则会丢失数据
finally
FreeMem(buf);
fs.Free;
end;

ShowMessage('文件发送完毕');
end;

procedure TForm1.ssServerClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
fs: TFileStream;
buf: PByte;
rcv: Integer;
strTemp: String;
fmode: Word;

{ 从接收到的信息取出文件名 }
function GetSaveFileName(fn: String): String;
begin
with TSaveDialog.Create(nil) do
try
Result := Copy(fn, 1, Pos('@', fn) - 1);
FileName := Result;
InitialDir := GetCurrentDir;
if Execute then Result := FileName;
finally
Free;
end;
end;
begin
if PSendRec(Socket.Data).State = ssCommand then
begin
{ 从接收到的数据中获取 文件长度、文件名信息 }
strTemp := Socket.ReceiveText;
if Pos('FILE', strTemp) <> 1 then Exit;
Delete(strTemp, 1, Pos('@', strTemp));
PSendRec(Socket.Data).FileName := GetSaveFileName(strTemp);
Delete(strTemp, 1, Pos('@', strTemp));
PSendRec(Socket.Data).FileSize := StrToInt(strTemp);
PSendRec(Socket.Data).State := ssSendingFile;
pbReceive.Max := PSendRec(Socket.Data).FileSize;
Socket.SendText('Send file OK');
end
else begin
{ 第一次接收到文件数据时,创建文件,以后打开文件往里面写数据 }
if FileExists(PSendRec(Socket.Data).FileName) then
fmode := fmOpenWrite
else fmode := fmCreate;

fs := TFileStream.Create(PSendRec(Socket.Data).FileName, fmode);
fs.Seek(0, soFromEnd);
GetMem(buf, RCV_SIZE);
{ 接收数据并写入文件 }
try
repeat
rcv := Socket.ReceiveBuf(buf^, RCV_SIZE);
fs.Write(buf^, rcv);
pbReceive.Position := pbReceive.Position + rcv;
until rcv <= 0;
finally
FreeMem(buf);
fs.Free;
end;
end;
end;

procedure TForm1.ssServerClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
var
rec: PSendRec;
begin
New(rec);
rec^.State := ssCommand;
Socket.Data := rec;
end;

function GetFileName: String;
begin
with TOpenDialog.Create(nil) do
try
if Execute then Result := FileName;
finally
Free;
end;
end;

procedure TForm1.btnChooseFileClick(Sender: TObject);
begin
edtFileName.Text := GetFileName;
end;

procedure TForm1.btnConnectClick(Sender: TObject);
begin
csClient.Port := 4545;
csClient.Address := edtServer.Text;
csClient.Active := True;
end;

procedure TForm1.csClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
btnSend.Enabled := True;
end;

procedure TForm1.csClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
btnSend.Enabled := False;
end;

procedure TForm1.csClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
if Socket.ReceiveText = 'Send file OK' then
SendFile;
end;

procedure TForm1.ssServerClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Dispose(PSendRec(Socket.Data));
ShowMessage('文件接收完毕');
end;

end.
 
呵呵,楼上的代码如果你用猫发文件看看,发送缓冲和接受缓冲的大小根本就没有必要
一样
 
接收缓冲区可以很大,比如16K什么的,发送的缓冲区一般我是4K,
 
谢谢指点。:)
还请问一个问题: 上面的代码运行后,如果在传输过程中打开一个占资源很大的程序,偶
尔也丢包。难道真的需要给包编号,接收端没有收到则请求重发才能保证正确?
 
后退
顶部