我在做一个用winsock传输文件的程序,用到的是serversocket与clientsocket控件,发送程序读取文件信息,利用tfilestream类的readbuffer函数每次读取长度2048,接受程序创建文件并在每次受到数据后追加。
现在发送与接受程序已经是正确的了,能够传输文本文件,可不能传输可执行文件。
我想原因是读取文件时读取的是ASCII流,而不是二进制流,因此只能传输文本而不能传输可执行文件,我想请问如何利用winsock技术来传输文件(包括文本文件与可执行文件)!

解决方案 »

  1.   

    文件传输的时候可以有两种模式,ASCII码和Bin,不过一般通用Bin 就可以了。
    Delphi中,当你用一个Socket发送一个较大的Stream,接受方会激发多次OnRead事件,
    Delphi她只保证多次OnRead事件中每次数据的完整,而不会自己收集数据并返回给用户。
    所以不要以为你把待传文件在一个Socket中Send一次,另一个中Recv一次就可以了。
    你必须自己收集数据或自己定义协议,所以采用自定义协议的方法。
    定义协议的规范方法是利用Record End
      

  2.   

    发送文件可用serversocket的sendstream或sendbuffer,
    接受文件用clientsocket的receivestream或receivebuffer,
    但要注意如果传送的文件超过几M,最好自己定义协议,否则用一次sendstream和N次receivestream即可传送几百K的文件。
    具体方法请查找网上的sendstream和receivestream的用法
      

  3.   

    WINSOCK都是传的流数据,并没有ASCII流与二进制流的区分。
    应该是你程序处理上的问题,把你的代码贴上来吧。
      

  4.   

    procedure TForm1.ClientSocket1Read(Sender: TObject;
    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;
      

  5.   

    我个人的意见认为如果需要传输文件或者流之类的东西,最好使用原生的socket来做,使用clientsockt控件也行,但是可能要受到一些限制.我现在正在做的一个东西就是使用最原始的socket来完成数据的发送和接收工作,在每一个线程中包含了对命令的解析,数据的校验等等.
      

  6.   

    我以前使用tfilestream,然后每次从文件中读取一块数据,用的函数是fileread,不过这个函数只能从文件读取ASCII流,因此我的程序只能传输文本文件,现在听了楼上各位的意见,我用内存流来做,已经行了,用tmemorystream类的loadfromfile来把文件装入内存流,然后分批发送,不过这样有一个问题,就是我以前不用内存流的原因,如果文件很大的话,那不是相应的内存流不是要很大吗?比如一个电影realplay,几百兆,那相应的内存流岂不是太大了,系统效率会不会降低?
      

  7.   

    to:lengfeng04(绝影);好象没有谁叫你用TMEMORYSTREM来LOADFROMFILE。。这显然是馊主意。。就用TFileStream,读一块发一块数据出去。做法没有问题的。如果有问题只可能出在你
    具体处理上面。
      

  8.   

    不会吧,那楼上的仁兄帮我看看吧
    我改了程序后,没有备分,只有简化写一下了发送程序
    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消息后执行发送程序
      

  9.   

    一,发送的问题:
    .........
      fs.ReadBuffer(Buf^,sendSize);
      clientsocket.Socket.SendBuf(Buf^,sendSize);//这样写的话,文件小不会错,
                                                 //文件大会出问题的。//可以改写成每次读到一块内存中,循环发出,
    //关键之处一定要检查SendBuf的返回值。。
    //也可以干脆就使用SendStream.
      clientSocket.Socket.SendStream(fs);
    二,接收的问题。。(吃饭回来再写)
      

  10.   

    二,接收的问题。。(socket.receiveLength;不是一个很可靠的东西,我一般不用它。
    你文本文件可以传而二进制不可传根源可能在它
    //正好回另一个贴子时候写了这个,顺便贴上来.
    //类似的代码我用来传上百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.