客户端向服务端发送zip文件包,由于文件比较大,客户有很多个,我采用异步连接方式,客户端先发送固定大小的字节,因为sock可能自动分包,我服务端循环读取,直到读取的字节为-1,再给客户端信息,客户端再发送,服务端再接收。本来程序做好了,再我的本机上测试也没有问题,我也向我们的老板吹牛,没有什么问题了,可是我用两台机器测试,发觉如果每次发送的包为1024字节,程序没有问题,接收的文件很正常,但我觉得发送太慢,于是改为每次发送8192或4096,这是测试,程序到没有问题,可是接收的Zip文件中的图像有一小部分的已经损坏,这下我心里凉了,今天下午就要去客户那里测试了啊,大哥们,帮忙说说为什么吧?部分程序如下:
client//准备文件流
       PRFS_SendFile := TFileStream.Create(Pb_ZipPath+trim(ls_BatchName)+'.Zip', fmOpenRead or fmShareDenyNone);
      
//发送文件名与长度
       PPFM_Send.FileName :=ls_BatchName;
       PPFM_Send.Size := PRi_LostSize ;
       CSK_Send.Socket.SendBuf(PPFM_Send^,Sizeof(PPFM_Send^)); 
procedure TFrm_Client.CSK_SendRead(Sender: TObject;
  Socket: TCustomWinSocket);
beginif PRSP_Protocol.Protocol = 'Y' then          //服务端已准备好
        begin            if PRi_LostSize > 1024 then                 //如果剩下的文件大小大于4000,
                li_ESendSize := 1024
            else
                li_ESendSize := PRi_LostSize;            try
                PRFS_SendFile.ReadBuffer(SendBuf^, li_ESendSize);
                Socket.SendBuf(SendBuf^, li_ESendSize);
            except
                On E:Exception do
                begin
                    ShowMessage(e.Message+ inttostr(li_ESendSize));
                end;
            end;            Gauge1.Progress := li_ESendSize;
            inc(PRi_Sended , li_ESendSize);
        end
else  if  PRSP_Protocol.Protocol = 'A'  then
        begin            li_ReceiveSize := PRSP_Protocol.ReceiveSize ;
            PRi_LostSize :=  PPFM_Send.Size -  li_ReceiveSize ;            PRFS_SendFile.Seek(li_ReceiveSize,soFromBeginning);
            PPFM_Send.Flag := '0';                        if PRi_LostSize > 1024 then
                li_ESendSize := 1024
            else
                li_ESendSize := PRi_LostSize;            try
                PRFS_SendFile.ReadBuffer(SendBuf^, li_ESendSize);
                Socket.SendBuf(SendBuf^, li_ESendSize);
            except
                On E:Exception do
                begin
                    ShowMessage(e.Message+ inttostr(li_ESendSize));
                end;
            end;        end else  if  PRSP_Protocol.Protocol = 'R' then
        begin
            if  (PRi_Sended  mod (2048*10)) = 0 then
            begin
                EndTime:= Now;
                if EndTime -  StartTime >0 then
                begin
                    StatusBar1.Panels[2].Text :=
                    FloatToStrF(((PRi_Sended/1024)/((EndTime - StartTime) * 24 * 60 * 60)),
                        ffFixed,18,4)+'k/s';
                end;
            end;
            li_ReceiveSize := PRSP_Protocol.ReceiveSize ;
            PRi_LostSize :=  PPFM_Send.Size -  li_ReceiveSize ;
            if  li_ReceiveSize >0 then
            begin
                PRFS_SendFile.Seek(li_ReceiveSize,soFromBeginning);
                PPFM_Send.Flag := '0';
            end;                        if PRi_LostSize > 1024 then
                li_ESendSize := 1024
            else
                li_ESendSize := PRi_LostSize;            try
                PRFS_SendFile.ReadBuffer(SendBuf^, li_ESendSize);
                Socket.SendBuf(SendBuf^, li_ESendSize);
            except
                On E:Exception do
                begin
                    ShowMessage(e.Message+ inttostr(li_ESendSize));
                end;
            end;            Gauge1.Progress := li_ReceiveSize + li_ESendSize;
            inc(PRi_Sended , li_ESendSize);
        end
        else If PRSP_Protocol.Protocol= 'O'  then
        begin            Memo2.Lines.Add('传输文件完成!');
            PRFS_SendFile.Free ;
            PPFM_Send.FileName := '';
            PPFM_Send.Size := 0;
            PRi_LostSize:= 0;
            PRi_Sended := 0;
        end;
