Results 1 to 32 of 32

Thread: IOCP Server - VB6

  1. #1

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

    IOCP Server - VB6

    The attached server program is my first attempt at using IOCP {Input Output Completion Port). It is based on one submitted by xiaoyao, but I am not sure who deserves the credit as it was a repost.

    To be effective at fully serving the needs of a high usage server, IOCP needs to be used in Overlapped mode as well as Multi Threaded mode. Overlapped I/O in non-blocking or asynchronous mode using multiple threads based on the number of CPU cores available, delivers the highest throughput according to Microsoft. My own experience so far supports that. Unfortunately there is very little information out there on utilizing this function with VB6. What I could find was information about C++ and .NET implementations with echo servers, and this information was not very helpful. Stepping through the program to identify problems proved to be next to impossible without the IDE crashing, so I was left with a lot of trial and error solutions to fix bugs.

    Although this program is currently designed to use non-encrypted data, the intention is to eventually add encryption. To this end the first transmission after connecting to the server is a simple Client Hello ("Hello") using a 5 byte header. Overlapped I/O apparently has the ability to split the data into separate buffers (eg. Header/Body), but I have not yet attempted to implement that.

    Overlapped I/O uses a fixed length buffer along with a length variable. Therefore it is not necessary to clear the buffer on each pass. The size of that buffer is configurable, and should be adjusted to suit the application. An application that hosts many connections with relatively short messages should have a smaller buffer than one that exchanges larger messages. This has to be balanced against operations such as encryption. Longer buffers take more processor power than shorter buffers. For this particular application, I have chosen 8,192 bytes as a starting point.

    When a request is passed to a thread with a WSARecv or WSASend, the request is directed to the appropriate thread defined by ComKey, which is the Completion Key being used as the index to the connection array.

    Sending encrypted files over a network requires messages to be sent as data defined by a header that can be decrypted as a complete block of data as defined in:
    https://www.vbforums.com/showthread....13-VB6-GetFile

    This worked well until I started experimenting with high speed server traffic over a Local Area network using IOCP in the IDE. File transfers must store the file blocks as they are received and decrypted to avoid having to store the entire file in memory before storage. Normally, network speed is slower than program speed, but in this scenario, the opening of the file for download was slow enough to only save the first record. Subsequently I have implemented an option to open the file in the client prior to starting the download.

    Because much of the work is done by the API, the code is relatively short in comparison to my previous attempts at server networking. The downside is that it is also less flexible. The same buffer is used for sending as receiving. We signal the buffer to receive data by applying a "WSARecv". An endless loop in the worker thread detects incoming traffic with GetQueuedCompletionStatus (GQCS), the index for the connection is recovered, and the recovered bytes are made available. Whether or not the buffer is returned before it is complete is unknown at this time. To change from receiving to sending, we apply a "WSASend" with the bytes to be sent in "PerData(nPerIndex).Buf" and the number of bytes in "PerData(nPerIndex).Len". This program currently does not support IPv6.

    That's about the gist of it, with some changes to file I/O. Using File API, I was able to improve speed and provide for automatic detection of file existence and prompting the operator before overwriting.

    There will probably be some bugs to fix. Feedback is welcome.

    J.A. Coutts

    Note: IOCP_Svr.zip & GetFile.zip have been restored. See post #15 for details
    Update: GetFile has been updated. See post #26 for details
    Update: (8/16/2024) IOCP_Svr has been updated. See post #30 for details
    Attached Files Attached Files
    Last edited by couttsj; Aug 16th, 2024 at 04:05 PM.

  2. #2

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

    Re: IOCP Server - VB6

    A problem has already surfaced. When I compile the server program and attempt to run it, it crashes after initialization. I could really use some help with this one, because my experience with Multi Threading is very limited.

    J.A. Coutts

  3. #3

  4. #4

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

    Re: IOCP Server - VB6

    Quote Originally Posted by The trick View Post
    Use this module for threading.
    I commend you for the work you have done on multi threading in VB6, but in this particular instance that module is far more than what I need. The threads do not have to communicate with each other or any other controls, and do not need to be marshalled. I simply need to make them work fast and efficiently when compiled.

    J.A. Coutts

  5. #5
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,756

    Re: IOCP Server - VB6

    Quote Originally Posted by couttsj View Post
    I commend you for the work you have done on multi threading in VB6, but in this particular instance that module is far more than what I need. The threads do not have to communicate with each other or any other controls, and do not need to be marshalled. I simply need to make them work fast and efficiently when compiled.

    J.A. Coutts
    Okay, then don't be surprised when you get crashes The module provides "flat" threads as well when you use vbCreateThread.

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

    Re: IOCP Server - VB6

    Quote Originally Posted by couttsj View Post
    I simply need to make them work fast and efficiently when compiled.
    The point is that first you have to make them *work at all* when compiled.

    cheers,
    </wqw>

  7. #7

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

    Re: IOCP Server - VB6

    Quote Originally Posted by The trick View Post
    Okay, then don't be surprised when you get crashes The module provides "flat" threads as well when you use vbCreateThread.
    Well, you are right. After more research, the standard EXE that VB6 produces apparently marshals all the threads into one. The ActiveX EXE does not. Also, I checked the IDE version to see if all the threads were being used, and they are. But the IDE crashes when I attempt to transfer multiple records over anything but the first connection.

    How does one tell if more than one CPU core is being used?

    J.A. Coutts

  8. #8
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,756

    Re: IOCP Server - VB6

    It's better to make an AxDll (apartment threading) and create an object for each IOCP thread. I wouldn't recommend to use Std-EXE threading because you lose the debugging ability (each IOCP thread sleeps in most time). Of course you could use a small asm thunk (the cycle with GetQueuedCompletionStatus call) to transmit the data from an IOCP thread to the main thread using my module as well. Regarding to AxDll approach, i had the experience with such approach when i needed to run several instances of ActiveScripting engines simultaneously:

    Code:
    Public Function ThreadProc( _
                    ByRef tData As tThreadData) As Long
        Dim hWnd    As Long
        Dim tMsg    As MSG
        Dim lRet    As Long
        Dim pObj    As Long
        
        If tData.hr < 0 Then Exit Function
        
        hWnd = tData.hWnd
        pObj = tData.pObjectRaw
        
        Do
        
            lRet = GetMessage(tMsg, 0, 0, 0)
            
            If lRet = -1 Then
                ' // Error
                tData.hr = E_FAIL
                Exit Do
            ElseIf lRet = 0 Then
                ' // Exit message queue
                Exit Do
            Else
                TranslateMessage tMsg
                DispatchMessage tMsg
            End If
            
        Loop While True
            
        If tData.pObjectRaw Then
            vbaObjSetAddref pObj, ByVal 0&
        End If
            
    End Function
    
    Public Function ThreadInitProc( _
                    ByRef tData As tThreadData) As Long
        Dim hr      As Long
        Dim tClsId  As UUID
        
        ' // Create communication window
        tData.hWnd = CreateWindowEx(0, THREAD_WND_CLASS, vbNullString, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, tData.hInstance, ByVal 0&)
        
        If tData.hWnd = 0 Then
            hr = E_FAIL
            GoTo exit_proc
        End If
        
        tData.lThreadID = GetCurrentThreadId()
        tData.hThread = OpenThread(SYNCHRONIZE, 0, tData.lThreadID)
        
        hr = CLSIDFromProgID(tData.sProgID, tClsId)
        If hr < 0 Then GoTo exit_proc
        
        ' // Create object
        hr = CoCreateInstance(tClsId, Nothing, CLSCTX_INPROC_SERVER, IID_IUnknown, tData.pObjectRaw)
        If hr < 0 Then GoTo exit_proc
        
        ' // Marshal
        hr = CoMarshalInterThreadInterfaceInStream(tData.tIID, ByVal tData.pObjectRaw, tData.pStream)
        If hr < 0 Then GoTo exit_proc
        
        tData.bInitialized = True
        
    exit_proc:
        
        If hr < 0 Then
            
            If tData.pStream Then
                vbaObjSetAddref tData.pStream, ByVal 0&
            End If
            
            If tData.hThread Then
                CloseHandle tData.hThread
                tData.hThread = 0
            End If
            
            If tData.lThreadID Then
                tData.lThreadID = 0
            End If
            
            If tData.pObjectRaw Then
                vbaObjSetAddref tData.pObjectRaw, ByVal 0&
            End If
            
            tData.bInitialized = False
            
            If tData.hWnd Then
                DestroyWindow tData.hWnd
                tData.hWnd = 0
            End If
            
        End If
        
        tData.hr = hr
        
    End Function
    
    
    ...
    
    Public Sub CreateObjectInThread( _
               ByRef sProgID As String)
        Dim hr      As Long
        Dim cObj    As Object
        
        If m_tThreadData.hThread Then
            Err.Raise 5, "CThreadItem::CreateObjectInThread", "There is an active object in the thread"
        End If
    
        With m_tThreadData
        
            .sProgID = sProgID
            .tIID = IID_IDispatch
            .hInstance = App.hInstance
            .hr = 0
            .hThread = 0
            .hWnd = 0
            .lThreadID = 0
            .pObjectRaw = 0
            .pStream = 0
    
        End With
        
        hr = SHCreateThread(AddressOf ThreadProc, m_tThreadData, CTF_COINIT_STA, AddressOf ThreadInitProc)
        If hr < 0 Then Err.Raise hr
        
        If m_tThreadData.hr Then Err.Raise m_tThreadData.hr
        
        hr = CoGetInterfaceAndReleaseStream(ByVal m_tThreadData.pStream, IID_IDispatch, cObj)
        
        If hr < 0 Then Err.Raise hr
        
        Set m_cObjInThread = cObj
        
    End Sub

  9. #9

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

    Re: IOCP Server - VB6

    I have made a little progress with this application. I discovered that when a thread is created, it is executed immediately, and that presented timing issues. Setting up the listening socket does not require that code to be part of the listening thread, so it was moved outside of the thread code.

    In the IDE, it was discovered that only the first working thread was created. Using a brief delay (Sleep 10) in the working thread initialization loop solved that problem, and coincidently it solved the same issue in the executable. The executable would crash after initializing the first working thread, and now it crashes after initializing all the working threads.

    Experimenting with the executable, it would remain running if I did not initialize the working threads. So I enabled the working thread initialization routine, but commented out the bulk of the code within the routine. Low and behold, the executable stayed running. So the problem in the executable seems to be related to the size of the code. I am finding it difficult to merge IOCP with any existing VB thread code, as IOCP already handles much of it.

    J.A. Coutts

  10. #10
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    If handled well, multithreading won't crash when compiled into an exe. Is your crash problem solved now?

  11. #11
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    Quote Originally Posted by The trick View Post
    It's better to make an AxDll (apartment threading) and create an object for each IOCP thread. I wouldn't recommend to use Std-EXE threading because you lose the debugging ability (each IOCP thread sleeps in most time). Of course you could use a small asm thunk (the cycle with GetQueuedCompletionStatus call) to transmit the data from an IOCP thread to the main thread using my module as well. Regarding to AxDll approach, i had the experience with such approach when i needed to run several instances of ActiveScripting engines simultaneously:

    Code:
    Public Function ThreadProc( _
                    ByRef tData As tThreadData) As Long
        Dim hWnd    As Long
        Dim tMsg    As MSG
        Dim lRet    As Long
        Dim pObj    As Long
        
        If tData.hr < 0 Then Exit Function
        
        hWnd = tData.hWnd
        pObj = tData.pObjectRaw
        
        Do
        
            lRet = GetMessage(tMsg, 0, 0, 0)
            
            If lRet = -1 Then
                ' // Error
                tData.hr = E_FAIL
                Exit Do
            ElseIf lRet = 0 Then
                ' // Exit message queue
                Exit Do
            Else
                TranslateMessage tMsg
                DispatchMessage tMsg
            End If
            
        Loop While True
            
        If tData.pObjectRaw Then
            vbaObjSetAddref pObj, ByVal 0&
        End If
            
    End Function
    
    Public Function ThreadInitProc( _
                    ByRef tData As tThreadData) As Long
        Dim hr      As Long
        Dim tClsId  As UUID
        
        ' // Create communication window
        tData.hWnd = CreateWindowEx(0, THREAD_WND_CLASS, vbNullString, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, tData.hInstance, ByVal 0&)
        
        If tData.hWnd = 0 Then
            hr = E_FAIL
            GoTo exit_proc
        End If
        
        tData.lThreadID = GetCurrentThreadId()
        tData.hThread = OpenThread(SYNCHRONIZE, 0, tData.lThreadID)
        
        hr = CLSIDFromProgID(tData.sProgID, tClsId)
        If hr < 0 Then GoTo exit_proc
        
        ' // Create object
        hr = CoCreateInstance(tClsId, Nothing, CLSCTX_INPROC_SERVER, IID_IUnknown, tData.pObjectRaw)
        If hr < 0 Then GoTo exit_proc
        
        ' // Marshal
        hr = CoMarshalInterThreadInterfaceInStream(tData.tIID, ByVal tData.pObjectRaw, tData.pStream)
        If hr < 0 Then GoTo exit_proc
        
        tData.bInitialized = True
        
    exit_proc:
        
        If hr < 0 Then
            
            If tData.pStream Then
                vbaObjSetAddref tData.pStream, ByVal 0&
            End If
            
            If tData.hThread Then
                CloseHandle tData.hThread
                tData.hThread = 0
            End If
            
            If tData.lThreadID Then
                tData.lThreadID = 0
            End If
            
            If tData.pObjectRaw Then
                vbaObjSetAddref tData.pObjectRaw, ByVal 0&
            End If
            
            tData.bInitialized = False
            
            If tData.hWnd Then
                DestroyWindow tData.hWnd
                tData.hWnd = 0
            End If
            
        End If
        
        tData.hr = hr
        
    End Function
    
    
    ...
    
    Public Sub CreateObjectInThread( _
               ByRef sProgID As String)
        Dim hr      As Long
        Dim cObj    As Object
        
        If m_tThreadData.hThread Then
            Err.Raise 5, "CThreadItem::CreateObjectInThread", "There is an active object in the thread"
        End If
    
        With m_tThreadData
        
            .sProgID = sProgID
            .tIID = IID_IDispatch
            .hInstance = App.hInstance
            .hr = 0
            .hThread = 0
            .hWnd = 0
            .lThreadID = 0
            .pObjectRaw = 0
            .pStream = 0
    
        End With
        
        hr = SHCreateThread(AddressOf ThreadProc, m_tThreadData, CTF_COINIT_STA, AddressOf ThreadInitProc)
        If hr < 0 Then Err.Raise hr
        
        If m_tThreadData.hr Then Err.Raise m_tThreadData.hr
        
        hr = CoGetInterfaceAndReleaseStream(ByVal m_tThreadData.pStream, IID_IDispatch, cObj)
        
        If hr < 0 Then Err.Raise hr
        
        Set m_cObjInThread = cObj
        
    End Sub
    when i needed to run several instances of ActiveScripting engines simultaneously:
    Is there a test project for this? Thank you

    SHCreateThread
    vbaObjSetAddref

    ' // Marshal
    hr = CoMarshalInterThreadInterfaceInStream(tData.tIID,
    CoGetInterfaceAndReleaseStream(ByVal
    Can you talk about why you use these APIs? I've never used these things before.

  12. #12

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

    Re: IOCP Server - VB6

    The reason for developing this program is that on larger file transfers, the Client program I was using (built with SimpleSock), was not fast enough to keep up with the IOCP server program I was working on, when both programs were operating in the IDE. I wanted to NOT use multithreading on the Client program, but I could not find a way to leave the endless loop in a signaled state without using the API function "CreateThread". So I ended up with a single threaded Client program instead.

    Each record is preceded by a 5 byte header, containing the record Type, the 2 byte version, and the 2 byte record length.
    To summarize the program flow:
    - Initialize working thread.
    - Connect to server port (5150).
    - Send Client Hello (Type 1).
    - Server responds with a list of available files and length (Type 8).
    - Client adds these to the listBox.
    - Client clicks on desired file, sends it to the server (Type 8), and opens the file for receipt.
    - Server opens that file and starts sending in blocks of 8192 bytes (Type 9).
    - Client receives each block, adds it to the file and decrements the file length.
    - When the file length is less than or equal to the "BlockLen", the file is closed and the operation is complete.

    All this worked quite nicely while operating in the IDE. But when I compiled the program and ran it, it crashed as soon as it attempted to save the file. A zero length file would be left open. I suspected that the problem might be a conflict between IOCP and the VB6 file system. That is why I experimented with the Windows file API (See: https://www.vbforums.com/showthread....ndows-File-API ). That appears to be a good guess, because when I changed the program to use the API, the executable crash problem went away.

    For testing purposes, I have provided a server program that uses SimpleSock. It was easy to throw together, and SimpleSock is quite stable, but also quite slow. If you are going to use this program in the IDE for files more than a few records, be sure to disable some of the Debug.Print statements, as the Immediate window has very limited capacity.

    This is a work in progress, as is the IOCP server program. To implement IOCP in this Client program, I used a single structure to define the server, but I retained the structure array for the Client in case I wanted to allow it to support more than one download at a time. I have temporarily removed the IOCP server program as I try to resolve some of the issues in that particular program.

    J.A. Coutts
    Last edited by couttsj; Jan 26th, 2024 at 12:56 AM.

  13. #13
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    Some Chinese have achieved iocp server for webserver,websocket
    the goal of supporting 100,000 concurrency without crashing.
    You definitely need to use multithreading.

  14. #14

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

    Re: IOCP Server - VB6

    Quote Originally Posted by xiaoyao View Post
    Some Chinese have achieved iocp server for webserver,websocket
    the goal of supporting 100,000 concurrency without crashing.
    You definitely need to use multithreading.
    The need for multi threading on the Client end is highly questionable considering the amount of resources it requires. On the Server end however, it is very desirable.

    J.A. Coutts

  15. #15

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

    Re: IOCP Server - VB6

    In this latest update, the IOCP Server essentially remains the same, but with some improvements in the File I/O. GetFile is still using SimpleSock with a much improved File I/O. If the file to be downloaded already exists and the API uses the "OPEN_ALWAYS" option, the API opens the file but returns "ERROR_ALREADY_EXISTS". Should the operator choose to overwrite the file, the file is closed and reopened using the "TRUNCATE_EXISTING" option. This is similar to the way the Command Line uses the Copy command.

    If anyone can provide a better work around for the Start delays, I am all ears. Without fully understanding exactly how VB6 works, I can only guess that the problem is that the call automatically initiates the thread process, which can take longer than the API call itself depending on its size. The current process appears to be quite stable in the IDE as well as compiled.

    J.A. Coutts

  16. #16
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    Maybe it can be packaged as a WEBSOCKET purpose?

  17. #17
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    Code:
    Sub WaitEventAnsy(ByVal hEvent As Long)  '
        Do While MsgWaitForMultipleObjects(1&, hEvent, 0&, INFINITE, QS_ALLINPUT)   '??1??0????
            DoEvents
        Loop
     
    End Sub
    Code:
    Sub WaitEventByFrm(frm As Form)
     Dim hEvent As Long
        hEvent = CreateEvent(ByVal 0, 1, 0, vbNullString)   '"Event" & f2.hwnd
         
        frm.EventHandle = hEvent
        
        WaitEventAnsy hEvent
        Do While MsgWaitForMultipleObjects(1&, hEvent, 0&, INFINITE, QS_ALLINPUT)   '??1??0????
            DoEvents
        Loop
            CloseHandle hEvent
    End Sub
    Code:
    form1.frm
    
    Public EventHandle As Long
    Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
    
    If EventHandle <> 0 Then SetEvent EventHandle
    End Sub
    Last edited by xiaoyao; Jun 27th, 2024 at 08:12 AM.

  18. #18
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    Vb6 sends and receives event signals across processes, CreateEvent.

    Code:
    Dim Start As Date
    Private Declare Function ResetEvent Lib "kernel32" (ByVal hEvent As Long) As Long
    Private Declare Function CreateEvent Lib "kernel32" Alias "CreateEventA" (lpEventAttributes As Any, ByVal bManualReset As Long, ByVal bInitialState As Long, ByVal lpName As String) As Long
    Private Declare Function SetEvent Lib "kernel32" (ByVal hEvent As Long) As Long
    Private Declare Function OpenEvent Lib "kernel32.dll" Alias "OpenEventA" ( _
                                            ByVal dwDesiredAccess As Long, _
                                            ByVal bInheritHandle As Long, _
                                            ByVal lpName As String) As Long
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    
    Private Const STANDARD_RIGHTS_REQUIRED As Long = &HF0000
    Private Const SYNCHRONIZE As Long = &H100000
    Private Const EVENT_ALL_ACCESS As Long = (STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or &H3)
    
    Private Const INFINITE As Long = -1&
    Private Const QS_ALLINPUT As Long = &H4FF&
    Private Declare Function MsgWaitForMultipleObjects Lib "user32.dll" (ByVal nCount As Long, ByRef pHandles As Long, ByVal bWaitAll As Long, ByVal dwMilliseconds As Long, ByVal dwWakeMask As Long) As Long
     
     Dim hEvent As Long 
    
    Private Sub Command1_Click()
    'From ABC.EXE WaitEvent:
    Command1.Enabled = False
    Start = Now
    
        'hEvent = CreateEvent(ByVal 0, 1, 0, "eventName1")
        hEvent = CreateEvent(0&, False, False, "eventName1")
    WaitEventAnsy hEvent
    Me.Caption = "WaitEvent:" & DateDiff("s", Start, Now) & "?"
    Command1.Enabled = True
    End Sub
    
    sub WaitMessageAgain
        ResetEvent   hEvent 
        WaitEventAnsy hEvent
    end sub
    
    Private Sub Command2_Click()
    'From Client2.exe 'SetEvent,So ABC.EXE Will Get Event Message
    
        hEvent = OpenEvent(EVENT_ALL_ACCESS, False, "eventName1")
    
        If hEvent <> 0 Then
        SetEvent hEvent
        Else
        MsgBox "hEvent=0"
        End If
    End Sub
    
    Sub WaitEventAnsy(ByVal hEvent As Long)  '
        Do While MsgWaitForMultipleObjects(1&, hEvent, 0&, INFINITE, QS_ALLINPUT)   '??1??0????
            DoEvents
        Loop
    
    End Sub
    
    Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
        CloseHandle hEvent
    End
    End Sub
    Last edited by xiaoyao; Jun 27th, 2024 at 08:25 AM.

  19. #19

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

    Re: IOCP Server - VB6

    Quote Originally Posted by xiaoyao View Post
    Vb6 sends and receives event signals across processes, CreateEvent.
    Sorry, but I do not understand the connection between the CreateThread difficulty and CreateEvent.

    J.A. Coutts

  20. #20
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    Quote Originally Posted by couttsj View Post
    Sorry, but I do not understand the connection between the CreateThread difficulty and CreateEvent.

    J.A. Coutts
    . I wanted to NOT use multithreading on the Client program, but I could not find a way to leave the endless loop in a signaled state without using the API function "CreateThread". So I ended up with a single threaded Client program instead.

    I don't know exactly what function you want to do.
    Why use multithreading? What are you waiting for?

  21. #21

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

    Re: IOCP Server - VB6

    Quote Originally Posted by xiaoyao View Post
    . I wanted to NOT use multithreading on the Client program, but I could not find a way to leave the endless loop in a signaled state without using the API function "CreateThread". So I ended up with a single threaded Client program instead.

    I don't know exactly what function you want to do.
    Why use multithreading? What are you waiting for?
    The single threaded Client program is still in development and is not part of the current download.

    Threads are used to facilitate speedy network transmission using IOCP, fixed length records, and the actual length transmitted via the record header. When a thread is initialized, it has to allocate memory and load the thread process, leaving it in the signalled state using WSARecv to receive incoming data. When sending data, the data is loaded into the buffer before enabling the signalled state using WSASend.

    To setup each thread, CreateThread is used to run GetQueuedCompletionStatus (sometimes called GQCS). This is called the signalled state, as it remains in that state until it receives a signal that an I/O function has completed, whereupon it processes that I/O and waits for the next instruction.

    According to Microsoft, CreateThread can return before the thread has been enabled. In order to not crash using VB6, each thread is given 500 ms to finish loading accompanied by a form print to indicate that it is loaded and ready. This combination was discovered by trial and error, and why it works is unknown to me. Without it the program crashes, both compiled and in the IDE.

    J.A. Coutts

  22. #22
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    You can create 10 thread and suspend them if you complete the task.When the main program receives a new request, it wakes up and lets it continue to process the task.You can also send him a task instruction to call a different process.

    It can also turn all the tasks to be processed into a sequence. Processed sequentially by each multithread.

  23. #23

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

    Re: IOCP Server - VB6

    I finally got a single thread IOCP to receive a file without crashing, and the results were pretty much what I expected.
    -------------------------------------
    transfer 64 KB file over local network
    (File Open & File Close not included)

    Using SimpleSock for send and receive:
    110ms transfer complete!

    Using SimpleSock for send and IOCP for receive:
    124ms transfer complete!

    Using IOCP for send and SimpleSock for receive:
    32ms transfer complete!

    Using IOCP for send and IOCP for receive:
    doesn't complete transfer (2 records only)
    --------------------------------------
    The only conclusion I can come to is that using IOCP to receive files simply isn't worth the effort, but using IOCP to send files certainly is. One other thing that I noticed was that File Close seems to take quite a long time using IOCP for receiving. That is probably not significant if multiple threads were used.

    J.A. Coutts

  24. #24
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    How can a piece of software support 100,000 user connections? This is when multithreading is important.

  25. #25

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

    Re: IOCP Server - VB6

    Quote Originally Posted by xiaoyao View Post
    How can a piece of software support 100,000 user connections? This is when multithreading is important.
    100,000 user connections does not mean 100,000 active users. Each thread can only service 1 connection at a time, but a single thread can be made to support more than 1 connection in series. Further more the maximum number of TCP ports is 65,536. To achieve 100,000 connections, other facilities are necessary such as a server farm.

    J.A. Coutts

  26. #26

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

    Re: IOCP Server - VB6

    A bug was discovered in the GetFile program that did not save the last record. Not sure why, but the JPEG files I was testing with displayed properly without that last record.

    After correcting that flaw and fixing up a few other things, the 64KB file transferred in a time of 16ms. When both programs were run compiled, the results were the same. That simply demonstrates that the limiting factor is network speed. By way of comparison, transferring a 1,608KB file took 328ms, and a 10.7GB file took 1,705ms. These are all timed when overwriting existing files, and does not account for file buffering. For example, the 10.7GB file took 2,578ms after deleting the existing file and rebooting the client computer.

    Many of the debug statements in this version of GetFile have been commented out. If you are going to transfer relatively large files with IOCP_Svr in the IDE, it is advisable to comment out some of the debug statements there as well. The immediate window in the IDE has limited capacity, and too many debug prints will cause problems.

    J.A. Coutts

  27. #27
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    Quote Originally Posted by couttsj View Post
    The attached server program is my first attempt at using IOCP {Input Output Completion Port). It is based on one submitted by xiaoyao, but I am not sure who deserves the credit as it was a repost.

    To be effective at fully serving the needs of a high usage server, IOCP needs to be used in Overlapped mode as well as Multi Threaded mode. Overlapped I/O in non-blocking or asynchronous mode using multiple threads based on the number of CPU cores available, delivers the highest throughput according to Microsoft. My own experience so far supports that. Unfortunately there is very little information out there on utilizing this function with VB6. What I could find was information about C++ and .NET implementations with echo servers, and this information was not very helpful. Stepping through the program to identify problems proved to be next to impossible without the IDE crashing, so I was left with a lot of trial and error solutions to fix bugs.

    Although this program is currently designed to use non-encrypted data, the intention is to eventually add encryption. To this end the first transmission after connecting to the server is a simple Client Hello ("Hello") using a 5 byte header. Overlapped I/O apparently has the ability to split the data into separate buffers (eg. Header/Body), but I have not yet attempted to implement that.

    Overlapped I/O uses a fixed length buffer along with a length variable. Therefore it is not necessary to clear the buffer on each pass. The size of that buffer is configurable, and should be adjusted to suit the application. An application that hosts many connections with relatively short messages should have a smaller buffer than one that exchanges larger messages. This has to be balanced against operations such as encryption. Longer buffers take more processor power than shorter buffers. For this particular application, I have chosen 8,192 bytes as a starting point.

    When a request is passed to a thread with a WSARecv or WSASend, the request is directed to the appropriate thread defined by ComKey, which is the Completion Key being used as the index to the connection array.

    Sending encrypted files over a network requires messages to be sent as data defined by a header that can be decrypted as a complete block of data as defined in:
    https://www.vbforums.com/showthread....13-VB6-GetFile

    This worked well until I started experimenting with high speed server traffic over a Local Area network using IOCP in the IDE. File transfers must store the file blocks as they are received and decrypted to avoid having to store the entire file in memory before storage. Normally, network speed is slower than program speed, but in this scenario, the opening of the file for download was slow enough to only save the first record. Subsequently I have implemented an option to open the file in the client prior to starting the download.

    Because much of the work is done by the API, the code is relatively short in comparison to my previous attempts at server networking. The downside is that it is also less flexible. The same buffer is used for sending as receiving. We signal the buffer to receive data by applying a "WSARecv". An endless loop in the worker thread detects incoming traffic with GetQueuedCompletionStatus (GQCS), the index for the connection is recovered, and the recovered bytes are made available. Whether or not the buffer is returned before it is complete is unknown at this time. To change from receiving to sending, we apply a "WSASend" with the bytes to be sent in "PerData(nPerIndex).Buf" and the number of bytes in "PerData(nPerIndex).Len". This program currently does not support IPv6.

    That's about the gist of it, with some changes to file I/O. Using File API, I was able to improve speed and provide for automatic detection of file existence and prompting the operator before overwriting.

    There will probably be some bugs to fix. Feedback is welcome.

    J.A. Coutts

    Note: IOCP_Svr.zip & GetFile.zip have been restored. See post #15 for details
    Update: GetFile has been updated. See post #26 for details
    The link I originally sent should also be posted.
    The website I originally downloaded from has been closed.
    Like our vbforums community, 90% of the sites that survive purely on advertising have been shut down.So a little advertising fee can not maintain the server of the website and the cost of maintenance.

  28. #28

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

    Re: IOCP Server - VB6

    Quote Originally Posted by xiaoyao View Post
    The link I originally sent should also be posted.
    The website I originally downloaded from has been closed.
    Like our vbforums community, 90% of the sites that survive purely on advertising have been shut down.So a little advertising fee can not maintain the server of the website and the cost of maintenance.
    I gave yourself credit even though your original post was a repost of someone else's work. Unfortunately, that post and any other postings I could find were of very limited value because they were all about Echo Servers. What I have finally come up with bears little resemblance to an Echo Server.

    J.A. Coutts

  29. #29

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

    Re: IOCP Server - VB6

    Ran into a problem when exiting the server program in the IDE after transferring a large file (10.7 GB). The IDE itself would crash. The only function that operates upon exiting the program is shutting down the threads. For that I was using TerminateThread. MS cautions about using TerminateThread from outside of the thread itself, stating "If the target thread is allocating memory from the heap, the heap lock will not be released".

    The normal way of starting and stopping threads is to create an event and pick up the event code using WaitForSingleObject. But the threads using IOCP are not stopped and started like normal threads. The threads are put into a signaled state and simply sit there waiting for a signal to start processing a single buffer. Once processed they are programmed to enter the signaled state again. All this is complicated by the fact that VB handles much of the cleanup itself without requiring program intervention.

    So I started searching for answers. One response in StackOverflow suggested:
    --------------------------------------------------
    Safe options to kill a thread and exit could be:
    TerminateThread(thd);
    __fastfail(0);
    or
    TerminateThread(thd);
    TerminateProcess(GetCurrentProcess(), 0);
    --------------------------------------------------
    I could not find any info on using "fastFail" with VB, and a number of posts advised against it. So it was back to the old Trial and Error method again. What I found was that TerminateProcess alone solved the problem. From that I assume that TerminateThread simply kills the thread without doing any cleanup, whereas TerminateProcess (or VB6 itself) does some cleanup before killing the thread.

    Public Declare Function TerminateProcess Lib "kernel32" (ByVal hProcess As Long, ByVal uExitCode As Long) As Long

    J.A. Coutts
    Note: The download has not been updated yet.


    Addendum:
    Terminating a process has the following results:
    Any remaining threads in the process are marked for termination.
    Any resources allocated by the process are freed.
    All kernel objects are closed.
    The process code is removed from memory.
    The process exit code is set.
    The process object is signaled.
    Last edited by couttsj; Aug 8th, 2024 at 12:15 PM.

  30. #30

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

    Re: IOCP Server - VB6

    A bug in IOCP_Svr was discovered that went undetected. When the file length was less than the maximum record size (BlockLen), the server would send 2 copies of the file. GetFile did not complain, and it is presumed that it saved the file twice.

    The thread generation and termination routines were moved to the module along with updating TerminateThread to TerminateProcess. As well, a system call recovers the number of CPU processors and generates a thread for each one up to a maximum of 8.

    J.A. Coutts

  31. #31
    PowerPoster
    Join Date
    Jan 2020
    Posts
    4,180

    Re: IOCP Server - VB6

    If the task of a thread has been completed, it can be suspended directly.When you have a task, you can choose which free trips to let him recover.

  32. #32

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

    Re: IOCP Server - VB6

    Quote Originally Posted by xiaoyao View Post
    If the task of a thread has been completed, it can be suspended directly.When you have a task, you can choose which free trips to let him recover.
    IOCP threads are never completed until the program is ended. After one task is completed, that particular connection is theoretically suspended. It has to be told what to expect for the next task (WSARecv or WSASend). It has no idea when that will occur, so it should be immediately assigned a task. In the meantime that thread can service any other connections it has been assigned. If zero bytes are recovered, it is because the other end closed the socket, and our end should do the same.

    So you can see that we are never completely in charge of what the thread is doing. We cannot arbitrarily stop and start the thread.

    J.A. Coutts

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