|
Hi i currently am developing an application in which a server receives a no of connections from different clients aprroximately 20 and for each client an infinite looped thread is created which is mainly responsible for checking if the client is sending something to the server. Now if i run more than two thread my system slows down .. Is it because of the threads ?? so is there anyway by which i can control threads i.e make them less heavy on my system. ?? Some one said that I should make them event driven , any ideas + hints + suggestion how i can accomplish that?? this issue is making me crazy!!! Private Sub GetData(ByVal x As Integer) '''''''''''''''''''''This thread waits for data from the connected clients Dim myno As Integer Console.WriteLine("Listening For Incoming Data on " & x & " Connection") RETRY: Try If client(x).GetStream.DataAvailable = True Then Dim Buffer(client(x).ReceiveBufferSize) As Byte myno = client(x).GetStream.Read(Buffer, 0, CInt(client(x).ReceiveBufferSize)) If myno <> 0 And myno <> -1 Then MsgBox("Received Data") Dim mystring As String mystring = Encoding.ASCII.GetString(Buffer) MsgBox(mystring) ' Console.WriteLine(x & ":" & mystring) End If Else 'Do Nothing End If Catch ex As Exception MsgBox(ex.Message) End Try GoTo RETRY End Sub Any help or advice would be appreciated into managing threads A candle loses nothing by lighting another candle. | | Silver_Gates | With your current design, each thread constantly requires the attention of the processor to check DataAvailable. The more threads you have constantly using the processor, the less time the processor has to do other work.
A couple suggestions:
1. Don't call DataAvailable. If no data is available, Read will wait until there is data to be read. It does this in an efficient way which doesn't use the processor, unlike constantly calling DataAvailable. You will still have 20 threads going, but each thread will only need the attention of the processor when there is actually data to be handled.
2. Convert to a completely asynchronous model, using BeginRead instead of Read. Using the async pattern, you don't create a thread for each client. Instead, you call BeginRead from your application's main thread as soon as you accept the connection. BeginRead returns immediately, so the main thread can continue waiting for new connections. When data arrives, the framework will use a ThreadPool thread to run the method you passed as the callback parameter to BeginRead. This allows your server to handle thousands of clients without having to create a separate thread for each one.
(1) may be sufficient foryour simple 20-client system. If not, make the switch to (2).
You can also save some time by reusing the buffer you pass to Read instead of allocating a new one every time. This won't make a big difference if your message rates are low, but can become a big deal if clients start sending lots of data.
-dave - Marked As Answer bySilver_Gates Tuesday, September 01, 2009 12:30 AM
-
| | Dave Murray [NCL] | This loop that you have here is a busy loop, which is going to spin forever waiting for data to be ready.
In order to make this server scalable, you should convert it to an asynchronous pattern. See examples in MSDN of how to implement asynchronous reads on a network stream.
feroze
--
My blog
- Proposed As Answer byReymarx GeredaMSFT, OwnerMonday, August 31, 2009 2:26 PM
-
| | Feroze Daud | asynchronous pattern ?? could you explain a little ?? Do u mean i should use non blocking sockets??/
A candle loses nothing by lighting another candle. | | Silver_Gates | With your current design, each thread constantly requires the attention of the processor to check DataAvailable. The more threads you have constantly using the processor, the less time the processor has to do other work.
A couple suggestions:
1. Don't call DataAvailable. If no data is available, Read will wait until there is data to be read. It does this in an efficient way which doesn't use the processor, unlike constantly calling DataAvailable. You will still have 20 threads going, but each thread will only need the attention of the processor when there is actually data to be handled.
2. Convert to a completely asynchronous model, using BeginRead instead of Read. Using the async pattern, you don't create a thread for each client. Instead, you call BeginRead from your application's main thread as soon as you accept the connection. BeginRead returns immediately, so the main thread can continue waiting for new connections. When data arrives, the framework will use a ThreadPool thread to run the method you passed as the callback parameter to BeginRead. This allows your server to handle thousands of clients without having to create a separate thread for each one.
(1) may be sufficient foryour simple 20-client system. If not, make the switch to (2).
You can also save some time by reusing the buffer you pass to Read instead of allocating a new one every time. This won't make a big difference if your message rates are low, but can become a big deal if clients start sending lots of data.
-dave - Marked As Answer bySilver_Gates Tuesday, September 01, 2009 12:30 AM
-
| | Dave Murray [NCL] | That's what you mean by event-driven. Try using BeginRead method instead of Read method. http://msdn.microsoft.com/en-us/library/system.net.sockets.networkstream.beginread.aspxYou just pass the delegate which must envoke EndRead to receive the same data as would be otherwise returned by Read. The framework should handle all multithreading for you. - Proposed As Answer byOleksii Prokopchuk [NCL]MSFT, ModeratorMonday, August 31, 2009 4:44 PM
-
| | Oleksii Prokopchuk [NCL] | @Dave Murray [NCL] Thanks man for the great advise.. you definitely saved me. Anyways so I readjusted the above code which i had posted ,to the following new code
Private Sub GetData(ByVal x As Integer) 'My Never ending Thread :) Dim myno As Integer Dim File_Name As String Dim ShortString As String Console.WriteLine("Listening For Incoming Data on " & x & " Connection") RETRY: Try If client(x).Client.Connected = True Then Dim Buffer(client(x).ReceiveBufferSize) As Byte myno = client(x).GetStream.Read(Buffer, 0, CInt(client(x).ReceiveBufferSize)) If myno <> 0 And myno <> -1 Then ShortString = Encoding.ASCII.GetString(Buffer, 0, myno) '################################################ Dim fsstream As System.IO.FileStream Dim bwwriter As System.IO.BinaryWriter
Dim MyArray() As String ShortString = Encoding.ASCII.GetString(Buffer, 0, myno) MsgBox(ShortString)
If Microsoft.VisualBasic.Left(ShortString, 6) = "#Make#" Then MyArray = Split(ShortString, "#") File_Name = MyArray(UBound(MyArray)) File_Name = "d:\" & File_Name fsstream = New System.IO.FileStream(File_Name, IO.FileMode.Create, IO.FileAccess.Write, IO.FileShare.ReadWrite) bwwriter = New System.IO.BinaryWriter(fsstream) bwwriter.Close()
ElseIf Microsoft.VisualBasic.Left(ShortString, 5) = "*End*" Then fsstream = New System.IO.FileStream(File_Name, IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.ReadWrite) bwwriter = New System.IO.BinaryWriter(fsstream) bwwriter.Flush() bwwriter.Close() Else fsstream = New System.IO.FileStream(File_Name, IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.ReadWrite) bwwriter = New System.IO.BinaryWriter(fsstream) bwwriter.Write(Buffer, 0, myno) bwwriter.Flush() bwwriter.Close() End If End If Else End If Catch ex As Exception MsgBox(ex.Message) End Try GoTo RETRY End Sub
OK sO I decided to compare the results b/w my code and your and not so surprisingly your code is much better but with a little flaw . first let me tell you the results:
Your code: Results from Taskmanager (Performance Tab) Program Started :0%-3% consumption (Pretty good) :) 1st Client Connects :0%-3% consumption (Really good---just what i need) :) :) 1t Client Disconnects :58% :( [Get error msg:Unable to read data from tranport connection.an existing Connection was forcibly closed by remote Host] I guess the huge increase in memory consumption took place because of the error. i tried to use TCPclient.client.connected method to determine if the client is still connected but as you can see i still get the error message.How can i determine if the client is not available cause if i know that then i wont call the read method...but then again your advise is needed..looking forward to hearing your response.
Oh yeah my result Program Started :0%-3% consumption (Pretty good) :) 1st Client Connects :70% consumption (Really !!!@#) :) :) 1st Client DisConnects:70% consumption (same) :) :) 2md Client Connects :100% consumption ( youer code wins)
A candle loses nothing by lighting another candle. | | Silver_Gates | @ Oleksii Prokopchuk [NCL]
I think Daves suggestion is really aproachable cause I almost have it.. but i have a problem determining whether the client is still connected or not before calling the read method. Anysuggestion what could help A candle loses nothing by lighting another candle. | | Silver_Gates |
@Dave Here when the client is connected to my server thsi code is initiated myno = client(x).GetStream.Read(Buffer, 0,
CInt(client(x).ReceiveBufferSize)) //Blocking function I believe Anyways so the client disconnects and we get an error message and the system resources jum way high again resulting in the same situation as my orginal
A candle loses nothing by lighting another candle. | | Silver_Gates | @DAVE I solved my problem thanks to your help the code just needed to exit the thread....
Private Sub GetData(ByVal x As Integer) Dim myno As Integer Dim File_Name As String Dim ShortString As String
Console.WriteLine("Listening For Incoming Data on " & x & " Connection") RETRY: Try If client(x).Client.Connected = True Then Dim Buffer(client(x).ReceiveBufferSize) As Byte myno = client(x).GetStream.Read(Buffer, 0, CInt(client(x).ReceiveBufferSize)) If myno <> 0 And myno <> -1 Then ShortString = Encoding.ASCII.GetString(Buffer, 0, myno) '####################################################################### '##################PROCESSING RECEIVED BUFFER IN THREAD N############### '####################################################################### Dim fsstream As System.IO.FileStream Dim bwwriter As System.IO.BinaryWriter
Dim MyArray() As String ShortString = Encoding.ASCII.GetString(Buffer, 0, myno)
If Microsoft.VisualBasic.Left(ShortString, 6) = "#Make#" Then MyArray = Split(ShortString, "#") File_Name = MyArray(UBound(MyArray)) File_Name = "d:\" & File_Name
fsstream = New System.IO.FileStream(File_Name, IO.FileMode.Create, IO.FileAccess.Write, IO.FileShare.ReadWrite) bwwriter = New System.IO.BinaryWriter(fsstream) bwwriter.Close()
ElseIf Microsoft.VisualBasic.Left(ShortString, 5) = "*End*" Then fsstream = New System.IO.FileStream(File_Name, IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.ReadWrite) bwwriter = New System.IO.BinaryWriter(fsstream) bwwriter.Flush() bwwriter.Close() Else fsstream = New System.IO.FileStream(File_Name, IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.ReadWrite) bwwriter = New System.IO.BinaryWriter(fsstream) bwwriter.Write(Buffer, 0, myno) bwwriter.Flush() bwwriter.Close() End If '########################################################################## '###################END OF RECEIVED BUFFER PROCESSING###################### '##########################################################################
End If Else ' MsgBox("Disconnected") End If Catch ex As Exception MsgBox(ex.Message) GoTo Finish '''''''''''''''''This part tells the thread to goto finish so that thread closes...I hate it. How can i terminate this thread from here End Try ''''Any alternative for goto will exit sub work ??havent tested it GoTo RETRY finish: End Sub
A candle loses nothing by lighting another candle. | | Silver_Gates | Is it correct that you solved your problem? If so, Mark As Answerthe helpful post and Vote it As Helpful.
You can detect that client is disconnectedwhen client closes connection normally.Insome cases, the only way to determine that connection is lost is timeout when nothing is received from client. I beleive you want to detect client diconnection to free up your threads that otherwise blockedin read:you might consider startingasynchronous task that closeSocket after some time of inactivity,causing that Socket to unblock immediately and throw an exception from read.
| | Oleksii Prokopchuk [NCL] | Yeah My Problem is solved.. and thanks for the helpful suggestions. the final code which is working flawlessly till now is given below...anyways if it starts bothering me again i''ll come back to you guys thanks again Private Sub GetData(ByVal x As Integer) Dim myno As Integer Dim File_Name As String Dim ShortString As String Console.WriteLine("Listening For Incoming Data on " & x & " Connection") While (True) Try If client(x).Client.Connected = True Then Dim Buffer(client(x).ReceiveBufferSize) As Byte myno = client(x).GetStream.Read(Buffer, 0, CInt(client(x).ReceiveBufferSize)) If myno <> 0 And myno <> -1 Then ShortString = Encoding.ASCII.GetString(Buffer, 0, myno) '####################################################################### '##################PROCESSING RECEIVED BUFFER IN THREAD N############### '####################################################################### Dim fsstream As System.IO.FileStream Dim bwwriter As System.IO.BinaryWriter Dim MyArray() As String ShortString = Encoding.ASCII.GetString(Buffer, 0, myno) If Microsoft.VisualBasic.Left(ShortString, 6) = "#Make#" Then MyArray = Split(ShortString, "#") File_Name = MyArray(UBound(MyArray)) File_Name = "d:\" & File_Name fsstream = New System.IO.FileStream(File_Name, IO.FileMode.Create, IO.FileAccess.Write, IO.FileShare.ReadWrite) bwwriter = New System.IO.BinaryWriter(fsstream) bwwriter.Close() ElseIf Microsoft.VisualBasic.Left(ShortString, 5) = "*End*" Then fsstream = New System.IO.FileStream(File_Name, IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.ReadWrite) bwwriter = New System.IO.BinaryWriter(fsstream) bwwriter.Flush() bwwriter.Close() Else fsstream = New System.IO.FileStream(File_Name, IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.ReadWrite) bwwriter = New System.IO.BinaryWriter(fsstream) bwwriter.Write(Buffer, 0, myno) bwwriter.Flush() bwwriter.Close() End If '########################################################################## '###################END OF RECEIVED BUFFER PROCESSING############################# '########################################################################## End If Else Console.WriteLine("Client Has Been Disconnected") End If Catch ex As Exception If Err.Number = 57 Then 'Client Disconnected Console.WriteLine("Client Diconnected") Exit While Else MsgBox(Err.Description) MsgBox(Err.Number) End If End Try End While End Sub If you find any drawbacks here do let me know... Thanks Again
A candle loses nothing by lighting another candle. | | Silver_Gates |
|