我在做一个用winsock传输文件的程序,用到的是serversocket与clientsocket控件,发送程序读取文件信息,利用tfilestream类的readbuffer函数每次读取长度2048,接受程序创建文件并在每次受到数据后追加。
现在发送与接受程序已经是正确的了,能够传输文本文件,可不能传输可执行文件。
我想原因是读取文件时读取的是ASCII流,而不是二进制流,因此只能传输文本而不能传输可执行文件,我想请问如何利用winsock技术来传输文件(包括文本文件与可执行文件)!
现在发送与接受程序已经是正确的了,能够传输文本文件,可不能传输可执行文件。
我想原因是读取文件时读取的是ASCII流,而不是二进制流,因此只能传输文本而不能传输可执行文件,我想请问如何利用winsock技术来传输文件(包括文本文件与可执行文件)!
Delphi中,当你用一个Socket发送一个较大的Stream,接受方会激发多次OnRead事件,
Delphi她只保证多次OnRead事件中每次数据的完整,而不会自己收集数据并返回给用户。
所以不要以为你把待传文件在一个Socket中Send一次,另一个中Recv一次就可以了。
你必须自己收集数据或自己定义协议,所以采用自定义协议的方法。
定义协议的规范方法是利用Record End
接受文件用clientsocket的receivestream或receivebuffer,
但要注意如果传送的文件超过几M,最好自己定义协议,否则用一次sendstream和N次receivestream即可传送几百K的文件。
具体方法请查找网上的sendstream和receivestream的用法
应该是你程序处理上的问题,把你的代码贴上来吧。
Socket: TCustomWinSocket);
var
buffer:array [0..10000] of byte; //设置接收缓冲区
len:integer;
ll:string;
b:tbitmap;
j:tjpegimage;
begin
if c=0 then //C为服务端发送的字节数,如果为0表示为尚未开始图象接收
begin
ll:=socket.ReceiveText;
c:=strtoint(ll); //设置需接收的字节数
clientsocket1.Socket.SendText(‘okok‘); //通知服务端开始发送图象
end else
begin //以下为图象数据接收部分
len:=socket.ReceiveLength; //读出包长度
socket.ReceiveBuf(buffer,len); //接收数据包并读入缓冲区内
m.Write(buffer,len); //追加入流M中
if m.Size>=c then //如果流长度大于需接收的字节数,则接收完毕
begin
m.Position:=0;
b:=tbitmap.Create;
j:=tjpegimage.Create;
try
j.LoadFromStream(m); //将流M中的数据读至JPG图像对象J中
b.Assign(j); //将JPG转为BMP
Image1.Picture.Bitmap.Assign(b); //分配给image1元件
finally //以下为清除工作
b.free;
j.free;
clientsocket1.Active:=false;
clientsocket1.Active:=true;
m.Clear;
c:=0;
end;
end;
end;
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
s,s1:string;
desk:tcanvas;
bitmap:tbitmap;
jpg:tjpegimage;
begin
s:=socket.ReceiveText;
if s=‘gets‘ then //客户端发出申请
begin
bitmap:=tbitmap.Create;
jpg:=tjpegimage.Create;
desk:=tcanvas.Create; //以下代码为取得当前屏幕图象
desk.Handle:=getdc(hwnd_desktop);
m1:=tmemorystream.Create; //初始化流m1,在用sendstream(m1)发送流后,
//它将保留到socket对话结束,
//不能用手工free掉,否则会触发异常
with bitmap do
begin
width:=screen.Width;
height:=screen.Height;
canvas.CopyRect(canvas.cliprect,desk,desk.cliprect);
end;
jpg.Assign(bitmap); //将图象转成JPG格式
jpg.SaveToStream(m1); //将JPG图象写入流中
jpg.free;
m1.Position:=0;
s1:=inttostr(m1.size);
Socket.sendtext(s1); //发送图象大小
end;
if s=‘okok‘ then //客户端已准备好接收图象
begin
m1.Position:=0;
Socket.SendStream(m1); //发送JPG图象
end;end;
具体处理上面。
我改了程序后,没有备分,只有简化写一下了发送程序
var
fs:TFileStream;
Sendsize:integer;
buf:pointer;
begin
fs := TFileStream.Create(文件路径,fmOpenRead);
if LeftSize<buffersize then //Leftsize开始时赋值为文件总长度,buffer为2048
sendsize:=LeftSize
else
sendsize:=buffersize;
LeftSize:= LeftSize - SendSize;
GetMem(Buf,Size);
fs.Seek(0 - Leftsize,soFromEnd);
fs.ReadBuffer(Buf^,sendSize);
clientsocket.Socket.SendBuf(Buf^,sendSize);
freemem(buf);
fs.Destroy;
接受程序
var
buf:pointer;
nRec:integer;
fs:TFilestream;
begin
nRec:=socket.receiveLength;
GetMem(Buf,nrec);
Socket.ReceiveBuf(Buf^,nRec);
if not FIleExists(文件路径) then
begin
fs :=TFileStream.Create(文件路径,fmCreate);
fs.Seek(0,soFromBeginning);
end
else
begin
fs :=TFileStream.Create(文件路,fmOpenWrite);
fs.Seek(0,soFromEnd);
end;
fs.WriteBuffer(Buf^,nRec);
fs.Destroy;
FreeMem(Buf);
RecSize := RecSize + nRec; //recsize为目前为止接受到的字节数
if recsize=totalsize then //totalsize为文件总长度
showmessage('transportation complete')
else
sendmsg(MSG_REC_FILE,'',serversocket.socket.conncetion[0]);
end;可能有些遗漏,client端接到MSG_REC_FILE消息后执行发送程序
.........
fs.ReadBuffer(Buf^,sendSize);
clientsocket.Socket.SendBuf(Buf^,sendSize);//这样写的话,文件小不会错,
//文件大会出问题的。//可以改写成每次读到一块内存中,循环发出,
//关键之处一定要检查SendBuf的返回值。。
//也可以干脆就使用SendStream.
clientSocket.Socket.SendStream(fs);
二,接收的问题。。(吃饭回来再写)
你文本文件可以传而二进制不可传根源可能在它
//正好回另一个贴子时候写了这个,顺便贴上来.
//类似的代码我用来传上百M的压缩文件都是可行的unit Unit1;interfaceuses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ScktComp, StdCtrls;type
TForm1 = class(TForm)
ClientSocket1: TClientSocket;
Memo1: TMemo;
Button1: TButton;
Button2: TButton;
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
procedure Button1Click(Sender: TObject);
procedure ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
procedure ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
FStr:String;
Fo:TStream;
procedure DisplayMsg(s:string);
end;var
Form1: TForm1;implementation{$R *.dfm}procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
s:string;//用它只是因为我喜欢用STRING方式的内存分配。
iSize:Integer;
bufSize:integer;
begin
FSTr:='';
bufSize:=1024;
SetLength(s,bufSize);
repeat
iSize:=Socket.ReceiveBuf(pchar(s)^,bufSize);
if iSize>0 then
begin
SetLength(s,iSize);
Fo.Write(pchar(s)^,iSize);
FStr:=FSTr+s;
end
until (iSize<>3) or (iSize=-1);
DisplayMsg(FStr);
end;procedure TForm1.DisplayMsg(s: string);
begin
memo1.Lines.Add(s)
end;procedure TForm1.Button1Click(Sender: TObject);
begin
ClientSocket1.Active:=true;
end;procedure TForm1.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Fo:=TFileStream.Create('d:\test.xxx',FmCreate);
//我是一次创建文件流的,象你那样每次接收创建文件流也是可行的,
//要改的代码也不多。
end;procedure TForm1.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Fo.Free;
end;procedure TForm1.Button2Click(Sender: TObject);
begin
ClientSocket1.Active:=false;
end;
end.