Results 1 to 7 of 7

Thread: Decipher UDP message based on structure

  1. #1

    Thread Starter
    New Member
    Join Date
    Sep 2015
    Posts
    9

    Decipher UDP message based on structure

    Hi

    I'm a little confused by a document I have for the structure of a data packet I want to read via UDP. I have an app set up which is working at getting very basic messages in the form of a string so I know the UDP side works. Now I need to take it to the next level and receive the messages and even more so decipher this into usable information.

    I have been sent a document on the structure of the packet but I feel it's over my head so I am just looking for pointers on how to handle this. I have attached a pdf of some of the examples of the message structure. Do i create a module with this structure defined? Do I then do some sort of mask to extract the various segments?

    I'm basically trying to gauge if this is something I can learn to figure out or if I need to pay someone to just write the code for me but that feels not only expensive but also doesn't help me further my understanding.

    Thanks in advance for any help
    Attached Images Attached Images

  2. #2
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,537

    Re: Decipher UDP message based on structure

    Data comes in as packets, and packets are handled by Winsock. Winsock hands off the data portion to your program. The packets themselves are either streamed, or handled as a record by your program. A record may consist of several packets. UDP is connectionless, so if it is not normally used for streaming sensitive data. A more complete explanation can be seen here:
    https://www.vbforums.com/showthread....B6-Simple-Sock

    J.A. Coutts

  3. #3
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,536

    Re: Decipher UDP message based on structure

    Quote Originally Posted by simonwait View Post
    Do i create a module with this structure defined? Do I then do some sort of mask to extract the various segments?
    You just need a module with several "extracting primitives" -- i.e. functions which can read Long from a byte-array (in network byte order) or a fixed-length String or any other data type your documentation lists in the packet description.

    For instance this buffer management helper module

    Code:
    '--- mdMyBuffer.bas
    Option Explicit
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    Private Declare Function ArrPtr Lib "msvbvm60" Alias "VarPtr" (Ptr() As Any) As Long
    Private Declare Function IsBadReadPtr Lib "kernel32" (ByVal lp As Long, ByVal ucb As Long) As Long
    
    Public Type MyBuffer
        Data()              As Byte
        Pos                 As Long
        Size                As Long
    End Type
    
    Public Sub BufferWriteString(uOutput As MyBuffer, sValue As String)
        BufferWriteArray uOutput, StrConv(sValue, vbFromUnicode)
    End Sub
    
    Public Sub BufferWriteArray(uOutput As MyBuffer, baSrc() As Byte)
        Dim lSize       As Long
        
        With uOutput
            lSize = pvArraySize(baSrc)
            If lSize > 0 Then
                .Size = pvArrayWriteBlob(.Data, .Size, VarPtr(baSrc(0)), lSize)
            End If
        End With
    End Sub
    
    Public Sub BufferWriteLong(uOutput As MyBuffer, ByVal lValue As Long, Optional ByVal Size As Long = 1)
        Static baTemp(0 To 3) As Byte
        Dim lPos            As Long
    
        With uOutput
            If Size <= 1 Then
                BufferWriteBlob uOutput, VarPtr(lValue), Size
            Else
                lPos = .Size
                BufferWriteBlob uOutput, 0, Size
                Call CopyMemory(baTemp(0), lValue, 4)
                .Data(lPos + 0) = baTemp(Size - 1)
                .Data(lPos + 1) = baTemp(Size - 2)
                If Size >= 3 Then .Data(lPos + 2) = baTemp(Size - 3)
                If Size >= 4 Then .Data(lPos + 3) = baTemp(Size - 4)
            End If
        End With
    End Sub
    
    Public Sub BufferWriteBlob(uOutput As MyBuffer, ByVal lPtr As Long, ByVal lSize As Long)
        uOutput.Size = pvArrayWriteBlob(uOutput.Data, uOutput.Size, lPtr, lSize)
    End Sub
    
    Private Function pvArrayWriteBlob(baBuffer() As Byte, ByVal lPos As Long, ByVal lPtr As Long, ByVal lSize As Long) As Long
        Dim lBufPtr         As Long
        
        '--- peek long at ArrPtr(baBuffer)
        Call CopyMemory(lBufPtr, ByVal ArrPtr(baBuffer), 4)
        If lBufPtr = 0 Then
            pvArrayAllocate baBuffer, Clamp(lPos + lSize, 256)
        ElseIf UBound(baBuffer) < lPos + lSize - 1 Then
            pvArrayReallocate baBuffer, lPos + lSize
        End If
        If lSize > 0 And lPtr <> 0 Then
            Debug.Assert IsBadReadPtr(lPtr, lSize) = 0
            Call CopyMemory(baBuffer(lPos), ByVal lPtr, lSize)
        End If
        pvArrayWriteBlob = lPos + lSize
    End Function
    
    Public Sub BufferReadLong(uInput As MyBuffer, lValue As Long, Optional ByVal Size As Long = 1)
        Static baTemp(0 To 3) As Byte
        
        lValue = 0
        With uInput
            If .Pos + Size <= pvArraySize(.Data) Then
                If Size <= 1 Then
                    lValue = .Data(.Pos)
                Else
                    baTemp(Size - 1) = .Data(.Pos + 0)
                    baTemp(Size - 2) = .Data(.Pos + 1)
                    If Size >= 3 Then baTemp(Size - 3) = .Data(.Pos + 2)
                    If Size >= 4 Then baTemp(Size - 4) = .Data(.Pos + 3)
                    Call CopyMemory(lValue, baTemp(0), Size)
                End If
            End If
            .Pos = .Pos + Size
        End With
    End Sub
    
    Public Sub BufferReadBlob(uInput As MyBuffer, ByVal lPtr As Long, ByVal lSize As Long)
        Dim baDest()        As Byte
        
        BufferReadArray uInput, baDest, lSize
        lSize = pvArraySize(baDest)
        If lSize > 0 Then
            Call CopyMemory(ByVal lPtr, baDest(0), lSize)
        End If
    End Sub
    
    Public Sub BufferReadArray(uInput As MyBuffer, baDest() As Byte, ByVal lSize As Long)
        With uInput
            If lSize < 0 Then
                lSize = pvArraySize(.Data) - .Pos
            End If
            If lSize > 0 Then
                pvArrayAllocate baDest, lSize
                If .Pos + lSize <= pvArraySize(.Data) Then
                    Call CopyMemory(baDest(0), .Data(.Pos), lSize)
                ElseIf .Pos < pvArraySize(.Data) Then
                    Call CopyMemory(baDest(0), .Data(.Pos), pvArraySize(.Data) - .Pos)
                End If
            Else
                Erase baDest
            End If
            .Pos = .Pos + lSize
        End With
    End Sub
    
    Public Sub BufferReadString(uInput As MyBuffer, sValue As String, ByVal lSize As Long)
        Dim baTemp()        As Byte
        
        BufferReadArray uInput, baTemp(), lSize
        sValue = StrConv(baTemp, vbUnicode)
    End Sub
    
    Private Property Get pvArraySize(baArray() As Byte) As Long
        Dim lPtr            As Long
        
        '--- peek long at ArrPtr(baArray)
        Call CopyMemory(lPtr, ByVal ArrPtr(baArray), 4)
        If lPtr <> 0 Then
            pvArraySize = UBound(baArray) + 1
        End If
    End Property
    
    Private Sub pvArrayAllocate(baRetVal() As Byte, ByVal lSize As Long)
        If lSize > 0 Then
            ReDim baRetVal(0 To lSize - 1) As Byte
        Else
            baRetVal = vbNullString
        End If
    End Sub
    
    Private Sub pvArrayReallocate(baArray() As Byte, ByVal lSize As Long)
        If lSize > 0 Then
            ReDim Preserve baArray(0 To lSize - 1) As Byte
        Else
            baArray = vbNullString
        End If
    End Sub
    
    Private Function Clamp( _
                ByVal lValue As Long, _
                Optional ByVal lMin As Long = -2147483647, _
                Optional ByVal lMax As Long = 2147483647) As Long
        Select Case lValue
        Case lMin To lMax
            Clamp = lValue
        Case Is < lMin
            Clamp = lMin
        Case Is > lMax
            Clamp = lMax
        End Select
    End Function
    . . . can be used like this

    Code:
    Option Explicit
    
    Private Sub Form_Load()
        Dim uBuffer As MyBuffer
        
        '--- this comes from network
        Dim baRecv(0 To 99) As Byte
        baRecv(0) = 12
        baRecv(1) = 13
        baRecv(2) = 14
        BufferWriteArray uBuffer, baRecv
        
        Dim lMessageID      As Long
        Dim lVersion        As Long
        Dim sSenderID       As String
        Dim lTimestamp      As Long
        Dim lDataLength     As Long
        Dim lCrc16          As Long
        Dim baData()        As Byte
        
        BufferReadLong uBuffer, lMessageID, Size:=4
        BufferReadLong uBuffer, lVersion, Size:=2
        BufferReadString uBuffer, sSenderID, 10
        BufferReadLong uBuffer, lTimestamp, Size:=4
        BufferReadLong uBuffer, lDataLength, Size:=4
        BufferReadLong uBuffer, lCrc16, Size:=2
        BufferReadArray uBuffer, baData, lDataLength
        
        '--- reset buffer and parse PDO data
        uBuffer.Pos = 0
        uBuffer.Size = 0
        BufferWriteArray uBuffer, baData
        
        Dim lPacketType     As Long
        Dim lAlarmIndex     As Long
        Dim lAlarmStatus    As Long
        Dim lEnableIndex    As Long
        Dim lEnableStatus   As Long
        
        Do While uBuffer.Pos < uBuffer.Size
            BufferReadLong uBuffer, lPacketType, Size:=4
            Select Case lPacketType
            Case &HF600
                BufferReadLong uBuffer, lAlarmIndex, Size:=4
                BufferReadLong uBuffer, lAlarmStatus, Size:=4
                '--- ToDo: call alarm handler
            Case &HF700
                BufferReadLong uBuffer, lEnableIndex, Size:=4
                BufferReadLong uBuffer, lEnableStatus, Size:=4
                '--- ToDo: call enable handler
            Case &HFF00
                '--- ToDo: call end-of-data handler
            Case Else
                Err.Raise vbObjectError, , "Unknown PDO type (&H" & Hex$(lPacketType) & ")"
            End Select
        Loop
    End Sub
    cheers,
    </wqw>

  4. #4

    Thread Starter
    New Member
    Join Date
    Sep 2015
    Posts
    9

    Re: Decipher UDP message based on structure

    Thanks both of you! I read and "think" I understood the post you posted couttsj. I then went on to use your code wqweto.

    I have found that I also have a few integer types - can I get away with using the BufferReadLong for this or should I be using a BufferReadInteger? Also just before the Case iPacketType there is a do while ubuffer.pos<ubuffer size however both those have been changed to equal 0 a few lines earlier? Am I misunderstanding something there?

    Cheers

  5. #5
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,155

    Re: Decipher UDP message based on structure

    Variable Size is important. Longs are 4 byte reads, integers are 2 byte reads.
    If you gobble up to many bytes with one read you will lose your alignment and start grabbing data from other fields.

    I use one of the two following classes for these tasks:

    https://github.com/dzzie/libs/blob/m...emBuf.cls#L451

    https://github.com/dzzie/libs/blob/m...CMemStruct.cls

  6. #6
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,537

    Re: Decipher UDP message based on structure

    The best way to handle unknown data is approach it as byte data. That way you are not dependant on how the operating system might interpret the raw data.

    J.A. coutts

  7. #7
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    5,536

    Re: Decipher UDP message based on structure

    Quote Originally Posted by simonwait View Post
    Thanks both of you! I read and "think" I understood the post you posted couttsj. I then went on to use your code wqweto.

    I have found that I also have a few integer types - can I get away with using the BufferReadLong for this or should I be using a BufferReadInteger? Also just before the Case iPacketType there is a do while ubuffer.pos<ubuffer size however both those have been changed to equal 0 a few lines earlier? Am I misunderstanding something there?

    Cheers
    Using Integer for practically *anything* in VB6 is suboptimal. You can of course come up with BufferReadInteger but keep in mind that this will be very inconvenient to handle *unsigned* 16-bit values (which is usually the case with network protocols) but keeping unsigned 16-bit values in Longs is the way to go.

    Keep in mind that currently BufferReadLong as implemented expects integral values to be persisted in network byte order (so called big endian encoding) while VB6 and x86 architecture in particular use little endianness. Your protocol documentation must explicitly state how multi-byte integral values are sent or if this is missing then you should figure it out on your own with trials and errors.

    The buffer struct consists of a byte-array, a size which keeps how much of this byte-array is actually full with data (rest of the byte-array up to UBound is still free space) and a position which keeps currently reached offset for reading.

    After resetting the buffer with .Pos = 0 and .Size = 0 there is a BufferWriteArray call which places just parsed "PDO data" in the buffer which increments its available size. In the loops reading elements from the buffer increments its current position until reaching final buffer size.

    cheers,
    </wqw>

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width