Results 1 to 6 of 6

Thread: VB6 - SimpleServer Stress Test

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    VB6 - SimpleServer Stress Test

    I have been fairly satisfied with the performance of SimpleSock, but even though SimpleServer is being used successfully in a couple of server routines, I have not been able to stress test it. I would like to thank dilettante for providing the test routine for MSWinSck. After modifying the routines to use SimpleSock and SimpleServer, the SimpleServer routine failed the test miserably. It ran fine when tested with the default conditions (5 Clients, 100 Sends, same machine), but when I moved the client to a different machine, it failed at 3 Clients. The mode of failure was an incorrect calculation of the record length, which left the server waiting for the rest of the record. The point at which the failure occurred was not consistent, which made it difficult to troubleshoot. This is the relative debug output from one such failure.
    -----------------------------------------------
    FD_READ 664 = (socket 3)
    InBuff 0
    OK Bytes obtained from buffer: 5840 (4 x 1460)
    Header:
    01 01 00 00 00 00 28 57 (10327)
    FD_READ 860 (socket 1)
    InBuff 5832
    OK Bytes obtained from buffer: 8192
    Header:
    87 88 89 8A 8B 8C 8D 8E
    FD_READ 664 = (socket 3)
    InBuff 3689
    OK Bytes obtained from buffer: 4495
    -----------------------------------------------
    Socket 3 received 5,840 bytes. This represents 4 packets of 1,460 bytes each. Socket 1 then received 8,192 bytes. This represents the maximum Winsock buffer size this particular machine can handle. But that buffer already contained 5,832 bytes. This is equal to what socket 3 received less the header bytes.

    To understand this situation better, I ran a packet sniffer and captured a different failure.
    Code:
    socket packet
    65460 398 - sending 10327 bytes -  1513-61 = 1452 bytes
    65460 399 - 1513-53 = 1460 bytes
    65460 401 - 1513-53 = 1460 bytes
    65460 402 - 1513-53 = 1460 bytes
    65460 404 - 1513-53 = 1460 bytes
    65460 405 - 1513-53 = 1460 bytes
    65460 407 - 1513-53 = 1460 bytes
    65460 408 - 168-53 = 115 bytes     Total = 10327 bytes
    
    65461 416 - sending 10327 bytes -  1513-61 = 1452 bytes
    65459 417 - sending 10768 bytes -  1513-61 = 1452 bytes
    65459 418 - 1513-53 = 1460 bytes
    65459 420 - 1513-53 = 1460 bytes
    65459 421 - 1513-53 = 1460 bytes
    65459 423 - 1513-53 = 1460 bytes
    65459 424 - 1513-53 = 1460 bytes
    65459 426 - 1513-53 = 1460 bytes
    65459 427 - 609-53 = 556 bytes     Total = 10768 bytes
    65461 429 - 1513-53 = 1460 bytes
    65461 437 - 1513-53 = 1460 bytes
    65461 438 - 1513-53 = 1460 bytes
    65461 440 - 1513-53 = 1460 bytes
    65461 441 - 1513-53 = 1460 bytes
    65461 443 - 1513-53 = 1460 bytes
    65461 444 - 168-53 = 115 bytes     Total = 10327 bytes
    The first transfer (socket 65460) represents a normal transfer (8 packets totaling 10,327 bytes. On the second transfer, socket 65461 received a single packet, and was then interrupted by a transfer to socket 65459. This is what caused the failure, and is a real world scenario. This is because the sending programs are 3 separate applications operating independently.

    SimpleServer originally extracted all the bytes received by a socket, and accumulated and analyzed them in the "DataArrival" routine. Any left over bytes were retained, to be included in the next record. This worked well in SimpleSock because it only supported a single socket. Even though there are independent routines for each socket (defined by Index), the variables defined within each routine appear to be common. The solution was to define those variables (RecLen/Type) as an array, and let the local buffer in the class instance (m_bRecvBuffer) accumulate bytes until the entire record is received. This uses a feature that has always existed in SimpleServer that allows you to extract a restricted number of bytes (maxLen). This is used to extract the header, which I used to define the length of the following record. If you decide that the length will include the header, a routine called PeekData was added that allows you to copy the number of bytes needed, but leave them in place.

    J.A. Coutts
    Attached Files Attached Files

  2. #2
    Fanatic Member
    Join Date
    Aug 2016
    Posts
    673

    Re: VB6 - SimpleServer Stress Test

    I am glad that you can continue this update work, may be perfect replacement ms winsock.ocx.
    Hope you can make a topic update post, perhaps easier to read.thanks .

  3. #3

    Thread Starter
    Frenzied Member
    Join Date
    Dec 2012
    Posts
    1,470

    Re: VB6 - SimpleServer Stress Test

    Quote Originally Posted by xxdoc123 View Post
    I am glad that you can continue this update work, may be perfect replacement ms winsock.ocx.
    Hope you can make a topic update post, perhaps easier to read.thanks .
    Not sure what you mean by this, but I assume that you would like to see a separate post specific to SimpleServer with an explanation of its use. I can do that when I get time.

    In the meantime, this version of the test worked because all the records used were more than 4,096 bytes. But what if 2 or more small records were recovered within the same block of data. There would only be a single "DataArrival" event, and the second record would be left stranded. This required a further adjustment to the "DataArrival" routine.
    Code:
    Private Sub SimpleServer_DataArrival(ByVal Index As Long, ByVal bytesTotal As Long)
        Dim bData() As Byte
        Dim RecHeader() As Byte
        Dim bResponse() As Byte
    GetNextRecord:
        If RecLen(Index) = 0 Then 'Remove header
            Call mServer(Index).RecoverData(8)
            RecHeader = mServer(Index).bInBuffer
            Call DebugPrintByte("Header", RecHeader)
            CopyMemory ByVal VarPtr(RecLen(Index)), RecHeader(4), 4
            RecLen(Index) = ntohl(RecLen(Index))
            RecType(Index) = RecHeader(0)
            bytesTotal = bytesTotal - 8
        End If
        Debug.Print bytesTotal, RecLen(Index)
        If bytesTotal >= RecLen(Index) Then
            Call mServer(Index).RecoverData(RecLen(Index))
            bData = mServer(Index).bInBuffer
            bytesTotal = bytesTotal - RecLen(Index)
            Select Case RecType(Index)
                Case 1 'Data
                    'Pretend we have processed Data, generate new Data response:
                    bResponse = GetData(4096 + Int(Rnd() * 8192) - 1)
                    TotalMsgs = TotalMsgs + 1
                    lblTotalMsgs.Caption = Format$(TotalMsgs, "#,##0")
                    Call AddRecHeader(1, bResponse)
                    mServer(Index).bOutBuffer = bResponse
                    mServer(Index).TCPSend
                Case Else 'Ignore record
                    'Do nothing
            End Select
            RecLen(Index) = 0
            RecType(Index) = 0
            ReDim RecHeader(0)
            If bytesTotal > 0 Then GoTo GetNextRecord
        Else
            'Wait for all the data
        End If
    End Sub
    J.A. Coutts

  4. #4
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: VB6 - SimpleServer Stress Test

    couttsj:

    In the Server project, in the ServerUI form, you have:

    Code:
    Private Function InitServer() As Boolean
        ReDim mServer(MaxClients)
        For lNum = 0 To MaxClients
            Set mServer(lNum).Callback(lNum) = Me
            mServer(lNum).IPvFlg = 4
        Next
    
    End Function
    Me in that case, invoked in a form, returns a reference to it, to that instance of ServerUI.

    But in the SimpleServer Class, in the CallBack property Set, it expects a SimpleServer object reference, not a form:

    Code:
    Friend Property Set Callback(ByVal Index As Long, ByRef RHS As SimpleServer)
    I found that when I just started to try to understand the issue discussed here.

    PS: I would have expected the IDE to raise an error because of the type mismatch, but it doesn't.

  5. #5
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: VB6 - SimpleServer Stress Test

    Quote Originally Posted by Eduardo- View Post
    couttsj:

    In the Server project, in the ServerUI form, you have:

    Code:
    Private Function InitServer() As Boolean
        ReDim mServer(MaxClients)
        For lNum = 0 To MaxClients
            Set mServer(lNum).Callback(lNum) = Me
            mServer(lNum).IPvFlg = 4
        Next
    
    End Function
    Me in that case, invoked in a form, returns a reference to it, to that instance of ServerUI.

    But in the SimpleServer Class, in the CallBack property Set, it expects a SimpleServer object reference, not a form:

    Code:
    Friend Property Set Callback(ByVal Index As Long, ByRef RHS As SimpleServer)
    I found that when I just started to try to understand the issue discussed here.

    PS: I would have expected the IDE to raise an error because of the type mismatch, but it doesn't.
    I found the reason why it doesn't raise a type mismath: the code module of the form implements the Simpleserver interface.

    Code:
    Implements SimpleServer

  6. #6
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,996

    Re: VB6 - SimpleServer Stress Test

    I didn't examine the code in extend, but as far as I can tell, the confussion raised in the other thread (about the instances of Classes sharing the variables values, what is not true) comes from this design. I'll explain:

    You are using the Class SimpleServer in two different and unrelated ways:
    1) For handling the server tasks.
    2) As an interface for the callback to the object that is the caller.

    Related to 1) you have the mServer() variable array that holds several instances of the SimpleServer class.

    Related to 2) you have the Implements SimpleServer statement.
    It means that that object, that is the ServerUI form, will implement the interface of the SimpleServer class.
    It is not actually a SimpleServer class object, so it is not an instance of the SimpleServer class, but it is a ServerUI form that implements that interface.

    It doesn't actually hold an instance to any SimpleServer class object, but a reference to the calling object, that in this case is the form ServerUI.
    Doing so you can access the members of ServerUI that are in the SimpleServer interface, like:

    Code:
    Call m_Callback.Error(...
    or

    Code:
    Call m_Callback.Connect(...
    To avoid the confussion, I suggest to use two different classes for each purpose.
    One for the normal task performed by the class, and another one for the callback interface.
    This callback interface could be named ISimpleServerCallback, for example.

    Then, the implements statement would be:

    Code:
    Implements ISimpleServerCallback
    And you need to move the procedures definitions CloseSck, Connect, ConnectionRequest, DataArrival, Etc. to the new class.

    And in the SimpleServer make the necessary changes, like:
    Code:
    Friend Property Set Callback(ByVal Index As Long, ByRef RHS As ISimpleServerCallback)
    Perhaps you won't gain anything, but a clearer design.

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