http://www.greensoftcode.net/techntxt/201141310049498110245
我采用的开发语言是delphi 当然采用其它开发语言大同小异。
在写代码之前先说下网络传送协议:常用的网络文件传输协议为:TCP 和UDP,两者的区别是TCP是面向连接的 UDP是非面向连接的。TCP所谓的面向连接的及三次握手协议,报文头加入验证字段等很复杂
UDP相对TCP报头帧相对简单无验证帧。
一.udp 传送文件的特点:
1.速度快
2.传送质量差可能丢包。
3.1m小文件最佳
二、tcp 传送文件的特点:
1.传送文件速度相对UDP较慢。
2.传送质量较高丢包情况较少
3.大文件传送佳。
三、udp传送代码:
采用delphi IdUDPClient、IDUDPSERVER的的控件
//发送端
function udpsenchangefile(filename:string ;ip:string;port:integer; bar:TProgressBar;mainfrm:TForm;iconarr:array of TIcon;label1:TLabel;label2:TLabel):boolean; //发送文件
var
receivedString:string;
stream:TFileStream;
posi,len:integer;
p:array[0..1023] of byte;
sendresult:boolean; //发送结果
sendedsize :integer; //已发送字节数
udpclient:TIdUDPClient ;
begin
sendedsize:=0;
sendresult:=false;
udpclient:= TIdUDPClient.Create(Application);
udpclient.Host:=ip;
udpclient.Port:=port;
if not udpclient.Active then udpclient.Active:=true;
posi:=0;
stream:=nil;
try
stream:=tfileStream.Create(filename,fmOpenRead);
if stream.Size>0 then
udpclient.Send('token'+'|'+filename+'|'+IntToStr(stream.Size));
receivedString:=udpclient.ReceiveString();
if uppercase(receivedString)=upperCase('agree-accept')then
begin
udpclient.Active:=false;
udpclient.Free ;
while posi<stream.Size do
begin
udpclient:= TIdUDPClient.Create(Application); //每发送一个文件建立一个对象 因为 我用一个控件时发送大于1M文件就程序就死掉啦!
udpclient.Host:=ip;
udpclient.Port:=port;
udpclient.ReceiveTimeout:=-2;
udpclient.BufferSize :=1024000;
udpclient.BroadcastEnabled:=false;
if not udpclient.Active then udpclient.Active:=true;
len:= sendbytesize; //只能发 sendbytesize
if stream.Size< len then //如果长度不到 sendbytesize
len:=stream.Size;
stream.Read(p,len);
udpclient.SendBuffer(p,len);
sendedsize:=sendedsize+1;
inc(posi,len);
label1.Caption:=inttostr(stream.Size);
bar.Position:=round(posi/stream.size*100);
//softtitle:=softtitle+inttostr(sendedsize)+'字节';
receivedString:=udpclient.ReceiveString();
//if ( sendedsize>0 )and (sendedsize mod 1000 =0) then Delay(5000);
label2.Caption:=inttostr( sendedsize);
if upperCase(ReceivedString)<>upperCase('receivedok') then break;
application.ProcessMessages;
changeico(mainfrm,softtitle, iconarr) ;
udpclient.Active:=false;
udpclient.Free ;
end;
udpclient.Send('tokenfilal');
if udpclient.ReceiveString()='receivefilal' then
sendresult:=true;
end
else
sendresult:=false;
finally
stream.Free;
end;
Result:=sendresult;
end;
接受端:
procedure Tmainfrm.udpserverUDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var
str ,receiveval:string;
temp:TStringList;
begin
aData.Seek(0,0);
setLength(receiveval,aData.size);
aData.Read(receiveval[1],aData.Size);
temp:= SplitString(receiveval,'|');
case protocol.IndexOf(temp[0]) of
0: begin
receiveFileName:=temp[1];
receiveFileSize:=strtoint(temp[2]);
filename:=createreceivedir(receiveFileName,receivedir);
if not fileExists(filename) then
stream:=TFileStream.Create(FileName,sysutils.fmOpenReadWrite or fmCreate)
else
stream:=TFileStream.Create(FileName,fmopenReadWrite);
str:='agree-accept';
abinding.SendTo(aBinding.PeerIP ,aBindIng.PeerPort ,str[1],length(str));
end ;
1:
if stream<>nil then
begin
successfilesynchcount:=successfilesynchcount+1;
stream.Free;
stream:=nil;
str:='receivefilal';
aBinding.SendTo(aBinding.PeerIP,aBinding.PeerPort,str[1],length(str));
filename:='';
receiveFileName:='';
receiveFileSize:=0;
receivsize:=0;
softtitle:='目前成功接受'+inttostr(successfilesynchcount)+'个文件!';
end;
else
if stream<>nil then
begin
receivsize:=receivsize+1;
softtitle:='正在接受'+ filename ;
stream.Seek(0,2);
aData.Seek(0,0);
stream.CopyFrom(aData,aData.Size);
label1.Caption:=inttostr( receivsize) ;
Bar.Position:=round(stream.size/receiveFileSize*100);
str:='receivedok';
abinding.SendTo(aBinding.PeerIP,aBinding.PeerPort,str[1],length(str));
label2.Caption:=inttostr( receivsize) ;
application.ProcessMessages;
changeico(mainfrm,softtitle, notifyicon) ;
end;
end;
end;
通过这个例子udp 发送大文件时我发现确实存在不可靠事件,及文件可能没发送完!
要想准确发送成功!可能还要改进算法
tcp 发送例子
发送端:
function tcpsenchangefile(filename:string ;ip:string;port:integer; bar:TProgressBar;mainfrm:TForm;iconarr:array of TIcon):boolean; //发送文件
var
receivedString:string;
stream:TFileStream;
ReadCount : Integer;
Buf:array[0..1023] of byte;
sendresult:boolean; //发送结果
tcpclient:TIdTCPClient ;
begin
sendresult:=false;
tcpclient:=TIdTCPClient.Create(Application);
tcpclient.Host:=ip;
tcpclient.Port:=port;
if not tcpclient.Connected then tcpclient.Connect(5000);
stream:=nil;
try
stream:=tfileStream.Create(filename,fmOpenRead);
if stream.Size>0 then tcpclient.WriteLn('token'+'|'+filename+'|'+IntToStr(stream.Size));
receivedString:=tcpclient.ReadLn(#13#10, 1000);
if uppercase(receivedString)=upperCase('agree-accept')then
begin
while stream.Position < stream.Size do
begin
if stream.Size - stream.Position >= SizeOf(Buf) then
ReadCount := sizeOf(Buf)
else ReadCount := stream.Size - stream.Position;
stream.ReadBuffer(Buf, ReadCount);
tcpclient.WriteBuffer(Buf, ReadCount);
//receivedString:=tcpclient.ReadLn(#13#10, 1000);
//if upperCase(ReceivedString)<>upperCase('receivedok') then break;
changeico(mainfrm,softtitle, iconarr) ;
end;
tcpclient.WriteLn('tokenfilal');
receivedString:=tcpclient.ReadLn(#13#10, 1000);
if receivedString='receivefilal' then sendresult:=true;
tcpclient.Disconnect;
end;
except
sendresult:=false;
end;
tcpclient.Disconnect;
if stream<>nil then FreeAndNil(stream);
if tcpclient<>nil then FreeAndNil(tcpclient);
Result:=sendresult;
end;
接受端
procedure Tmainfrm.tcpserverExecute(AThread: TIdPeerThread);
var
str ,receiveval:string;
temp:TStringList;
Buff : array[0..1023] of Byte; // Buff 缓存区大小设置,byte型
ReadCount : Integer; //实际每次读取文件块的大小,整型
begin
if not AThread.Terminated and AThread.Connection.Connected then
begin
if State= dstNone then
receiveval := AThread.Connection.ReadLn(#13#10, 1000);
if receiveval='' then Exit;
temp:= SplitString(receiveval,'|');
if protocol.IndexOf(temp[0])=0 then
begin
receiveFileName:=temp[1];
receiveFileSize:=strtoint(temp[2]);
filename:=createreceivedir(receiveFileName,receivedir);
if not fileExists(filename) then
stream:=TFileStream.Create(FileName,sysutils.fmOpenReadWrite or fmCreate)
else
stream:=TFileStream.Create(FileName,fmopenReadWrite);
str:='agree-accept';
AThread.Connection.WriteLn(str);
State := dstReceiving;
end
else
begin
successfilesynchcount:=successfilesynchcount+1;
str:='receivefilal';
AThread.Connection.WriteLn(str);
filename:='';
receiveFileName:='';
receiveFileSize:=0;
receivsize:=0;
softtitle:='目前成功接受'+inttostr(successfilesynchcount)+'个文件!';
end;
end;
if stream<>nil then
begin
repeat
if receiveFileSize - Stream.Size > SizeOf(Buff) then
ReadCount := SizeOf(Buff)
else
ReadCount := receiveFileSize - Stream.Size;
AThread.Connection.ReadBuffer(Buff, ReadCount); //从连接中读取 ReadCount长度的文件块放到缓冲区Buff,中
stream.WriteBuffer(Buff, ReadCount); //将缓冲区中的内容写进文件流中,这是就是写到文件aFileName中啦
Bar.Position:=round(stream.size/receiveFileSize*100);
Application.ProcessMessages; //这句作用是让消息传递动态显示起来,如果没有这句上面的caption是不会显示跳动的
changeico(mainfrm,softtitle, notifyicon) ;
// str:='receivedok';
// AThread.Connection.WriteLn(str);
until Stream.Size >= receiveFileSize; //直到文件大小和原文件大小一致结束循环
State := dstNone;
stream.Free;
stream:=nil;
end;
end;
Tcp 发送大小文件测试过程中全部成功!这说明TCP发送文件可靠性比较强!算法要求低。
上面的算法有些变量没有写全 但核心代码没有问题 测试通过!
在写代码之前先说下网络传送协议:常用的网络文件传输协议为:TCP 和UDP,两者的区别是TCP是面向连接的 UDP是非面向连接的。TCP所谓的面向连接的及三次握手协议,报文头加入验证字段等很复杂
UDP相对TCP报头帧相对简单无验证帧。
一.udp 传送文件的特点:
1.速度快
2.传送质量差可能丢包。
3.1m小文件最佳
二、tcp 传送文件的特点:
1.传送文件速度相对UDP较慢。
2.传送质量较高丢包情况较少
3.大文件传送佳。
三、udp传送代码:
采用delphi IdUDPClient、IDUDPSERVER的的控件
//发送端
function udpsenchangefile(filename:string ;ip:string;port:integer; bar:TProgressBar;mainfrm:TForm;iconarr:array of TIcon;label1:TLabel;label2:TLabel):boolean; //发送文件
var
receivedString:string;
stream:TFileStream;
posi,len:integer;
p:array[0..1023] of byte;
sendresult:boolean; //发送结果
sendedsize :integer; //已发送字节数
udpclient:TIdUDPClient ;
begin
sendedsize:=0;
sendresult:=false;
udpclient:= TIdUDPClient.Create(Application);
udpclient.Host:=ip;
udpclient.Port:=port;
if not udpclient.Active then udpclient.Active:=true;
posi:=0;
stream:=nil;
try
stream:=tfileStream.Create(filename,fmOpenRead);
if stream.Size>0 then
udpclient.Send('token'+'|'+filename+'|'+IntToStr(stream.Size));
receivedString:=udpclient.ReceiveString();
if uppercase(receivedString)=upperCase('agree-accept')then
begin
udpclient.Active:=false;
udpclient.Free ;
while posi<stream.Size do
begin
udpclient:= TIdUDPClient.Create(Application); //每发送一个文件建立一个对象 因为 我用一个控件时发送大于1M文件就程序就死掉啦!
udpclient.Host:=ip;
udpclient.Port:=port;
udpclient.ReceiveTimeout:=-2;
udpclient.BufferSize :=1024000;
udpclient.BroadcastEnabled:=false;
if not udpclient.Active then udpclient.Active:=true;
len:= sendbytesize; //只能发 sendbytesize
if stream.Size< len then //如果长度不到 sendbytesize
len:=stream.Size;
stream.Read(p,len);
udpclient.SendBuffer(p,len);
sendedsize:=sendedsize+1;
inc(posi,len);
label1.Caption:=inttostr(stream.Size);
bar.Position:=round(posi/stream.size*100);
//softtitle:=softtitle+inttostr(sendedsize)+'字节';
receivedString:=udpclient.ReceiveString();
//if ( sendedsize>0 )and (sendedsize mod 1000 =0) then Delay(5000);
label2.Caption:=inttostr( sendedsize);
if upperCase(ReceivedString)<>upperCase('receivedok') then break;
application.ProcessMessages;
changeico(mainfrm,softtitle, iconarr) ;
udpclient.Active:=false;
udpclient.Free ;
end;
udpclient.Send('tokenfilal');
if udpclient.ReceiveString()='receivefilal' then
sendresult:=true;
end
else
sendresult:=false;
finally
stream.Free;
end;
Result:=sendresult;
end;
接受端:
procedure Tmainfrm.udpserverUDPRead(Sender: TObject; AData: TStream;
ABinding: TIdSocketHandle);
var
str ,receiveval:string;
temp:TStringList;
begin
aData.Seek(0,0);
setLength(receiveval,aData.size);
aData.Read(receiveval[1],aData.Size);
temp:= SplitString(receiveval,'|');
case protocol.IndexOf(temp[0]) of
0: begin
receiveFileName:=temp[1];
receiveFileSize:=strtoint(temp[2]);
filename:=createreceivedir(receiveFileName,receivedir);
if not fileExists(filename) then
stream:=TFileStream.Create(FileName,sysutils.fmOpenReadWrite or fmCreate)
else
stream:=TFileStream.Create(FileName,fmopenReadWrite);
str:='agree-accept';
abinding.SendTo(aBinding.PeerIP ,aBindIng.PeerPort ,str[1],length(str));
end ;
1:
if stream<>nil then
begin
successfilesynchcount:=successfilesynchcount+1;
stream.Free;
stream:=nil;
str:='receivefilal';
aBinding.SendTo(aBinding.PeerIP,aBinding.PeerPort,str[1],length(str));
filename:='';
receiveFileName:='';
receiveFileSize:=0;
receivsize:=0;
softtitle:='目前成功接受'+inttostr(successfilesynchcount)+'个文件!';
end;
else
if stream<>nil then
begin
receivsize:=receivsize+1;
softtitle:='正在接受'+ filename ;
stream.Seek(0,2);
aData.Seek(0,0);
stream.CopyFrom(aData,aData.Size);
label1.Caption:=inttostr( receivsize) ;
Bar.Position:=round(stream.size/receiveFileSize*100);
str:='receivedok';
abinding.SendTo(aBinding.PeerIP,aBinding.PeerPort,str[1],length(str));
label2.Caption:=inttostr( receivsize) ;
application.ProcessMessages;
changeico(mainfrm,softtitle, notifyicon) ;
end;
end;
end;
通过这个例子udp 发送大文件时我发现确实存在不可靠事件,及文件可能没发送完!
要想准确发送成功!可能还要改进算法
tcp 发送例子
发送端:
function tcpsenchangefile(filename:string ;ip:string;port:integer; bar:TProgressBar;mainfrm:TForm;iconarr:array of TIcon):boolean; //发送文件
var
receivedString:string;
stream:TFileStream;
ReadCount : Integer;
Buf:array[0..1023] of byte;
sendresult:boolean; //发送结果
tcpclient:TIdTCPClient ;
begin
sendresult:=false;
tcpclient:=TIdTCPClient.Create(Application);
tcpclient.Host:=ip;
tcpclient.Port:=port;
if not tcpclient.Connected then tcpclient.Connect(5000);
stream:=nil;
try
stream:=tfileStream.Create(filename,fmOpenRead);
if stream.Size>0 then tcpclient.WriteLn('token'+'|'+filename+'|'+IntToStr(stream.Size));
receivedString:=tcpclient.ReadLn(#13#10, 1000);
if uppercase(receivedString)=upperCase('agree-accept')then
begin
while stream.Position < stream.Size do
begin
if stream.Size - stream.Position >= SizeOf(Buf) then
ReadCount := sizeOf(Buf)
else ReadCount := stream.Size - stream.Position;
stream.ReadBuffer(Buf, ReadCount);
tcpclient.WriteBuffer(Buf, ReadCount);
//receivedString:=tcpclient.ReadLn(#13#10, 1000);
//if upperCase(ReceivedString)<>upperCase('receivedok') then break;
changeico(mainfrm,softtitle, iconarr) ;
end;
tcpclient.WriteLn('tokenfilal');
receivedString:=tcpclient.ReadLn(#13#10, 1000);
if receivedString='receivefilal' then sendresult:=true;
tcpclient.Disconnect;
end;
except
sendresult:=false;
end;
tcpclient.Disconnect;
if stream<>nil then FreeAndNil(stream);
if tcpclient<>nil then FreeAndNil(tcpclient);
Result:=sendresult;
end;
接受端
procedure Tmainfrm.tcpserverExecute(AThread: TIdPeerThread);
var
str ,receiveval:string;
temp:TStringList;
Buff : array[0..1023] of Byte; // Buff 缓存区大小设置,byte型
ReadCount : Integer; //实际每次读取文件块的大小,整型
begin
if not AThread.Terminated and AThread.Connection.Connected then
begin
if State= dstNone then
receiveval := AThread.Connection.ReadLn(#13#10, 1000);
if receiveval='' then Exit;
temp:= SplitString(receiveval,'|');
if protocol.IndexOf(temp[0])=0 then
begin
receiveFileName:=temp[1];
receiveFileSize:=strtoint(temp[2]);
filename:=createreceivedir(receiveFileName,receivedir);
if not fileExists(filename) then
stream:=TFileStream.Create(FileName,sysutils.fmOpenReadWrite or fmCreate)
else
stream:=TFileStream.Create(FileName,fmopenReadWrite);
str:='agree-accept';
AThread.Connection.WriteLn(str);
State := dstReceiving;
end
else
begin
successfilesynchcount:=successfilesynchcount+1;
str:='receivefilal';
AThread.Connection.WriteLn(str);
filename:='';
receiveFileName:='';
receiveFileSize:=0;
receivsize:=0;
softtitle:='目前成功接受'+inttostr(successfilesynchcount)+'个文件!';
end;
end;
if stream<>nil then
begin
repeat
if receiveFileSize - Stream.Size > SizeOf(Buff) then
ReadCount := SizeOf(Buff)
else
ReadCount := receiveFileSize - Stream.Size;
AThread.Connection.ReadBuffer(Buff, ReadCount); //从连接中读取 ReadCount长度的文件块放到缓冲区Buff,中
stream.WriteBuffer(Buff, ReadCount); //将缓冲区中的内容写进文件流中,这是就是写到文件aFileName中啦
Bar.Position:=round(stream.size/receiveFileSize*100);
Application.ProcessMessages; //这句作用是让消息传递动态显示起来,如果没有这句上面的caption是不会显示跳动的
changeico(mainfrm,softtitle, notifyicon) ;
// str:='receivedok';
// AThread.Connection.WriteLn(str);
until Stream.Size >= receiveFileSize; //直到文件大小和原文件大小一致结束循环
State := dstNone;
stream.Free;
stream:=nil;
end;
end;
Tcp 发送大小文件测试过程中全部成功!这说明TCP发送文件可靠性比较强!算法要求低。
上面的算法有些变量没有写全 但核心代码没有问题 测试通过!
c语言示例代码
ReplyDelete在c编程中从png图像创建负片