Announcement

Collapse
No announcement yet.

Echo client sample in a thread??

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

  • Echo client sample in a thread??

    Is there any reason why I can't put the echo client code in a new th read?

    I built my BBSDown (Mini webserver) from that code but I think it would be better off in a thread of it's own.....

    Tx,
    Scott Turchin
    MCSE, MCP+I
    http://www.tngbbs.com
    ----------------------
    True Karate-do is this: that in daily life, one's mind and body be trained and developed in a spirit of humility; and that in critical times, one be devoted utterly to the cause of justice. -Gichin Funakoshi

  • #2
    If there are no restrictions in the documentation you should assume there are no such restrictions.

    In addition, I think somewhere in the doc it explicitly states that all PB code is in fact thread-safe. (Assumes you are using the THREAD CREATE statement to launch extra threads of execution, not trying to back-door it with direct API calls).

    As far as "better," that would be application-specific.

    MCM
    Michael Mattias
    Tal Systems (retired)
    Port Washington WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Thanks, think it had to do with the TCP_READ and those messages, I'm not sure why I assume they had to be tied into DialogProc vs ThreadProc.

      I think most TCP apps s hould have a TCP thread lest they get a hang up and convert to the white screen of death....

      Tx!
      Scott Turchin
      MCSE, MCP+I
      http://www.tngbbs.com
      ----------------------
      True Karate-do is this: that in daily life, one's mind and body be trained and developed in a spirit of humility; and that in critical times, one be devoted utterly to the cause of justice. -Gichin Funakoshi

      Comment


      • #4
        ...TCP_READ and those messages, I'm not sure why I assume they had to be tied into DialogProc
        DialogProc? Oh, there is a user interface screen in this application?

        There's an old rule, bit a good one, in Mr. Petzold's book called "the 1/10 second rule." This rule states a separate thread of execution should be used -or at least considered - when "something" may take more than 1/10 second to complete.

        Then there's the rule from the school of hard knocks which says, "if you need your screen to be updated whilst a function executes, you need to run that function in a separate thread of execution."

        Sounds like your application qualifies under both rules. Given that, you may find this demo handy:
        GUI + Worker Thread + Abort Demo 11-24-07

        (That uses DDT syntax for the screen but should be easily portable to SDK-style syntax).

        MCM
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          Yes, there is a screen,just a user interface

          - and here in lies the problem I expected:

          CALLBACK FUNCTION REQUIRED:

          Code:
          '==========================================================================================
          Function TCPThread() As Long
          Local hThread   As Long
          Local lResult As Long
          Local MyThread  As Long
          MyThread = 1
          Thread Create TCPThreadProc(ByVal MyThread) To hThread
          Thread Close hThread To lResult
          'WaitForSingleObject hThread,%INFINITE
          Function = hThread
          End Function
          '==========================================================================================
          
          
          Function TCPThreadProc() As Long
          Static hTcp     As Dword
          Static nServer  As Long
          Static nServerIP As Long
          Static tcpCount As Long
          
          Local lResult   As Long
          Local sBuffer   As String
          Local sPacket   As String
          Local sTmp      As String
          Local lHost     As String
          
          Select Case CbMsg
          Scott Turchin
          MCSE, MCP+I
          http://www.tngbbs.com
          ----------------------
          True Karate-do is this: that in daily life, one's mind and body be trained and developed in a spirit of humility; and that in critical times, one be devoted utterly to the cause of justice. -Gichin Funakoshi

          Comment


          • #6
            If the user interface is just a little progress display (user can't really do anything), I might use something like..

            Code:
            FUNCTION WinMain 
            
               DIALOG NEW  .......  to hDlg    
                 Control add stufff
            
                THREAD CREATE  TCPFunction (BYVAL hDlg), SUSPEND to hThread
                DIALOG SET USER hDlg, 1, hThread 
                DIALOG SHOW somehow   CALL DialogProc 
                Message loop if somehow=modeless
            
            END FUNCTION
            
            FUNCTION TCPFunction (BYVAL hDlg AS LONG ) AS LONG 
            
                TCP Stuff
                to update display 
                  CONTROL SET TEXT hDlg , %ID_CONTROL, "progress message" 
                  ' all done 
                  DIALOG POST hDlg, %WM_CLOSE, %NULL, %NULL 
            
            END FUNCTION
            
            CALLBACK FUNCTION DialogProc 
            
             LOCAL hThread 
            
             SELECT CASE msg 
                  CASE WM_INITDIALOG 
                       ' do necessary stuff here 
                       DIALOG GET USER CBHNDL, 1 to hThread
                       THREAD RESUME hThread 
            
                CASE %WM_SYSCOMMAND 
                      if CBWPARAM AND &hFFF0 = %SC_CLOSE THEN 
                           DIALOG GET USER  CBHNDL, 1 TO hThread 
                           THREAD STATUS  hThread to iStatus 
                            if iStatus =  %STILL_RUNNING (    &h103???) THEN 
                                MSGBOX "Can't quit now, TCP function still running 
                                FUNCTION = %TRUE     ' don't allow default action
                                EXIT FUNCTION
                            end if 
                     end if 
            
              CASE WM_CLOSE
                    DIALOG END CBHNDL, some_return_value  
            
            
            
            ....
            Something like that anyway. That should get you started.

            (Look ma, no GLOBALs!)


            MCM
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Michael
              You really should stop posting on internet subjects.
              Scott
              Once a TCP server is opened control is passed to the OS (Winsock) which is a non blocking call so you program can either continue or not tie up CPU time waiting for a connection thus it needs a callback function to activate and do the work when a connection is made. In the PB TCP functions this is done by specifying the handle of the window that created the TCP socket and a specific message number (user defined) that is sent to the window callback on activation. So creating a thread as in Michaels code is useless as the real work is done in the dialogs callback not the thread
              Using threads depends on your reason for doing so.
              If your reason is to stop window freezes as MCM is talking about then your thread should have its own window or dialog (which is normally hidden) for its callback to receive the messages from winsock and do the required processing. In which case it would probably be better to start the thread in the original window or dialogs callback say the on receiving the initdialog message.
              If your reason is to be able to handle multiple connections at the same time then it becomes more complex as you need to open a new socket for each connection. As at this stage then it is better to start each of these in their own thread with their own hidden window/dialog
              Hope that makes sense.
              John
              Last edited by John Petty; 5 Oct 2008, 12:50 PM.

              Comment


              • #8
                >You really should stop posting on internet subjects

                That's some really outstanding advice coming from the same source as this brilliant insight:

                "Using threads depends on your reason for doing so."

                Really? Ya think?
                Michael Mattias
                Tal Systems (retired)
                Port Washington WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  Michael
                  Yes of course you are right, but thats what I get for trying to think of a reason why such an experienced and knowledgable programmer like you would have made such posts.
                  There's an old rule, bit a good one, in Mr. Petzold's book called "the 1/10 second rule." This rule states a separate thread of execution should be used -or at least considered - when "something" may take more than 1/10 second to complete.

                  Then there's the rule from the school of hard knocks which says, "if you need your screen to be updated whilst a function executes, you need to run that function in a separate thread of execution."
                  Of course Mr Petzold as always gives advice so can't disagree with that, but then you go on
                  Sounds like your application qualifies under both rules. Given that, you may find this demo handy:
                  HUH All TCP notifys like "Connect", "Accept" and "Recv" are non-blocking so maybe it's better if I leave you to explain how this application would qualify.
                  I trust you will advance my admitedly limited knowledge with your explanation.
                  John

                  Comment


                  • #10
                    Well, I can't comment on Mr. Turchin's specific program since the code is not shown, but the echo client example to which he refers does not include any TCP NOTIFY statements, so notification is moot.... which leaves....

                    .. the PB "TCP RECV" statements therein.. which are blocking calls until the timeout value is reached.

                    Were the user interface executed in the same thread context as the TCP statements, the screen would be unable to update itself, since the thread *is* blocked until 'TCP RECV' returns... and at that, a "DIALOG DOEVENTS" would be required in the TCP RECV loop to update the screen... statements not included in the example to which Mr. Turchin refers.

                    No, I'm not really an "Internet" guy, but "thread management" is not limited to Internet functions, is it?

                    MCM
                    Michael Mattias
                    Tal Systems (retired)
                    Port Washington WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #11
                      Michael
                      My apologies, I did miss reading the key word "client". Seems both of us occasionally read posts too fast.
                      Yes in that case TCP RECV is a blocking statement.
                      I was not critisising the use of a thread to stop "white screens" (use the same concept all the time myself).
                      Guess I got totally confused by where you started the thread. In a client it has to be sending something to echo so wouldn't the thread be better started in the original dialogs callback say when when a "send" button is clicked? Otherwise wouldn't you need to create another dialog within the thread and again a send button just so you could enter something to send?
                      John

                      Comment


                      • #12
                        What's the difference "who goes first?"

                        The important thing is , "GUI in one thread, TCP in another"

                        But, if you really want to see it, this demo shows running a GUI as a support to a main 'batch-type' procedure...

                        Progress Bar Dialog for PB/CC programs October 24, 2002
                        (Ignore the "PB/CC" in the thread title. This will work in a GUI program exactly the same way).






                        MCM
                        Michael Mattias
                        Tal Systems (retired)
                        Port Washington WI USA
                        [email protected]
                        http://www.talsystems.com

                        Comment


                        • #13
                          You guys are both correct. It looks to be that a modeless dialog box will be required.
                          This is the pertinent code, it's a mini webserver, user connects, I send either a customized index.html page that is refreshed every 1 minute by timer or a built in string (html page).
                          Tells the users the site is down closes connection.

                          Code:
                          CallBack Function DialogProc() As Long
                          Static hTcp     As Dword
                          Static nServer  As Long
                          Static nServerIP As Long
                          Static tcpCount As Long
                          
                          Local lResult   As Long
                          Local sBuffer   As String
                          Local sPacket   As String
                          Local sTmp      As String
                          Local lHost     As String
                          
                          Static ti           As NOTIFYICONDATA
                          
                          Select Case CbMsg 
                          
                              Case %WM_INITDIALOG
                                  'Set timer to refresh the page EVERY 1 min
                                  SetTimer CbHndl, %IDT_TIMER1,  60000, ByVal %NULL
                                  Control Handle CbHndl,%IDLABEL1 To g_lblHandle
                                  Control Handle CbHndl,%IDTEXT1 To g_txtHandle
                                  Control Handle CbHndl, %IDSTATUSBAR To g_hStatus
                                      ' Initialization handler
                                   Graphic Attach CbHndl,%IDC_GC1,ReDraw
                                   Graphic Color %Red,%Yellow
                                   Graphic Clear
                                   Graphic ReDraw
                                   Call CylonEyeThread()
                          
                          
                          
                                  'Running in System Tray?
                                  If IsTrue g_RuninSysTray Then
                                       ' ** Add tray icon
                                      ti.cbSize           = SizeOf(ti)
                                      ti.hWnd             = CbHndl
                                      ti.uID              = g_hInst
                                      ti.uFlags           = %NIF_ICON Or %NIF_MESSAGE Or %NIF_TIP
                                      ti.uCallbackMessage = %WM_TRAYICON
                                      ti.hIcon            = g_hIcon
                                      ti.szTip            = g_szMine & " " & g_szVer
                                      Shell_NotifyIcon %NIM_ADD, ti
                                      DestroyIcon ti.hIcon
                                  End If
                          
                                  If IsTrue g_StayOnTop Then 'Stay on top or not
                                      SetWindowPos CbHndl, %HWND_TOPMOST, 0, 0, 0, 0, %SWP_NOMOVE Or %SWP_NOSIZE
                                  Else
                                      SetWindowPos CbHndl, %HWND_NOTOPMOST, 0, 0, 0, 0, %SWP_NOMOVE Or %SWP_NOSIZE
                                  End If
                          
                                  If IsFalse Len(g_ServerIP) Then nServerIP = CCSServerIP(g_Port)
                          '        g_ServerIP is set at that function
                          
                          
                                  'Write to log file:
                                  UpdateLogfile "#Software: " & g_szMine & " " & g_szVer,%FALSE,%TRUE,%FALSE
                                  UpdateLogFile "#Date: " & TimeStamp(),%FALSE,%TRUE,%FALSE
                                  UpdateLogFile "#Fields: date time s-ip cs-method s-port c-ip cs(User-Agent) cs(Referer) sc-bytes cs-bytes",%FALSE,%TRUE,%FALSE
                                  nServer = FreeFile
                                  Tcp Open Server Addr nServerIP Port g_Port As nServer TimeOut g_TimeOut
                          
                                  If Err Then
                                      sBuffer = "Couldn't create socket!"
                                      Control Set Text CbHndl,%IDLABEL1, sBuffer
                                  Else
                                      Tcp Notify nServer, Accept To g_hDlg As %TCP_ACCEPT
                                      sBuffer = "Listening on port " & Format$(g_Port)
                                      Control Set Text CbHndl,%IDLABEL1, sBuffer
                                  End If
                          
                                  'LogEvent sBuffer
                                  hTcp = %INVALID_SOCKET
                                  Function = 1
                          
                              Case %TCP_ACCEPT
                                  Select Case Lo(Word, CbLParam)
                          
                                  Case %FD_ACCEPT
                                      hTcp = FreeFile
                                      Tcp Accept nServer As hTcp
                                      Tcp Notify hTcp, Recv Close To g_hDlg As %TCP_ECHO
                          
                                  End Select
                                  Function = 1
                          
                              Case %TCP_ECHO
                                  Select Case Lo(Word, CbLParam)
                          
                                  Case %FD_READ
                                      If hTcp <> %INVALID_SOCKET Then
                                          Incr tcpCount
                                          Control Set Text CbHndl,%IDTEXT1, "Connection established"
                                          Control Set Text CbHndl,%IDCONNLABELTL, Format$(tcpCount,"")
                                          ' Perform a receive-loop until there is no data left (ie, the end of stream)
                                          sBuffer = ""
                                          sPacket = ""
                                          Do
                                            Tcp Recv hTcp, 1024, sBuffer
                                            sPacket = sPacket + sBuffer
                                            If Len(sPacket) > 4096 Then Exit Do 'Buffer overflow prevention
                                          Loop Until sBuffer = "" Or IsTrue Eof(hTcp) Or IsTrue Err
                                          '---<DEBUG>-------------------------------------
                          '                Local hFile As Long
                          '                Open AppPath & "Debug.log" For Append As #hFile
                          '                Print #hFile, sPacket
                          '                Close #hFile
                                          '----------------------------------------------
                                          sPacket = Left$(sPacket,Len(sPacket)-2) & String$(50,"-")
                                          SendMessage g_txtHandle , %WM_SETTEXT, 0, StrPtr(sPacket)
                                          Call ParseAndUpdateLogfile(ByVal sPacket,ByVal hTcp) 'Updates window
                                          'local nClient as long
                                          'nClient = RemoteIp (ByVal hTcp)
                          
                                          ' Send it back!
                                          Tcp Send hTcp, "HTTP/1.1 200"
                                          Tcp Send hTcp, "Server: " & g_szMine
                                          Tcp Send hTcp, "Date: " & GetPCTimeandDate()
                                          Tcp Send hTcp, "Content-Type: text/xml"
                                          Tcp Send hTcp, "Content-Length: " & Format$(Len(g_BBSDown))
                                          Tcp Send hTcp, g_BBSDown
                                          Tcp Close hTcp
                                          hTcp = %INVALID_SOCKET
                                          'End connection here
                                      Else
                                          'LogEvent "* FD_READ Error!"
                                      End If
                          
                                  Case %FD_CLOSE
                                      Tcp Close hTcp
                                      hTcp = %INVALID_SOCKET
                          
                                  End Select
                                  Function = 1
                          Scott Turchin
                          MCSE, MCP+I
                          http://www.tngbbs.com
                          ----------------------
                          True Karate-do is this: that in daily life, one's mind and body be trained and developed in a spirit of humility; and that in critical times, one be devoted utterly to the cause of justice. -Gichin Funakoshi

                          Comment


                          • #14
                            Scott
                            This is of course a simple web server not a client. Can't think any reason it has to be modeless (the sample seems to be pre DDT) just a DIALOG END statement on whatever conditions you want the program to close. I can't see any reason for the code shown to use a thread as the thread would also need to create its own dialog to be of any value.
                            However as I previously posted if you wanted it to be able to handle multiple connections at the same time then you would start threads at your Case %FD_ACCEPT each of which would have their own Dialog (hidden) and callback to respond to the NOTIFY RECV messages for that individual connection. When the RECV is finished that dialog is ended and then the thread ends. Of course each thread can place information in the main Dialog to indicate status or whatever.
                            Actually I am a little confused as to what you are trying to do, a server should not (cannot?) push messages to a client say every minute as a web server should close the connection after every page is sent. It is up to the client (user) to request the pages on a regular basis. If the server needs to know specifically which client it is responding to then normally it would send a cookie back which the client transmits on every request
                            John

                            Comment


                            • #15
                              I think you misread what I wrote, I never said it was a client - it is a mini webserver.

                              But it does not PUSH every 1 minute, it simply re-reads the index.html file (cache control if you will) - in the event it changes....

                              IT gets a connection, sends the response, sends the html file in teh form of a string, closes the connection and sits and waits

                              I don't know if I NEED a thread, especially not for a light duty server.

                              However, being tht I tested it on XP I cannot do more than 10 concurrent connections so I'm going to load test it on my Win2k3 server when time permits. I ran 1000 connections through it 4 per second and it handled it just fine....
                              Scott Turchin
                              MCSE, MCP+I
                              http://www.tngbbs.com
                              ----------------------
                              True Karate-do is this: that in daily life, one's mind and body be trained and developed in a spirit of humility; and that in critical times, one be devoted utterly to the cause of justice. -Gichin Funakoshi

                              Comment


                              • #16
                                Yes all MS non server products have limits on the number of concurrent network connections from memory XP home is only 5, Pro is 10.
                                If you are only expecting a small number of concurrent connects from good speed network connections and the data being sent both ways is small the putting it in a thread won't gain you anything.
                                The problem occurs if (as your program can actually only service one connection at a time) the number of attempted connections reaces a point that some start getting time outs as the server cannot process them fast enough, then you move each accept along with its RECV and SENDs to their own thread (at which time probably wise to set a limit to the number of threads you allow at any one time)

                                Comment


                                • #17
                                  Originally posted by John Petty View Post
                                  Yes all MS non server products have limits on the number of concurrent network connections from memory XP home is only 5, Pro is 10.
                                  If you are only expecting a small number of concurrent connects from good speed network connections and the data being sent both ways is small the putting it in a thread won't gain you anything.
                                  The problem occurs if (as your program can actually only service one connection at a time) the number of attempted connections reaces a point that some start getting time outs as the server cannot process them fast enough, then you move each accept along with its RECV and SENDs to their own thread (at which time probably wise to set a limit to the number of threads you allow at any one time)
                                  That's what it's looking like I'm going to do - I've got a Windows 2003 server here where I run my own website on it....can do unlimited testing..

                                  So each thread needs a modeless window I'm seeing - this will be this weekends project....it "Seems" to me this would be GDI intesive...
                                  Scott Turchin
                                  MCSE, MCP+I
                                  http://www.tngbbs.com
                                  ----------------------
                                  True Karate-do is this: that in daily life, one's mind and body be trained and developed in a spirit of humility; and that in critical times, one be devoted utterly to the cause of justice. -Gichin Funakoshi

                                  Comment


                                  • #18
                                    Originally posted by John Petty View Post
                                    Scott
                                    However as I previously posted if you wanted it to be able to handle multiple connections at the same time then you would start threads at your Case %FD_ACCEPT each of which would have their own Dialog (hidden) and callback to respond to the NOTIFY RECV messages for that individual connection. When the RECV is finished that dialog is ended and then the thread ends. Of course each thread can place information in the main Dialog to indicate status or whatever.John

                                    OK Guys - Mike - John - It's time for threading this - the app works flawlessly right now - but if I received two concurrent connections things could ge ugly, or 5 or 10 concurrent connections.

                                    I understand creating the new modelss dialog and thread at the FD_ACCEPT, I'm just not sure whether the Tcp = freefile, tcp ACCEPT, etc goes in the new dialog and if so where, under WM_INITDIALOG?


                                    Thanks,
                                    Scott Turchin
                                    MCSE, MCP+I
                                    http://www.tngbbs.com
                                    ----------------------
                                    True Karate-do is this: that in daily life, one's mind and body be trained and developed in a spirit of humility; and that in critical times, one be devoted utterly to the cause of justice. -Gichin Funakoshi

                                    Comment

                                    Working...
                                    X