end;

解决方案 »

  1.   

    服务端服务端
    procedure TFrm_Server.ServerSocket1ClientRead(Sender: TObject;
      Socket: TCustomWinSocket);if  PSockentPoninter(Socket.Data).flag = '1'  then
        begin        if PPFM_Send.Flag = '0' then                              //开始传输新文件
            begin
                try
                    PSockentPoninter(Socket.Data).FileName := PPFM_Send.FileName ;
                    PSockentPoninter(Socket.Data).FileSize := PPFM_Send.Size ;
                    PSockentPoninter(Socket.Data).ReceiveSize := 0;
                    PSockentPoninter(Socket.Data).Ts_File  :=
                    TFileStream.Create(Pb_RecievePath + ls_CurrentDate+'\'+ls_ClientID +
                        '\'+ls_SendID+ '\'+ls_WorkID +
                        '\'+PSockentPoninter(Socket.Data).FileName
                    + '.MS!', fmCreate or fmShareDenyNone);
                    PSockentPoninter(Socket.Data).Ts_File.Seek(0, soFromBeginning);
                    //接收文件大小成功                //记录传输信息
                    PrF_AddSendMessage(PSockentPoninter(Socket.Data));
                    PSockentPoninter(Socket.Data).flag := '2';                  //准备接收
                    //Socket.SendText('Y'); //通知客户
                    PRSP_Protocol.Protocol := 'Y';
                    PRSP_Protocol.ReceiveSize := 0;                Socket.SendBuf(PRSP_Protocol^,Sizeof(PRSP_Protocol^));
                except
                end;
            end
            else if  PPFM_Send.Flag = '1'  then                                                //传输中断的文件
            begin
                try
                      PSockentPoninter(Socket.Data).FileName := PPFM_Send.FileName ;
                      PSockentPoninter(Socket.Data).FileSize := PPFM_Send.Size ;
                      PSockentPoninter(Socket.Data).Ts_File  :=
                      TFileStream.Create(Pb_RecievePath + ls_CurrentDate+'\'+ls_ClientID +
                          '\'+ls_SendID+'\'+ls_WorkID +
                          '\'+PSockentPoninter(Socket.Data).FileName
                       + '.MS!', fmOpenReadWrite);
                      PSockentPoninter(Socket.Data).ReceiveSize := PSockentPoninter(Socket.Data).Ts_File.Size ;                  PSockentPoninter(Socket.Data).Ts_File.Seek(0, sofromend);                  ls_GuestID := PSockentPoninter(Socket.Data).GuestID;                  st_ReceiveSize.Caption  :=
                          inttostr((strtoint(st_ReceiveSize.Caption) + PSockentPoninter(Socket.Data).ReceiveSize)) ;                  PSockentPoninter(Socket.Data).flag := '2';                  //准备接收                  PRSP_Protocol.Protocol := 'R';
                      PRSP_Protocol.ReceiveSize := PSockentPoninter(Socket.Data).ReceiveSize;
                      Socket.SendBuf(PRSP_Protocol^,Sizeof(PRSP_Protocol^));
                except
                end;
                Panel1.Refresh ;
            end;
        end
        else if  PSockentPoninter(Socket.Data).flag = '2'  then
        begin
            ls_CurrentDate :=  PSockentPoninter(Socket.Data).SendDate ;
            ls_GuestID := PSockentPoninter(Socket.Data).GuestID ;
            ls_ClientID := PSockentPoninter(Socket.Data).ClientID ;
            ls_SendID   := PSockentPoninter(Socket.Data).SendID ;
            ls_WorkID := PSockentPoninter(Socket.Data).WorkID ;
    //循环接收
            repeat
                li_ReceiveSize := socket.receivelength();
                try
                    getmem(lP_ReceiveBuffer, li_ReceiveSize);
                    li_ReceiveSize := Socket.ReceiveBuf(lP_ReceiveBuffer^, li_ReceiveSize);                if  li_ReceiveSize > 0 then
                    begin                    PSockentPoninter(Socket.Data).Ts_File.Seek(0, sofromend);
                        PSockentPoninter(Socket.Data).Ts_File.WriteBuffer(lP_ReceiveBuffer^, li_ReceiveSize);
                    end;
                except
                          on e:exception do
                          begin
                            Application.MessageBox(Pchar(e.Message ),'提示',mb_OK);
                          end;
                end;
                FreeMem(lP_ReceiveBuffer);
            until (li_ReceiveSize <= 0);
            
            PSockentPoninter(Socket.Data).ReceiveSize :=
                PSockentPoninter(Socket.Data).Ts_File.Size ;        if PSockentPoninter(Socket.Data).ReceiveSize =
                PSockentPoninter(Socket.Data).FileSize then
            Begin            //准备下一次接收
                ls_FileName := PSockentPoninter(Socket.Data).FileName ;
                ls_CurrentDate := PSockentPoninter(Socket.Data).SendDate;
                PSockentPoninter(Socket.Data).FileName := '' ;
                PSockentPoninter(Socket.Data).FileSize := 0 ;
                PSockentPoninter(Socket.Data).ReceiveSize := 0;
                PSockentPoninter(Socket.Data).Ts_File.Free ;
                PSockentPoninter(Socket.Data).Ts_File := nil;
                PSockentPoninter(Socket.Data).flag := '1';            //修改文件名
                RenameFile(Pb_RecievePath +ls_CurrentDate+ '\'+ls_ClientID +
                    '\'+ls_SendID +'\'+ls_WorkID +'\'+ls_FileName
                 + '.MS!', Pb_RecievePath +ls_CurrentDate+ '\'+ls_ClientID +
                     '\'+ls_SendID +'\'+ls_WorkID +'\'+ls_FileName+'.Zip');            st_ReceiverPack.Caption  := inttostr(strtoint(st_ReceiverPack.Caption)+1);            PRSP_Protocol.Protocol := 'O';
                PRSP_Protocol.ReceiveSize :=PSockentPoninter(Socket.Data).ReceiveSize;
                Socket.SendBuf(PRSP_Protocol^,Sizeof(PRSP_Protocol^));
                Panel1.Refresh ;        end
            else
            begin            //修改数据库中的记录信息
                PRSP_Protocol.Protocol := 'A';
                PRSP_Protocol.ReceiveSize :=PSockentPoninter(Socket.Data).ReceiveSize;
                Socket.SendBuf(PRSP_Protocol^,Sizeof(PRSP_Protocol^));
                //Socket.SendText('A'+FloattoStr(PSockentPoninter(Socket.Data).ReceiveSize));
            end;
        end;procedure TFrm_Server.ServerSocket1ClientConnect(Sender: TObject;
      Socket: TCustomWinSocket);
    var
        lv_node:TListItem;
    begin    //得到一动态变量
        New(Pr_PSockentPoninter);
        Pr_PSockentPoninter.GuestAddr := Socket.RemoteAddress ;     //客户地址
        //标志 0表示下一步为验证密码
        Pr_PSockentPoninter.flag := '0';
        Socket.Data := Pr_PSockentPoninter;    Memo1.Lines.Add('客户地址: '+  Socket.RemoteAddress  +' 已连接');
        st_Connect.Caption := inttostr(strtoint(st_Connect.Caption) +1);
        Panel1.Refresh ;end;
      

  2.   

    代码太长,没细看,理论上,发送的文件格式是没有区别的,不会是zip就不行,bmp就行!系统是会限制IP包的大小的,你在程序改大,用处不大,超过了, 系统还是会拆包发送的
      

  3.   

    我做了改动
    lp_SendBuf为局部变量
     GetMem(lP_SendBuf, 1024);
                PRFS_SendFile.ReadBuffer(lP_SendBuf^, li_ESendSize);
                try
                    Socket.SendBuf(lP_SendBuf^, li_ESendSize);
                finally
                    FreeMem(lP_SendBuf) ;
                end;
    在我的本机上没有问题,如果每次发送比1024大,在internet上每次发到最后,都要出现收到的比源文件大的问题。发1024非常稳定
      

  4.   

    为什么要这么做呢??
    为什么要分片一应一答的传呢?TCP层已经保证的传输的可靠性,不应该再在应用程序这层上面做这样的事情了.
    你程序的通讯效率其实可以大大提高的.既然通讯协议这样定了,就定了吧:)
    观察到你代码中一个重要问题...
    居然楼主从来没有取Sendbuf的返回值进行判断..
    仅仅是在代码外面抓了异常(非阻塞这样的情况,这儿抓异常一点用都没有)
      

  5.   

    非阻塞模式下,Sendbuf只是写数据到本地SOCKET的内部缓冲队列,
    因此,它的调用返回是非常快的(没做什么实质的事情当然快)它有两种返回值,成功表示这块数据已经写进本地SOCKET缓冲队列,
    失败表示缓冲已满,你需要延时重发这整块数据.
    特别注意,除了返回值,这时候不会有任何异常抛出!
      

  6.   

    先谢谢halfdream(哈欠) ,我做测试后在来。
      

  7.   

    我现在已经改为
    服务端
    repeat
                li_ReceiveSize := socket.receivelength();
                try
                    getmem(lP_ReceiveBuffer, li_ReceiveSize);
                    li_ReceiveSize := Socket.ReceiveBuf(lP_ReceiveBuffer^, li_ReceiveSize);                if  li_ReceiveSize > 0 then
                    begin                    PSockentPoninter(Socket.Data).Ts_File.Seek(0, sofromend);
                        PSockentPoninter(Socket.Data).Ts_File.WriteBuffer(lP_ReceiveBuffer^, li_ReceiveSize);
                    end;
                except
                          on e:exception do
                          begin
                            Application.MessageBox(Pchar(e.Message ),'提示',mb_OK);
                          end;
                end;
                FreeMem(lP_ReceiveBuffer);
            until (li_ReceiveSize <= 0);
    客户端
    if PRi_LostSize > 8192 then
                    li_ESendSize := 8192
                else
                    li_ESendSize := PRi_LostSize;            PRFS_SendFile.ReadBuffer(SendBuf^, li_ESendSize);            repeat
                    try
                        li_SendedSize := Socket.SendBuf(SendBuf^, li_ESendSize);
                    except
                        On E:Exception do
                        begin
                            ShowMessage(e.Message+ inttostr(li_ESendSize));
                        end;
                    end;
                    if li_SendedSize <0 then
                    begin
                        Sleep(100);
                    end;
                    if li_SendedSize <>  8192 then
                    begin
                        Memo1.Lines.Add(inttostr(li_SendedSize)) ;
                    end;
                until (li_SendedSize >= 0);
    每次发8192,我本机通过,不知到在internet上能否通过,等我测试好后在来
      

  8.   

    >>Client端一次只能只能传输1024字节,为什么? 
    這是系統的設定吧, 一般系統會設置最大的ip包大小, 超過了, 就分自動分包!也可能與控件的緩衝區大小有關