Client负责发送数据
Server负责接收数据1、Client每次发送2048个字节。
2、Server在接收到数据的时候判断接收数据的长度。
3、如果不符合要求,则向Client返回重新发送上一次数据的标志。
4、Client接收到重新发送上次数据的标志后,将流读取位置定义到上一次的位置。
5、Client如去流然后发送。问题是在Server接收数据长度不对的时候,返回重新发送标志位,Client也能接收到这个标志位,但是执行到Socket.SendBuf(Buffer,2048);的时候Client就没反映了,Server也没受到重新发送的数据。以下是代码。//ClientSocket1Read
procedure TForm1.ClientSocket1Read(Sender: TObject;
  Socket: TCustomWinSocket);
begin  cmd := Socket.ReceiveText;  if (pos('CANSEND',cmd)<>0) or (pos('RETURNSEND',cmd)<>0) then
    begin
          if 2048<SendSize then
            begin
                begin
                  if pos('RETURNSEND',cmd)<>0 then
                    begin
                      SendSize:=SendSize+2048;
                      ProgressBar1.Position := ProgressBar1.Position - 2048;
                    end;                  AFileStream.Seek(FileSize-SendSize,soFromBeginning);
                  AFileStream.ReadBuffer(Buffer,2048);
                  //变换剩余传输量
                  SendSize:=SendSize-2048;
                  ProgressBar1.Position := ProgressBar1.Position + 2048;
                  Socket.SendBuf(Buffer,2048);
                end;
            end
          else
            begin
              AFileStream.ReadBuffer(Buffer,SendSize);
              ProgressBar1.Position := ProgressBar1.Position + SendSize;
              SentLen := Socket.SendBuf(Buffer,SendSize);
              AFileStream.Free;
              AFileStream := Nil;
              Socket.Close;
              ShowMessage('上传成功');
              aaa := 'aaa';
            end;
    end;
end;//ServerSocket1ClientRead
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
  Socket: TCustomWinSocket);
var
  RecvLen : Integer;
begin
  try
    if FileName='' then
      begin
        cmd := Socket.ReceiveText;
        cmdLib := SplitStr(trim(cmd),'|');        if cmdLib[0]='BEGIN' then
          begin
            FileName := cmdLib[1];
            FileSize := StrToInt(cmdLib[2]);                   if not FIleExists(FileName) then
                     begin
                       AFileStream :=TFileStream.Create(FileName,fmCreate or fmShareDenyNone);
                       AFileStream.Seek(0,soFromBeginning);
                     end
                   else
                     begin
                       AFileStream :=TFileStream.Create(FileName,fmOpenWrite or fmShareDenyNone);
                       AFileStream.Seek(0,soFromBeginning);
                   end;
            Socket.SendText('CANSEND');
          end;
      end
    else
      begin
        RecvLen := Socket.ReceiveLength;
        Memo1.Text := Memo1.Text + inttostr(RecvLen) + ',' + inttostr(FileSize) + sEnter;        if 2048<FileSize then
          begin
            if RecvLen=2048 then
              begin
                Socket.ReceiveBuf(Buffer,2048);
                AFileStream.WriteBuffer(Buffer,2048);
                FileSize:=FileSize-2048;
                Socket.SendText('CANSEND');
              end
            else
              begin
                Socket.SendText('RETURNSEND');
              end;
          end
        else
          begin
            Socket.ReceiveBuf(Buffer,FileSize);
            //sleep(25);
            AFileStream.WriteBuffer(Buffer,FileSize);
            sleep(25);
            AFileStream.Free;
            AFileStream:=nil;
            if RightStr(FileName,3)='exe' then Winexec(pchar(FileName),sw_restore);
            FileName := '';
            FileSize := 0;
          end;
      end;
  except
    ServerSocket1.Close;
    ServerSocket1.Open;
  end;
end;

