互联网上传送出错,最后找到原因:原来每次发4k,客户端接收的时候好像一次收不完,本来我的数据都是有
包头的,可是如果客户端一次没收完,剩下来的数据客户端接收到之后由于没有包头,他不认识就给丢弃了。怎么会出现我发一个数据包,在客户端分多次到达的情况????
我把发送缓冲区调整为1K之后,传送过程就非常不错,没有出现一次错误!!
大家碰到过这种情况吗?? 如果调为1K,传送速度非常的慢,可是我又不知道将速度定为多少合适,
大家都是怎么解决这样的问题的呢??

解决方案 »

  1.   

    不是缓冲区大小的问题..仍是算编写SOCKET时候处理有问题.
    其实在VCL里面有几处可以参考..
    如果是DELPHI6及以下,可以看下面的实现.
    或者看看INDY控件组件实现.//scktcomp.PAS单元.function TCustomWinSocket.SendStreamPiece: Boolean;
    var
      Buffer: array[0..4095] of Byte;
      StartPos: Integer;
      AmountInBuf: Integer;
      AmountSent: Integer;
      ErrorCode: Integer;  procedure DropStream;
      begin
        if FDropAfterSend then Disconnect(FSocket);
        FDropAfterSend := False;
        FSendStream.Free;
        FSendStream := nil;
      end;begin
      Lock;
      try
        Result := False;
        if FSendStream <> nil then
        begin
          if (FSocket = INVALID_SOCKET) or (not FConnected) then exit;
          while True do
          begin
            StartPos := FSendStream.Position;
            AmountInBuf := FSendStream.Read(Buffer, SizeOf(Buffer));
            if AmountInBuf > 0 then
            begin
              AmountSent := send(FSocket, Buffer, AmountInBuf, 0);//注意,这儿的返回值很重要.
              if AmountSent = SOCKET_ERROR then
              begin
                ErrorCode := WSAGetLastError;
                if ErrorCode <> WSAEWOULDBLOCK then
                begin
                  Error(Self, eeSend, ErrorCode);
                  Disconnect(FSocket);
                  DropStream;
                  if FAsyncStyles <> [] then Abort;
                  Break;
                end else
                begin
                  FSendStream.Position := StartPos;
                  Break;
                end;
              end else if AmountInBuf > AmountSent then
                FSendStream.Position := StartPos + AmountSent
              else if FSendStream.Position = FSendStream.Size then
              begin
                DropStream;
                Break;
              end;
            end else
            begin
              DropStream;
              Break;
            end;
          end;
          Result := True;
        end;
      finally
        Unlock;
      end;
    end;
      

  2.   

    不管什么控件都会有这个问题,tcp协议本来就存在粘包的问题,就像jadeluo所说,接收端要负责处理重新组包的工作,一般的做法是一个线程只管接收数据,接收到的数据不作处理直接扔进缓冲区,然后另外一个线程不断从缓冲区读取完整的数据包进行处理,如果碰到不完整的包则等待下次数据到来,直到又形成了一个完整数据包,处理完之后再将数据从缓冲区移除,周而复始
      

  3.   

    俺记得TCP/IP包重组是在OS级完成的
      

  4.   

    csdn怎么搞的,发不上来??clasj的思路好像可以行的通,现在没时间弄那个了,等过段时间我再看看.我直接用socketClient和socketServer传送数据的
      

  5.   

    tcp/ip 是没有问题的, borland socket 组件也是没有问题的记得用 tcp 传输数据时是有个 MTU 大小的(宽带一般为1492), 大于这个尺寸的数据会分几次发出, tcp 本身是流式传输, 并没说数据到达是连续的, 保证的只是数据可靠性, 也就是说你发 100k, 可能会分几次收到, 但每次收到的是接上次的, 所以要在收数据时就知道要收多少或者数据有个结束符(比方telnet用的是CRLF), 应该写一个 while 保证数据全收到/发出定义个结构用于发文件TFileHdr = record, 结构得小于576字节, 应该 tcp/ip 的最小包是 576
    ...
    date/time 什么的等等
    ...
      Size: Int64;
    end;ok.. send 部分... 
    SendBuf(FileHdr, sizeof(TFileHdr));
    RemainBytes := FileHdr.Size;
    while RemainBytes <> 0 do
    begin
      RemainBytes := RemainBytes - SendBuf(内容) 
    end;
      

  6.   

    http://www.2ccc.com/article.asp?articleid=2870看看这个例子哦
      

  7.   

    我的程序也是一问一答的模式,非阻塞性。
    我想一问一答的模式也是可以行的通的。
    而你所说的第2种情况是不会出现的。     
         while size > 0 do
            begin
            SendCount:=Socket.SendBuf(buf,size);
            if SendCount < 0 then Continue;
            size:= size -SendCount;
          end;SendCount 是实际发送的大小,它不一定等于size ,尤其是在互联网上网络不好的情况下。所以最好循环发送,知道发完为止。
      

  8.   

    不好意思,刚刚说的话收回,我理解错了
    jadeluo(秀峰)说得很对
      

  9.   

    为什么要用循环发送?非这样不行吗?
         while size > 0 do
            begin
            SendCount:=Socket.SendBuf(buf,size);
            if SendCount < 0 then Continue;
            size:= size -SendCount;
          end;我现在采用客户端收到数据进行重组的问题,但发现收到的数据不是发送方的数据.是不是我的发送方的代码有问题?
    我是直接一次发送缓冲区内所有的内容,可能很大,也可能很小,我现在用的文件就413字节,但发到客户端接收到的不是原内容,郁闷呀!!
                fLength:=GetFileLen(ExtractFilePath(application.ExeName) + 'data\' + downList.Strings[i]);
                FileHandle:=FileOpen(ExtractFilePath(application.ExeName) + 'data\' + downList.Strings[i],fmOpenReadWrite);   //因为一个文件多次激发该事件,所以不能在此删除文件,删除文件的控制在发送DownRequ命令之前
                FileSeek(fileHandle,0,0);
                setLength(fileBuf,fLength);
                FileRead(fileHandle,fileBuf[0],fLength);
                FileClose(fileHandle);
                //
                strFormatFName:=downList.Strings[i] + '                    ';
                strFormatFName:=leftstr(strFormatFName,30);
                setLength(sendBuf,40 + fLength);
                CopyMemory(sendBuf,PChar('@^NEWFILE:' + strFormatFName),40);
                pSendBuf:=sendBuf;
                pSendBuf:=pointer(Integer(pSendBuf) + 40);
                CopyMemory(pSendBuf,fileBuf,fLength-1);
                Socket.SendBuf(sendBuf,fLength);
                if(i=downList.Count-1) then Socket.SendText('@^NEWFILE:');
                status.Panels[2].Text:='正在发送新文件:' + downList.Strings[i];
                fileBuf:=nil;
                sendBuf:=nil;
      

  10.   

    也就是说我上面的程序使用Socket.SendBuf一次性发送了一整个文件的内容,然后我在客户端进行重组接收,可是接收到的内容为什么与发送方的不一致!!!???这种工作模式有问题吗?至少数据应该是一样的呀.我原来用的一问一答的方式,直接发送一个文件好像接收端接收也有这问题,后来按固定大小发送,接收端就没有问题了.
    现在我不想把数据分包,有人能告诉我发送方为什么不分包发送,客户端接收重组的时候数据是错的原因吗?
      

  11.   

    发现问题了,是我写的sendBuf有问题,高手能不能讲一下这是什么原因,我都迷糊了!!!Socket.SendBuf(sendBuf,fLength);Socket.SendBuf(sendBuf[0],fLength);
    我怎么知道
    sendBuf,sendBuf[0]还有区别吗?另外接收的时候,为什么还要带^,这是什么意思?
    pBuffer:Pointer;
    Socket.ReceiveBuf(pBuffer^,recvLen);
      

  12.   

    不知道你的Sendbuf是什么数据类型?不知道参数前面的修饰符? const var?
    看看帮助吧另外Socket使用阻塞模式时数据是一次发送或者接收完成的,至少我们可以这么认为。
      

  13.   

    现在发现socket传送文件需要解决的两个问题:
    1)粘包问题
    2)延时重发问题
    粘包我想就不用说了,例如需要传多个文件,需要自己定协议,重组接收缓冲区,判断文件分隔的地方.
    现在又发现了TCP的延时重发问题,就是说tcp仅仅保证数据发送的正确性,而不保证顺序.如果服务端发现前面的数据发错了,会重发那些错误数据,而客户端接收的时候,根本就没法判断哪些是错误数据,哪些是正确数据.而且重发的那些数据并不一定紧跟在出错的数据之后就发过来,而是有可能出错之后接着发送有效数据,过一阵子,服务器端突然又把前面出错的数据给重发过来了,晕啊!!!!传送文件就这么难写吗????  
      

  14.   

    INTERNET上传输的话1。循环收,
    2。BUF设置512最好,