i have a program with 2 threads. thread1 is waiting for data over ethernet using sockets. the data is received and i invoke a routine to update a field on the screen. this works great for 2,750 times then i get a stack exception for recursive routines. basically my process gets the data, updates the screen and calls a routine that calls my "waiting for data" routine.
Now i tried to slow things down by putting the thread to sleep for 100ms however i received the same error after about 2000 times processed.
the error happens on line me.invoke(mi)
thread_start just calls waitfordata
Code:
Private Sub waitfordata()
Try
Dim serverStream As NetworkStream = clientSocket.GetStream()
Dim bytes(clientSocket.ReceiveBufferSize) As Byte
serverStream.Read(bytes, 0, CInt(clientSocket.ReceiveBufferSize))
Dim returndata As String = Encoding.ASCII.GetString(bytes)
rtdata = returndata.ToString
Dim mi As MethodInvoker = AddressOf UpdateFormText
Me.Invoke(mi)
Call thread_start()
Catch ex As Exception
MessageBox.Show(Err.Description)
End Try
End Sub
You're going to stack overflow no matter what. Every recursive routine needs an exit condition. If you don't write one, the only difference between that an an infinite loop is eventually recursion stack overflows.
If you want to continue listening for data with some exit condition that can't be governed by these two methods, use asynchronous calls. You can look up the details in the documentation, but the pattern looks like this:
Code:
Sub WaitForData()
' blah blah setup code
serverStream.ReadAsync([blah blah], AddressOf DataArrived)
End Sub
Sub DataArrived(blah blah)
' end the asynchronous operation, process results...
WaitForData()
End Sub
My guess is NetworkStream's using the IAsyncResult pattern, but you can find out all about that in the documentation. Or ask questions about it!
You'd actually get better results if, instead of asking "How do I make this code work?", you revised it to "What's the right way to accomplish <goal>?"
__________________ .NET Resources My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
You usually would kick off the background thread and have it loop, receiving data and sending it off to be process, and reading again. You would exit the background thread when you no longer needed to receive data.
Here's my code for the receiving end of set of programs. The one end would do periodic captures of an area of a screen and send it to this application over Ethernet (TCP) to be displayed in a picturebox.
You see the Sub "Listener" is started as a background thread and receives, delegates and loops back to receive again. This one is a little more involved since the loop itself is inside another loop so that if we loose connection and get an exception, it will try to re-establish the connection, but the principle is the same to what your are trying to do.
Code:
Imports System.Net.Sockets
Imports System.Net
Imports System.IO
Public Class Form1
Dim server As TcpListener
Delegate Sub noParam()
Dim updateDelegate As New noParam(AddressOf UpdateDisplay)
Dim ListenThread As Threading.Thread = New Threading.Thread(AddressOf Listener)
Dim bData() As Byte
Dim busy As Boolean
Dim mstream As System.IO.MemoryStream
Dim exiting As Boolean
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If Not exiting Then
exiting = True
e.Cancel = True
End If
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
ListenThread.IsBackground = True
ListenThread.Start()
End Sub
Private Sub Listener()
Dim lPort As Int32 = 3030
Dim lAddr As IPAddress = IPAddress.Parse("127.0.0.1")
Dim bLen(3) As Byte
Dim iLen As Int32
Do Until exiting 'Outer loop to restart listening if we loose the connection
server = New TcpListener(lAddr, lPort)
server.Start()
Dim lClient As TcpClient = server.AcceptTcpClient()
Dim nStream As NetworkStream = lClient.GetStream
Dim i As Int32
Dim br As BinaryReader = New BinaryReader(nStream)
Try
Do Until exiting
i = nStream.Read(bLen, 0, 4)
iLen = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(bLen, 0))
If iLen < 10000000 Then
' bData = New Byte(iLen - 1) {}
' i = nStream.Read(bData, 0, iLen)
bData = br.ReadBytes(iLen)
If Not exiting Then
If bData.Length = iLen Then
If Not busy Then
If mstream IsNot Nothing Then mstream.Dispose()
mstream = New System.IO.MemoryStream
mstream.Write(bData, 0, bData.Length)
Me.Invoke(updateDelegate)
mstream.Dispose()
mstream = Nothing
Else
Debug.Print("Read short")
End If
End If
End If
End If
Loop
Catch ex As Exception
Finally
br.Dispose()
mstream.Dispose()
nStream.Dispose()
lClient.Close()
End Try
server.Stop()
server = Nothing
Loop
End Sub
Private Sub UpdateDisplay()
If Not exiting Then
busy = True
With PictureBox1
If .Image IsNot Nothing Then .Image.Dispose()
.Image = New Bitmap(mstream)
End With
busy = False
Else
Me.Close()
End If
End Sub
End Class
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.
I tried your code but i can not even build it because i receive an error on the Dim Server as tcplistener. it has a blue line under it and when i highlight it it states TYPE EXPECTED.
Now I have looked at many examples on the net and there are many people using the same syntax and i can not figure out why i am getting this error. my imports are the same as yours (as i said i copied your code into a new project)
This is getting frustrating. I do appreciate everyones help though but I am kind of under the gun on this one.
Well, you don't really need to build my code, although I'm not sure why it wouldn't. What version of VB.Net are you using? <edit p.p.s I just copied the code from the post above, pasted it in a new project, added a picturebox and it compiled and ran fine. Are you running an old, possibly express version>
The point was just to look at how it was structured to Loop in the background thread, to receive data independent of other threads.
Bottom line, if you just put a loop around your receiver, then you won't keep calling your Sub, pushing more and more data on the stack and never cleaning it up, until it runs out of space.
Sounds like you may have missed out on some fundamental instruction on how computers and compilers generally work.
Expanding on what AtmaWeapon said, when you call a procedure it "pushes" (allocates memory) on a "stack" which is a form of First In/Last Out structure, where parameters are passed to the sub/function and memory space is allocated to keep track of "local" variables. When you exit the sub/function this allocated memory on the stack is "popped" off the stack.
When you call a subroutine it allocates what it needs, and then if you call another sub/function it allocates what it needs, and if it calls another function it allocates what it needs, ....and so on. It is assumed that eventually the last function called will exit, clean up the bottom of the stack it had allocated and return to the previous caller, which will finished, and clean up the bottom of the stack where it allocated stuff and return to the previous caller, etc...
Since you call your routine, then have your routine, call another routine, which calls your routine, nobody ever finishes and exits, so you just keep piling on more and more local data on the stack until you run out of stack memory for your process.
Don't do that.
p.s. I didn't look closely at your code, but just now as I posted, you probably want to move some declarations outside the loop, so I modified the routine slightly, but since I'm not testing it, it may not work as modified.
Code:
Private Sub waitfordata()
Dim serverStream As NetworkStream = clientSocket.GetStream()
Dim bytes(clientSocket.ReceiveBufferSize) As Byte
Do Until TimeToLeave
Try
serverStream.Read(bytes, 0, CInt(clientSocket.ReceiveBufferSize))
Dim returndata As String = Encoding.ASCII.GetString(bytes)
rtdata = returndata.ToString
Dim mi As MethodInvoker = AddressOf UpdateFormText
Me.Invoke(mi)
' Call thread_start() ' <= Don't call this, you just dig yourself in a hole
Catch ex As Exception
MessageBox.Show(Err.Description)
End Try
Loop
End Sub
p.p.p.s A "TYPE EXPECTED" error usually indicates a naming conflict, typically an ambiguous or duplicate name in a namespace. You could try not using any imports, just explicitly use the full name of the various items to see if it will still not compile. Of course, even if it does compile, running this receiver without anything to connect and transmit to it is indistinguishable from a program that has no user code in it at all.
Same code, imports removed and names expanded to resolve the missing namespaces.
Code:
Public Class Form1
Dim server As System.Net.Sockets.TcpListener
Delegate Sub noParam()
Dim updateDelegate As New noParam(AddressOf UpdateDisplay)
Dim ListenThread As Threading.Thread = New Threading.Thread(AddressOf Listener)
Dim bData() As Byte
Dim busy As Boolean
Dim mstream As System.IO.MemoryStream
Dim exiting As Boolean
Private Sub Form1_FormClosing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If Not exiting Then
exiting = True
e.Cancel = True
End If
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
ListenThread.IsBackground = True
ListenThread.Start()
End Sub
Private Sub Listener()
Dim lPort As Int32 = 3030
Dim lAddr As System.Net.IPAddress = System.Net.IPAddress.Parse("127.0.0.1")
Dim bLen(3) As Byte
Dim iLen As Int32
Do Until exiting 'Outer loop to restart listening if we loose the connection
server = New System.Net.Sockets.TcpListener(lAddr, lPort)
server.Start()
Dim lClient As System.Net.Sockets.TcpClient = server.AcceptTcpClient()
Dim nStream As System.Net.Sockets.NetworkStream = lClient.GetStream
Dim i As Int32
Dim br As System.IO.BinaryReader = New System.IO.BinaryReader(nStream)
Try
Do Until exiting
i = nStream.Read(bLen, 0, 4)
iLen = System.Net.IPAddress.NetworkToHostOrder(BitConverter.ToInt32(bLen, 0))
If iLen < 10000000 Then
' bData = New Byte(iLen - 1) {}
' i = nStream.Read(bData, 0, iLen)
bData = br.ReadBytes(iLen)
If Not exiting Then
If bData.Length = iLen Then
If Not busy Then
If mstream IsNot Nothing Then mstream.Dispose()
mstream = New System.IO.MemoryStream
mstream.Write(bData, 0, bData.Length)
Me.Invoke(updateDelegate)
mstream.Dispose()
mstream = Nothing
Else
Debug.Print("Read short")
End If
End If
End If
End If
Loop
Catch ex As Exception
Finally
br.Dispose()
mstream.Dispose()
nStream.Dispose()
lClient.Close()
End Try
server.Stop()
server = Nothing
Loop
End Sub
Private Sub UpdateDisplay()
If Not exiting Then
busy = True
With PictureBox1
If .Image IsNot Nothing Then .Image.Dispose()
.Image = New Bitmap(mstream)
End With
busy = False
Else
Me.Close()
End If
End Sub
End Class
__________________
There Is An Island Of Opportunity In The Middle of Every Difficulty.
Miss That, Though, And You're Pretty Much Doomed.
Oh gosh please don't say this and propagate a myth that Express can't write the same code the for-pay versions do. Just... never say that again please.
Something sounds fishy though. TcpListener has been around since .NET 1.1 and has been in System.dll since then. You'd have to go pretty far out of your way to make VS not recognize it. My guess is there's some other syntax error confusing VS.
__________________ .NET Resources My FAQ threads | Tutor's Corner | Code Library
I would bet money 2/3 of .NET questions are already answered in one of these three places.
when i copied the code and put it into a program the error i received is because i did not put it into a console program, i put it into a windows form program. when i realized that i created a windows console program and yes all built correctly.
I only did this because when i tried taking pieces of it and including it in my logic i was getting the error. so now at least i know why i have the one error. so now i am continuing trying to write a proper code.
thanks you guys definitely put me on the track for trying new ideas hopefully i will get it soon.
Thank you guys. I applied the ideas mentioned above and the process is working well, i looped my program with continuous input and it is running without fail for over 20,000 loops right now and climbing as i write.....
The ASP.NET 2.0 Anthology
101 Essential Tips, Tricks & Hacks - Free 156 Page Preview. Learn the most practical features and best approaches for ASP.NET. subscribe