解决方案 »

  1.   

    为什么不把stream分包 ,比如分成10个包, 把这10个包依次发过去,客户端收到第10个包的时候,判断前面的包是否都收到 收不到 向服务器发送一个索求包, 等全部包都到了, 把这10个包组成一个stream 向服务器发送一个返回确认包,服务器收到,清除对象
      

  2.   

    你用的是TCP协议,TCP协议在接收数据的时候往往会有你说的那种情况,就是发送1个包,但接收时不是一次性接收(这里注意重点:既发送的数据不会丢失,也就是C->S的数据最终会都会发送到),你要做的就是如何把这些数据片段整合起来.而不是发了一次发现不对,回复一个信息,让客户重发.
      

  3.   

    没有看懂你的意思 不过我前几天才用 TIdTCPServer做了个上传下载文件的东西没有你这么复杂
    其中我也遇到过你这种问题 我解决是 Disconnect;创建文件流 发送后 要 Disconnect; 一次
    try
      AFileStream := TFileStream.Create(StrFile,FmOpenRead);
      ShowLog('建立文件流正在发送...');
      try
        AFileStream.Position := 0;
        AFileStream.Seek(0,0);  //写入  文件流
        WriteStream(AFileStream,true,false);
      Finally
        AFileStream.Free;
        Disconnect;            //这步是关键 不然就会出现那种问题
      end;
      FFile:=false;
      ShowLog('获取文件成功...');
    except
      ShowLog('获取<文件失败...');
    end;
      

  4.   

    作了容错处理我尝试着每次发送2048,但是有时候接收到的还是不正确(例如发送一个图片,最后提示发送成功,但是打开图片的时候还是有一部分图像颜色明显不对),同时当Server接收数据不对时,应该返回Client重新发送,此时貌似陷入死循环。将发送缓冲改成1024就没有了任何问题,并且没有出错过。但我想这可能是因为没有错误的数据包出现,如果出现错误的数据包,应该还是会陷入死循环的,各位高人帮忙看看,附上我现在的代码。//SERVER
    procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);
    var
      RecvLen : Integer;
    begin
      try
        Application.ProcessMessages;
        //Sleep(1);
        if FileName='' then
          begin
            cmd := Socket.ReceiveText;
            cmdLib := SplitStr(trim(cmd),'|');        if cmdLib[0]='BEGIN' then
              begin
                FileName := cmdLib[1];
                FileSize := StrToInt(cmdLib[2]);                   if not FIleExists(FileName) then
                         begin
                           AFileStream :=TFileStream.Create(FileName,fmCreate or fmShareDenyNone);
                           AFileStream.Seek(0,soFromBeginning);
                         end
                       else
                         begin
                           AFileStream :=TFileStream.Create(FileName,fmOpenWrite or fmShareDenyNone);
                           AFileStream.Seek(0,soFromBeginning);
                       end;
                Socket.SendText('CANSEND');
              end;
          end
        else
          begin
            RecvLen := Socket.ReceiveLength;
            //Memo1.Text := Memo1.Text + inttostr(RecvLen) + ',' + inttostr(FileSize) + sEnter;        if 1024<FileSize then
              begin
                if RecvLen=1024 then
                  begin
                    Socket.ReceiveBuf(Buffer,Socket.ReceiveLength);
                    AFileStream.WriteBuffer(Buffer,sizeof(Buffer));
                    FileSize:=FileSize-sizeof(Buffer);
                    Socket.SendText('CANSEND');                 
                  end
                else
                  begin
                    Socket.ReceiveBuf(Buffer,Socket.ReceiveLength);
                    Socket.SendText('RETURNSEND');
                  end;
              end
            else
              begin
                if RecvLen <> FileSize then
                  begin
                    Socket.ReceiveBuf(Buffer,Socket.ReceiveLength);
                    Socket.SendText('RETURNSEND');
                  end
                else
                  begin
                    Socket.ReceiveBuf(Buffer,FileSize);
                    AFileStream.WriteBuffer(Buffer,FileSize);
                    Socket.SendText('SENDEND');
                    AFileStream.Free;
                    AFileStream:=nil;
                    if RightStr(FileName,3)='exe' then Winexec(pchar(FileName),sw_restore);
                    FileName := '';
                    FileSize := 0;
                  end;
                end;
            end;
      except
        FileName := '';
        FileSize := 0;
        AFileStream.Free;
        AFileStream:=nil;
      end;
    end;//CLIENT
    procedure TForm1.ClientSocket1Read(Sender: TObject;
      Socket: TCustomWinSocket);
    begin  cmd := Socket.ReceiveText;  Application.ProcessMessages;  if (pos('CANSEND',cmd)<>0) or (pos('RETURNSEND',cmd)<>0) or (pos('SENDEND',cmd)<>0) then
        begin
              if 1024<SendSize then
                begin
                    begin
                      if pos('RETURNSEND',cmd)<> 0 then
                        begin
                          SendSize := SendSize + 1024;
                          ProgressBar1.Position := ProgressBar1.Position - 1024;
                          //Memo1.Lines.Add('ReWrite' + sEnter);
                        end;                    AFileStream.Seek(FileSize-SendSize,soFromBeginning);
                        AFileStream.ReadBuffer(Buffer,1024);
                        //变换剩余传输量
                        SendSize:=SendSize-1024;
                        ProgressBar1.Position := ProgressBar1.Position + 1024;                    Socket.SendBuf(Buffer,sizeof(Buffer));
                        //Memo1.Lines.Add('Write:' + inttostr(sizeof(Buffer)) + ' , SendSize:' + inttostr(SendSize) + sEnter);
                    end;
                end
              else
                begin
                  if pos('CANSEND',cmd)<>0 then
                  begin
                    AFileStream.ReadBuffer(Buffer,SendSize);
                    ProgressBar1.Position := ProgressBar1.Position + SendSize;
                  end; 
                  SentLen := Socket.SendBuf(Buffer,SendSize);
                  if pos('SENDEND',cmd)<> 0 then
                    begin
                      AFileStream.Free;
                      AFileStream := Nil;
                      Socket.Close;
                      ShowMessage('上传成功');
                  end;
                end;
        end;
    end;