Announcement

Collapse
No announcement yet.

Tcp recv

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Tcp recv

    I tried every flavor of TCP Safe Receive I could find and just seemed unable to get things to work all the time with messages larger than my stated buffer size. Out of frustration I reverted back to my API ways and in a few minutes had this which seems to work without regard to the message size.

    Code:
     Function TCPReceive(ByVal hSocket As Long, _
                        ByVal iBufferSize As Long, _
                        ByRef sReceived As String, _
                        ByRef nWinError As Long) As Long
     Local hWinSocket        As Dword
    Local lpBuffers         As WSABUF
    Local sBuffer           As String
    Local nFlags            As Long
    Local nBytesReceived    As Long
    Local nReturn           As Long                    
               
        hWinSocket = FileAttr(hSocket,2)
        sReceived = ""
        nFlags = 0
        
        Do
        
            sBuffer = Space$(iBufferSize)
            lpBuffers.dLen = iBufferSize
            lpBuffers.buf = StrPtr(sBuffer)
            nBytesReceived = 0
        
            nReturn = WSARecv (hWinSocket, ByVal VarPtr(lpBuffers), 1, ByVal VarPtr(nBytesReceived), ByVal VarPtr(nFlags), ByVal(0), ByVal(0))
            nWinError = WSAGetLastError
    
             If nWinError <> 0 Then
            
               Function = %FALSE
               Exit Function
               
            End If
             
            Select Case nBytesReceived
                 Case > 0
                     sReceived = sReceived + Left$(sBuffer,nBytesReceived)
                    
                    If nReturn = 0 Then
                    
                       Exit Do
      
                     Else
      
                         Sleep 5
                       
                    End If
                    
                Case Else
                
                    Exit Do
     
            End Select
        
        Loop                   
                        
        Function = %TRUE
                        
    End Function
    OMG! I'm now mixing PB TCP and native winsock together.....What will Mike Mattias think of me now?


    Rick Kelly
    Last edited by Rick Kelly; 13 Jan 2015, 07:31 AM.
    ------------------------------------------------------------
    sigpic

    It has come to my attention that certain dubious forces are interpolating their desires in my search for Mom, apple pie and the girl you left behind. Stop it or I'll scream...

  • #2
    I use TcpSafeReceive by Don Dickinson all the time.
    Added a progress bar.

    It never returns more than recBuff
    Code:
    IF LEN(recBuff) >= iBufferLen THEN
      EXIT DO
    END IF



    Code:
    GLOBAL ghDlg AS DWORD
    %false = 0
    %true = -1
    %statusbar1 = 0
    %progressbar1 = 0
    
    FUNCTION tcpSafeReceive(BYVAL hSocket AS LONG, BYVAL iBufferLen AS LONG, recBuff AS STRING) AS LONG
       'by Don Dickinson        Mike Doty added progressbar
       'Downloads a fixed amount of bytes with a decreasing buffer size
       DIM iLeft AS LONG
       DIM sBuffer AS STRING
       LOCAL previouspercent AS LONG  'added
       LOCAL percent AS LONG 'added
       LOCAL s AS STRING     'added
       LOCAL sContentLength AS STRING 'added
       sContentLength = FORMAT$(iBufferLen,"#,")'added
       s = SPACE$(60) 'added
        iLeft = iBufferLen
        DO
          sBuffer = SPACE$(iLeft)  'decreasing buffer
          TCP RECV hSocket, iLeft, sBuffer
           IF ERR THEN
             FUNCTION = %False
             EXIT FUNCTION
          END IF
           recBuff = recBuff + sBuffer
     
          percent = LEN(recBuff) /iBufferLen *100
          IF percent <> previouspercent THEN 'only if percent changed
            MID$(s,20) = FORMAT$(LEN(recBuff),"#,") + "  of  " + sContentLength + " bytes  (" + FORMAT$(percent) + "%" + ")    "
            STATUSBAR SET TEXT ghDlg, %statusbar1,1,0,s
            previouspercent = percent
            PROGRESSBAR SET POS ghDlg, %ProgressBar1, percent
          END IF
          IF LEN(recBuff) >= iBufferLen THEN
             EXIT DO
          END IF
          iLeft = iBufferLen - LEN(recBuff)
          SLEEP 5
       LOOP
       FUNCTION = %True
     END FUNCTION
    Last edited by Mike Doty; 13 Jan 2015, 09:52 AM.

    Comment


    • #3
      What makes it a "safe" receive; versus, what, an "unsafe" receive?

      BTW, I think you are brave.
      Michael Mattias
      Tal Systems Inc.
      Racine WI USA
      mmattias@talsystems.com
      http://www.talsystems.com

      Comment


      • #4
        I believe the safe refers to the fact that you already know the
        number of bytes that should be received and you can test
        against that value. Some download routines download, but
        do not have a check figure.

        In this original code notice that sBuffer is set to SPACE$(ileft)
        and then just before the receive it changed back to SPACE$(iBufferLen).
        I changed that to
        sBuffer = SPACE$(iLeft) 'decreasing buffer
        TCP RECV hSocket, iLeft, sBuffer

        I remember asking Don about this years ago.
        It also came up in this thread:
        http://www.powerbasic.com/support/pb...ad.php?t=29040


        Code:
         FUNCTION tcpSafeReceive(BYVAL hSocket AS LONG, BYVAL iBufferLen AS LONG, _
                                recBuff AS STRING) AS LONG
            DIM iLeft AS LONG
           DIM sBuffer AS STRING
            recBuff = ""
           iLeft = iBufferLen
            DO
              sBuffer = SPACE$(iLeft)
               ON ERROR RESUME NEXT
              sBuffer = SPACE$(iBufferLen)
              TCP RECV hSocket, iLeft, sBuffer
              IF ERR THEN
                 FUNCTION = %False
                 EXIT FUNCTION
              END IF
               recBuff = recBuff + sBuffer
              IF LEN(recBuff) >= iBufferLen THEN
                 EXIT DO
              END IF
              iLeft = iBufferLen - LEN(recBuff)
              SLEEP 5
           LOOP
           FUNCTION = %True
         END FUNCTION
        Last edited by Mike Doty; 13 Jan 2015, 10:37 AM.

        Comment


        • #5
          Oh, I see the problem. While TCP RECV will tell you it got less than the "bytes requested" it does not tell you how many it DID get.

          Seems a New Feature Suggestion is in order:

          Code:
           TCP RECV [#] fNum&, count&, Buffer$ [TO nBytereceived ] 

          MCM
          Michael Mattias
          Tal Systems Inc.
          Racine WI USA
          mmattias@talsystems.com
          http://www.talsystems.com

          Comment


          • #6
            The length of buffer after TCP RECV is the number of bytes received.

            '
            Code:
            FUNCTION PBMAIN () AS LONG
              LOCAL sData,Buffer,file, site AS STRING
              LOCAL portnumber, h AS LONG
              Site = ""
              file = "/filename.txt"
              h = FREEFILE
              TCP OPEN PORT 80 AT site AS #h TIMEOUT 10000
              IF ERR THEN  ? "Unable to contact " + site,%MB_ICONERROR,"Error" + STR$(ERR):EXIT FUNCTION
              TCP PRINT #h, "GET " & file & " HTTP/1.0"
              TCP PRINT #h, "Host: " & site
              TCP PRINT #h, "Pragma: no-cache"
              TCP PRINT #h, "Cache-Control: max-age=0"
              TCP PRINT #h, ""
              DO
                TCP RECV  #h, 80, buffer
                sdata = sdata + buffer
                ? USING$("sdata #,  buffer #",LEN(sdata),LEN(buffer))
                IF ERR    THEN          EXIT DO
                IF LEN(buffer) = 0 THEN EXIT DO
                REM IF EOF(h) THEN     EXIT DO  'do not use this!!!!
                SLEEP 5
              LOOP
              TCP CLOSE #h
              ? "Version " + REMAIN$(sdata,$CRLF & $CRLF),,"Data only
            END FUNCTION
            '
            Last edited by Mike Doty; 14 Jan 2015, 11:50 AM.

            Comment


            • #7
              Download string created with StringBuilder
              String concatenation could be extensive with sData = sData + sBuffer

              Code:
              %BytesWantedPerRECV = 32 * 4096
               
              FUNCTION PBMAIN () AS LONG 'sb.bas
               
                LOCAL Buffer,file, site AS STRING
                LOCAL portnumber,h,BytesWantedPerRECV AS LONG
                LOCAL sb AS ISTRINGBUILDERA
                sb = CLASS "StringBuilderA"
               
                PortNumber = 80
                Site = "dotysoftware.com"
                file = "/version.txt"
               
                h = FREEFILE
                TCP OPEN PORT PortNumber AT site AS #h TIMEOUT 10000
                IF ERR THEN  ? "Unable to contact " + site,%MB_ICONERROR,"Error" + STR$(ERR):EXIT FUNCTION
                TCP PRINT #h, "GET " & file & " HTTP/1.0"
                TCP PRINT #h, "Host: " & site
                TCP PRINT #h, "Pragma: no-cache"
                TCP PRINT #h, "Cache-Control: max-age=0"
                TCP PRINT #h, ""
               
                sb.clear
                DO
                  TCP RECV  #h, %BytesWantedPerRECV, buffer
                  sb.add buffer
                  IF ERR OR LEN(buffer) = 0 THEN EXIT DO
                  SLEEP 5
                LOOP
                TCP CLOSE #h
               
                ? "Version " + REMAIN$(sb.string,$CRLF & $CRLF),,"Data only
               
              END FUNCTION

              Comment


              • #8
                > The length of buffer after TCP RECV is the number of bytes received.

                Duh. I missed that, but it's clearly documented under TCP RECV..

                Typically used in a loop to retrieve a stream of data, a TCP RECV loop should be terminated if Buffer$ returns an empty string, or if ...
                If TCP RECV can return "zero bytes" it can surely return "less than was requested!"

                MCM
                Michael Mattias
                Tal Systems Inc.
                Racine WI USA
                mmattias@talsystems.com
                http://www.talsystems.com

                Comment


                • #9
                  TCPSafeReceive does not work with secure sites so the better approach is to use Jose Roca includes!!
                  His code even works when https is not specified and the site automatically routes http requests to https:
                  May only work with text based files.
                  Code:
                  #INCLUDE  "MSXML.INC" 'requires Jose Roca includes
                  FUNCTION PBMAIN () AS LONG
                    LOCAL ws AS WSTRING
                    ws = test("http://www.dotysoftware.com/version.txt")
                    ? ws
                  END FUNCTION
                  
                  FUNCTION test(wsUrl AS WSTRING) AS WSTRING
                    DIM sMethod AS WSTRING
                    DIM oXml AS IServerXMLHTTPRequest2
                    SET oXml = NEWCOM "MsXml2.ServerXMLHTTP.6.0"
                    sMethod = "GET"
                    IF ISTRUE(ISOBJECT(oXml)) THEN
                        oXml.Open(sMethod, wsURL, %FALSE)
                        oXml.Send()
                        DO WHILE oXml.ReadyState<>4
                            SLEEP 0
                            ? "[" & STR$(oXml.ReadyState) & "]"
                            ? "error exit function":EXIT FUNCTION
                        LOOP
                        FUNCTION= oXml.ResponseText
                    ELSE
                        ? "Unable to get version",,FUNCNAME$
                    END IF
                  END FUNCTION
                  Get text or any file
                  Code:
                  #INCLUDE  "MSXML.INC" 'requires Jose Roca includes
                  
                  FUNCTION PBMAIN () AS LONG
                    LOCAL sUrl,sSaveAs,sErrorMsg AS STRING, wsUrl AS WSTRING
                    sUrl = "http://www.dotysoftware.com/version.txt"
                    wsUrl = sUrl
                  
                    'Get text as display
                    ? GetText(wsUrl)
                  
                    'Get any file and save as something
                    sSaveAs = EXE.PATH$ + "junk.txt"
                    IF DownloadFile(sUrl,sSaveAs,sErrorMsg) THEN
                        ? "Saved as " + sSaveAs
                    ELSE
                        ? "Download failed"
                    END IF
                  
                  END FUNCTION
                  
                  FUNCTION GetText(wsUrl AS WSTRING) AS WSTRING
                    DIM sMethod AS WSTRING
                    DIM oXml AS IServerXMLHTTPRequest2
                    SET oXml = NEWCOM "MsXml2.ServerXMLHTTP.6.0"
                    sMethod = "GET"
                    IF ISTRUE(ISOBJECT(oXml)) THEN
                        oXml.Open(sMethod, wsURL, %FALSE)
                        oXml.Send()
                        DO WHILE oXml.ReadyState<>4
                            SLEEP 0
                            ? "[" & STR$(oXml.ReadyState) & "]"
                            ? "error exit function":EXIT FUNCTION
                        LOOP
                        FUNCTION= oXml.ResponseText
                    ELSE
                        ? "Unable to get version",,FUNCNAME$
                    END IF
                  END FUNCTION
                  
                  
                  
                  
                  FUNCTION DownloadFile(sUrl AS STRING,sFile AS STRING,sErrorMsg AS STRING) AS LONG
                  
                    LOCAL hr AS LONG
                    LOCAL pIBindStatusCallbackImpl AS IBindStatusCallbackImpl
                    pIBindStatusCallbackImpl = CLASS "CIBindStatusCallbackImpl"
                    hr = URLDownloadToFile(NOTHING, sUrl+$NUL, sFile+$NUL,0,pIBindStatusCallbackImpl)
                    IF hr THEN
                        sErrorMsg = "Error &H" & HEX$(hr, 8)
                        BEEP
                    ELSE
                      FUNCTION = 1
                    END IF
                  END FUNCTION
                  
                  CLASS CIBindStatusCallbackImpl COMMON
                  
                     INTERFACE IBindStatusCallbackImpl $IID_IBindStatusCallback
                  
                        INHERIT IUNKNOWN
                  
                        ' =====================================================================================
                        METHOD OnStartBinding ( _                            ' VTable offset = 12
                          BYVAL dwReserved AS DWORD _                        ' __in DWORD dwReserved
                        , BYVAL pib AS IBinding _                            ' __in IBinding *pib
                        ) AS LONG                                            ' HRESULT
                  
                        END METHOD
                  
                        ' =====================================================================================
                        METHOD GetPriority ( _                               ' VTable offset = 16
                          BYREF pnPriority AS LONG _                         ' __out LONG *pnPriority
                        ) AS LONG                                            ' HRESULT
                  
                        END METHOD
                        ' =====================================================================================
                        METHOD OnLowResource ( _                             ' VTable offset = 20
                          BYVAL reserved AS DWORD _                          ' __in DWORD reserved
                        ) AS LONG                                            ' HRESULT
                  
                        END METHOD
                        ' =====================================================================================
                        METHOD OnProgress ( _                                ' VTable offset = 24
                          BYVAL ulProgress AS DWORD _                        ' __in ULONG ulProgress
                        , BYVAL ulProgressMax AS DWORD _                     ' __in ULONG ulProgressMax
                        , BYVAL ulStatusCode AS DWORD _                      ' __in ULONG ulStatusCode
                        , BYREF szStatusText AS WSTRINGZ _                   ' __in LPCWSTR szStatusText
                        ) AS LONG                                            ' HRESULT
                  
                        OutputDebugString STR$(ulProgress) & STR$(ulProgressMax)
                  
                        END METHOD
                        ' =====================================================================================
                        METHOD OnStopBinding ( _                             ' VTable offset = 28
                          BYVAL hresult AS LONG _                            ' __in HRESULT hresult
                        , BYREF szError AS WSTRINGZ _                        ' __in LPCWSTR szError
                        ) AS LONG                                            ' HRESULT
                  
                        END METHOD
                        ' =====================================================================================
                        METHOD GetBindInfo ( _                               ' VTable offset = 32
                          BYREF grfBINDF AS DWORD _                          ' __out DWORD *grfBINDF
                        , BYREF pbindinfo AS BINDINFO _                      ' __in_out BINDINFO *pbindinfo
                        ) AS LONG                                            ' HRESULT
                  
                        END METHOD
                        ' =====================================================================================
                        METHOD OnDataAvailable ( _                           ' VTable offset = 36
                          BYVAL grfBSCF AS DWORD _                           ' __in DWORD grfBSCF
                        , BYVAL dwSize AS DWORD _                            ' __in DWORD dwSize
                        , BYREF pformatetc AS FORMATETC _                    ' __in FORMATETC *pformatetc
                        , BYREF pstgmed AS STGMEDIUM _                       ' __in STGMEDIUM *pstgmed
                        ) AS LONG                                            ' HRESULT
                  
                        END METHOD
                        ' =====================================================================================
                        METHOD OnObjectAvailable ( _                         ' VTable offset = 40
                          BYREF riid AS GUID _                               ' __in REFIID riid
                        , BYVAL punk AS IUNKNOWN _                           ' __in IUnknown *punk
                        ) AS LONG                                            ' HRESULT
                  
                        END METHOD
                        ' =====================================================================================
                  
                     END INTERFACE
                  
                  END CLASS








                  Last edited by Mike Doty; 28 Sep 2016, 03:01 AM.

                  Comment


                  • #10
                    TCP RECV Problem in Documentation
                    ...................................................................................................
                    Exiting a TCP RECV loop because of EOF will lose data still coming.

                    This example from the documentation does not check for EOF(hEcho)
                    Code:
                    CASE %FD_READ
                       InBuffer = ""
                       IF hEcho = %INVALID_SOCKET THEN EXIT SELECT
                       DO
                         TCP RECV hEcho, 1024, buffer
                         IF LEN(buffer) = 0 OR ISTRUE ERR THEN EXIT LOOP
                         InBuffer = InBuffer + buffer
                         TCP SEND hEcho, buffer
                         LogEvent $DQ + Buffer + $DQ
                       LOOP
                    Code:
                    The EchoServ.bas example checks for EOF and if someone follows this they may lose data
                    CASE %FD_READ
                      IF hEcho <> %INVALID_SOCKET THEN
                      ' Perform a receive-loop until there is no data left (ie, the end of stream)
                      sBuffer = ""
                      sPacket = ""
                      DO
                        TCP RECV hEcho, 1024, sBuffer
                        sPacket = sPacket + sBuffer
                      LOOP UNTIL sBuffer = "" OR ISTRUE EOF(hEcho) OR ISTRUE ERR
                      'Send it back!
                      IF LEN(sBuffer) THEN
                        TCP SEND hEcho, sPacket + " -> Received Ok!"
                      END IF
                    Receive data from a specified TCP/IP port.
                    Syntax
                    TCP RECV [#] fNum&, count&, Buffer$
                    Remarks
                    Receive count& bytes from the fNum& TCP/IP port and place them in Buffer$.
                    If count& bytes are not available, Buffer$ will receive whatever bytes are available and EOF(fNum&) will return TRUE (non-zero).
                    Typically used in a loop to retrieve a stream of data, a TCP RECV loop should be terminated if Buffer$ returns an empty , or if
                    EOF(fNum&) returns TRUE, or if ERR becomes set. If a time-out occurs, ERR will be set to indicate a run-time Error 24 ("Device timeout").
                    See TCP OPEN to specify the TCP socket timeout value.

                    Comment

                    Working...
                    X