解决方案 »

  1.   

    冒犯高手了,请勿见怪,我只想问个问题:
    DELPHI中,用TClientSocket和一个TServerSocket连接,正常发送和接收少量消息时,完全正常。但是,如果服务器端发送大量消息(相对而言,200以上)时,客户端就会来由于不及处理而产生数据的丢失现象,如何解决?
    一个数据包大概在1~4K左右。
      

  2.   

    TCP能够自动重发,我觉得应该问题不大,你不妨试试以下这个程序:
    unit Unit1;
    interface
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
      StdCtrls, ScktComp;
    type
      TCon = record
        FileName : String;
        TotalSize : Integer;
        Status : Integer;
      end;  PCON = ^TCON;  TForm1 = class(TForm)
        GroupBox1: TGroupBox;
        GroupBox2: TGroupBox;
        ClientSocket: TClientSocket;
        ServerSocket: TServerSocket;
        btnServerActive: TButton;
        btnClientCon: TButton;
        btnClientDisConn: TButton;
        BtnClientSendF: TButton;
        Memo1: TMemo;
        OpenDialog: TOpenDialog;
        Edit1: TEdit;
        Label1: TLabel;
        procedure btnClientConClick(Sender: TObject);
        procedure btnClientDisConnClick(Sender: TObject);
        procedure BtnClientSendFClick(Sender: TObject);
        procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
        procedure ClientSocketError(Sender: TObject; Socket: TCustomWinSocket;
          ErrorEvent: TErrorEvent; var ErrorCode: Integer);
        procedure btnServerActiveClick(Sender: TObject);
        procedure ServerSocketClientConnect(Sender: TObject;
          Socket: TCustomWinSocket);
        procedure ServerSocketClientRead(Sender: TObject;
          Socket: TCustomWinSocket);
        procedure ServerSocketClientError(Sender: TObject;
          Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
          var ErrorCode: Integer);
        procedure FormCreate(Sender: TObject);
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;implementation{$R *.DFM}var  Count : Integer;function GetFileSize(const FileName: string):integer;
    var f : TFileStream;
    begin
        f := TFileStream.Create(FileName,fmOpenRead or fmShareDenyNone);
        Result :=f.Size;
        F.Free;
    end;procedure TForm1.btnClientConClick(Sender: TObject);
    begin
      ClientSocket.Active := True;
      with OpenDialog do
      begin
          Execute;
          if FileName <> '' then
          begin
              Edit1.Text := 'UPLOAD '+ ExtractFileName(FileName) +' '+Inttostr(GetFileSize(FileName));
              Label1.Caption := FileName;
              ClientSocket.Socket.SendText(edit1.Text);
          end;
      end;
    end;procedure TForm1.btnClientDisConnClick(Sender: TObject);
    begin
      ClientSocket.Active := False;
    end;procedure TForm1.BtnClientSendFClick(Sender: TObject);
    var fs : TFileStream;
        Buf : pointer;
    begin
      fs := TFileStream.Create(Label1.Caption ,fmOpenRead or fmShareDenyNone);
      GetMem(Buf,fs.Size);
      fs.Seek(0,soFromBeginning);
      fs.ReadBuffer(Buf^,fs.Size);
      memo1.Lines.Add('has send : '+inttostr(ClientSocket.Socket.SendBuf(Buf^,fs.Size)));
    end;procedure TForm1.ClientSocketRead(Sender: TObject;
      Socket: TCustomWinSocket);
    begin
      Memo1.Lines.add(socket.ReceiveText);
    end;procedure TForm1.ClientSocketError(Sender: TObject;
      Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
      var ErrorCode: Integer);
    begin
      ErrorCode := 0;
    end;procedure TForm1.btnServerActiveClick(Sender: TObject);
    begin
      ServerSocket.Active := True;
    end;procedure TForm1.ServerSocketClientConnect(Sender: TObject;
      Socket: TCustomWinSocket);
    var c : pcon;
    begin
      c :=new(pcon);
      c.FileName := '';
      c.TotalSize :=  0 ;
      c.Status := 0;
      Socket.Data := c;
      Socket.SendText('已经连接,请输入UPLOAD FILENAME SIZE'#13#10);
    end;procedure TForm1.ServerSocketClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    var C : PCON;
        cmd:String;
        Buffer : pointer;
        nRetr : integer;
        fs : TFileStream;
    const bufferSize =  1024 ;begin
      C:= Socket.Data ;
      case c.Status of
       0 :
       begin
         cmd := trim(Socket.ReceiveText) ;
         if  Pos('UPLOAD ',uppercase(cmd)) >  0 then
         begin
           c.FileName := trim(Copy(cmd,Pos(' ',cmd)+1,Length(cmd)));
           c.TotalSize := StrToInt(Copy(c.FileName,Pos(' ',c.FileName)+1,Length(c.FileName)));
           c.FileName := trim(Copy(c.FileName,1,Pos(' ',c.FileName)));
           c.Status := 1;
           Socket.Data := C;
           Socket.SendText('you can send File  !'#13#10);
         end;
       end;
       1 :
       begin
         Count := count + 1;
         GetMem(Buffer,BufferSize);
         nRetr := Socket.ReceiveBuf(Buffer^,BufferSize);
         Memo1.Lines.Add(IntToStr(Count) + ' ' + IntToStr(nRetr));
         if not FIleExists('c:\'+c.FileName) then
         begin
           fs :=TFileStream.Create('c:\'+c.FileName,fmCreate or fmShareDenyNone);
           fs.Seek(0,soFromBeginning);
         end
         else
         begin
           fs :=TFileStream.Create('c:\'+c.FileName,fmOpenWrite or fmShareDenyNone);
           fs.Seek(0,soFromEnd);
         end;
         fs.WriteBuffer(Buffer^,nRetr);
         fs.Destroy;
         FreeMem(Buffer);
       end;
      end;
    end;procedure TForm1.ServerSocketClientError(Sender: TObject;
      Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
      var ErrorCode: Integer);
    begin
      ErrorCode := 0;
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      Count := 0;
    end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      ClientSocket.Active := False;
      ServerSocket.Active := False;
    end;
    end.
      

  3.   

    forgot(让一切随风)老兄:
    你的程序还是只发送一个数据包,这在我的程序里也能处理。
    如果是发送多个数据包的话,有没有好的办法:
    如:
    for i:=0 to 1000 do
      SendToClient(addr,msg[i]);
    客户端如何及时处理而不至于丢失数据。
    THANKS。
      

  4.   

    Socket通信在Windows 中是排队的形式由操作系统处理,而且接收方和发送方相互协同工作,否则就会造成数据丢失。因此,不能用类似于for 语句的循环来实现对多组数据的发送,更不能用循环语句来接收数据。比如,你可以用for 语句来实型若干文件的复制,这很普遍也很正常,但在 Socket编程以及大多数网络应用编程中都是行不通的,因为网络通信的基本方式是请求和应答。另外,和所有的通信编程一样,Socket编程也遵循数据分包传送这一基本规则。也就是说,在 Socket编程中,每次发送和接收一个包,以保证数据传输的安全性和稳定性,同时也不至于过多地占用系统资源。 对于ClientSocket组件,从字面上就可以看出,它用于请求方。也就是说,它的动作是主动地建立连接。显然,ServerSocket组件用于响应方,它的动作是侦听以及被动接受连接。 组件ClientSocket的属性是相对静态的,它和ServerSocket之间只是连接和断开的关系。并且仅当ServerSocket对其接受才表示建立连接。 组件ServerSocket的属性是动态的。伴随着一个新的ClientSocket与之建立连接的同时,就会产生一个新的Socket与该ClientSocket对应,保持单独的连接,进行单独的通信。因此,在同一个 ServerSocket中,可以与多个ClientSocket保持同时连接和各自独立的通信。ServerSocket的属性 Socket.ActiveConnections用于表示客户端连接的数量;属性Socket.Connections[Index] 则用于访问单个与ClientSocket连接的Socket。 正是这样的结构,才使得WinSocket 技术能够稳定实现一个服务程序向多个客户端提供服务。 在独立的ClientSocket中,属性Socket.Data 是一个指针,缺省值是nil ;在ServerSocket的每个独立的Socket.Connections[Index]中, 属性Data也是一个指针,缺省值是nil 。因此,可以通过该指针建立并保存各自独立的相关信息,用于实现各自独立的通信。而在ClientSocket的事件 OnRead中,调用方法传递的Socket值就是响应该事件的对象属性ClientSocket.Socket 。同样,在 ServerSocket的事件OnClientRead中,调用方法传递的参数Socket就是对应于当前发送数据客户端的唯一的Socket连接,即ServerSocket.Socket.Connections[Index]。这样,就能够对不同的连接分得清清楚楚明明白白。 首先介绍实例程序的设计思想。上传文件的过程是这样的(这里的C和S分别代表客户端和服务器端):  C:请求上传文件; 
     S:准备就绪,可以接收; 
     C:需要上传的文件信息; 
     S:收到文件信息: 
     C:第一个包; 
     S:收到第一个包;创建文件,开始写数据; 
     C:中间的包; 
     S:收到中间的包;继续写数据; 
     C:发送最后一个包,关闭文件; 
     S:收到最后一个包;写数据,关闭文件。 
    下载文件的过程是这样的:  C:请求下载文件; 
     S:准备就绪,可以下载; 
     C:需要下载的文件信息(文件名); 
     S:反馈文件信息(文件大小); 
     C:准备就绪,可以接收数据; 
     S:第一个包; 
     C:收到第一个包;创建文件,开始写数据; 
     S:中间的包; 
     C:收到中间的包;继续写数据; 
     S:发送最后一个包,关闭文件; 
     C:收到最后一个包;写数据,关闭文件;下载成功; 
     S:下载成功。 
    其中,发送中间的包和收到中间的包根据包的数量可以重复。不难看出,上面的两个过程是典型的“你一句我一句”的应答方式。