You are not logged in. You can browse in the PowerBASIC Community, but you must click Login (top right) before you can post. If this is your first visit, check out the FAQ or Sign Up.
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
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.
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
...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."
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
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.
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
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
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?
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
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
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
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
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)
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
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
We process personal data about users of our site, through the use of cookies and other technologies, to deliver our services, and to analyze site activity. For additional details, refer to our Privacy Policy.
By clicking "I AGREE" below, you agree to our Privacy Policy and our personal data processing and cookie practices as described therein. You also acknowledge that this forum may be hosted outside your country and you consent to the collection, storage, and processing of your data in the country where this forum is hosted.
Comment