bradbritton
asked on
Issue with UDP Port Program freezing. HELP!!!
I have a program that is listening on a UDP Port for an incoming datapacket, then turns that output to hex. If there is no data comming in (streaming from field device), the program freezes. I have tried using threads and such, but it is still freezing! Here is the body of the code.
Public Class udpReader
Public Const udpInPort As Integer = 54321
Public modemIP As New System.Net.IPEndPoint(Syst em.Net.IPA ddress.Any , udpInPort) 'address of sender and port used
Public bytes As [Byte]()
Public hexstring As String
Public udpRecThread As Thread
Public ba As BitArray ' Bit array to store the data
Public dataListener As UdpClient ' the UDP Client that is used for the data to be streamed in, port client is sending from
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Public Sub recMessage() 'gets message from remote logger
bytes = dataListener.Receive(modem IP) 'displays modem info
ba = New BitArray(bytes) 'places incoming data into the bitarray
hexstring = BytesToString(bytes, True)
lblSizeInfo.Text = ba.Length.ToString 'gives length of the bit array
End Sub
Private Sub btnStart_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
Try
dataListener = New System.Net.Sockets.UdpClie nt(udpInPo rt) 'starts listening for data
recMessage() 'runs the sub routine to get the data
udpRecThread = New System.Threading.Thread(Ad dressOf recMessage) 'thread to handle connection
udpRecThread.Start() 'starts thread when data is received
Catch ex As Exception
MessageBox.Show(ex.Message )
End Try
txtIP.Text = modemIP.ToString() ' displays the modems IP
txtData.Text = hexstring 'displays the hex data from the modem
End Sub
Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
udpRecThread.Abort() ' closes the any threads
dataListener.Close() ' closes the ports
Me.Close() ' closes the form
End Sub
Function BytesToString(ByVal bytes As Byte(), Optional ByVal AsHex As Boolean = True) As String
Dim sb As New System.Text.StringBuilder
Dim sw As New System.IO.StringWriter(sb)
Dim nl As String = System.Environment.NewLine
Dim count As Integer = 0
Dim blockSize As Integer = 16
Dim blockFormat As String
Dim itemFormat As String
If AsHex Then
blockFormat = "{0:X4}: "
itemFormat = " {0:X2}"
Else
blockFormat = "{0,5}: "
itemFormat = " {0,3}"
End If
For blockStart As Integer = 0 To bytes.Length Step blockSize
sw.Write(String.Format(blo ckFormat, blockStart))
For index As Integer = blockStart To blockStart + blockSize - 1
If index < bytes.Length Then
sw.Write(String.Format(ite mFormat, bytes(index)))
End If
Next
sw.WriteLine()
Next
Return sw.ToString
End Function
End Class
Public Class udpReader
Public Const udpInPort As Integer = 54321
Public modemIP As New System.Net.IPEndPoint(Syst
Public bytes As [Byte]()
Public hexstring As String
Public udpRecThread As Thread
Public ba As BitArray ' Bit array to store the data
Public dataListener As UdpClient ' the UDP Client that is used for the data to be streamed in, port client is sending from
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
Public Sub recMessage() 'gets message from remote logger
bytes = dataListener.Receive(modem
ba = New BitArray(bytes) 'places incoming data into the bitarray
hexstring = BytesToString(bytes, True)
lblSizeInfo.Text = ba.Length.ToString 'gives length of the bit array
End Sub
Private Sub btnStart_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
Try
dataListener = New System.Net.Sockets.UdpClie
recMessage() 'runs the sub routine to get the data
udpRecThread = New System.Threading.Thread(Ad
udpRecThread.Start() 'starts thread when data is received
Catch ex As Exception
MessageBox.Show(ex.Message
End Try
txtIP.Text = modemIP.ToString() ' displays the modems IP
txtData.Text = hexstring 'displays the hex data from the modem
End Sub
Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
udpRecThread.Abort() ' closes the any threads
dataListener.Close() ' closes the ports
Me.Close() ' closes the form
End Sub
Function BytesToString(ByVal bytes As Byte(), Optional ByVal AsHex As Boolean = True) As String
Dim sb As New System.Text.StringBuilder
Dim sw As New System.IO.StringWriter(sb)
Dim nl As String = System.Environment.NewLine
Dim count As Integer = 0
Dim blockSize As Integer = 16
Dim blockFormat As String
Dim itemFormat As String
If AsHex Then
blockFormat = "{0:X4}: "
itemFormat = " {0:X2}"
Else
blockFormat = "{0,5}: "
itemFormat = " {0,3}"
End If
For blockStart As Integer = 0 To bytes.Length Step blockSize
sw.Write(String.Format(blo
For index As Integer = blockStart To blockStart + blockSize - 1
If index < bytes.Length Then
sw.Write(String.Format(ite
End If
Next
sw.WriteLine()
Next
Return sw.ToString
End Function
End Class
Here is my attempt...it compiles but I can't really test it. =\
Imports System.Threading
Imports System.net.Sockets
Public Class udpReader
Inherits System.Windows.Forms.Form
Public Const udpInPort As Integer = 54321
Public modemIP As New System.Net.IPEndPoint(Syst em.Net.IPA ddress.Any , udpInPort) 'address of sender and port used
Public bytes As [Byte]()
Public hexstring As String
Public udpRecThread As Thread
Public ba As BitArray ' Bit array to store the data
Public dataListener As UdpClient ' the UDP Client that is used for the data to be streamed in, port client is sending from
Private Sub btnStart_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
btnStart.Enabled = False
dataListener = New System.Net.Sockets.UdpClie nt(udpInPo rt) 'starts listening for data
udpRecThread = New System.Threading.Thread(Ad dressOf recMessage) 'thread to handle connection
udpRecThread.Start() 'starts thread when data is received
End Sub
Private Sub recMessage() 'gets message from remote logger
bytes = dataListener.Receive(modem IP) 'displays modem info
ba = New BitArray(bytes) 'places incoming data into the bitarray
hexstring = BytesToString(bytes, True)
updateMessage(hexstring, modemIP.ToString, ba.Length.ToString)
End Sub
Private Delegate Sub UpdateMessageDelegate(ByVa l hex As String, ByVal IP As String, ByVal length As String)
Private Sub UpdateMessage(ByVal hex As String, ByVal IP As String, ByVal length As String)
If Me.InvokeRequired Then
Dim umd As New UpdateMessageDelegate(Addr essOf UpdateMessage)
Me.Invoke(umd, New Object() {hex, IP, length})
Else
lblSizeInfo.Text = length 'gives length of the bit array
txtIP.Text = IP ' displays the modems IP
txtData.Text = hex 'displays the hex data from the modem
btnStart.Enabled = True
udpRecThread = Nothing
End If
End Sub
Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
If Not IsNothing(udpRecThread) AndAlso udpRecThread.IsAlive Then
dataListener.Close() ' closes the ports
udpRecThread.Abort() ' closes the any threads
End If
Me.Close() ' closes the form
End Sub
Function BytesToString(ByVal bytes As Byte(), Optional ByVal AsHex As Boolean = True) As String
Dim sb As New System.Text.StringBuilder
Dim sw As New System.IO.StringWriter(sb)
Dim nl As String = System.Environment.NewLine
Dim count As Integer = 0
Dim blockSize As Integer = 16
Dim blockFormat As String
Dim itemFormat As String
If AsHex Then
blockFormat = "{0:X4}: "
itemFormat = " {0:X2}"
Else
blockFormat = "{0,5}: "
itemFormat = " {0,3}"
End If
For blockStart As Integer = 0 To bytes.Length Step blockSize
sw.Write(String.Format(blo ckFormat, blockStart))
For index As Integer = blockStart To blockStart + blockSize - 1
If index < bytes.Length Then
sw.Write(String.Format(ite mFormat, bytes(index)))
End If
Next
sw.WriteLine()
Next
Return sw.ToString
End Function
End Class
Imports System.Threading
Imports System.net.Sockets
Public Class udpReader
Inherits System.Windows.Forms.Form
Public Const udpInPort As Integer = 54321
Public modemIP As New System.Net.IPEndPoint(Syst
Public bytes As [Byte]()
Public hexstring As String
Public udpRecThread As Thread
Public ba As BitArray ' Bit array to store the data
Public dataListener As UdpClient ' the UDP Client that is used for the data to be streamed in, port client is sending from
Private Sub btnStart_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnStart.Click
btnStart.Enabled = False
dataListener = New System.Net.Sockets.UdpClie
udpRecThread = New System.Threading.Thread(Ad
udpRecThread.Start() 'starts thread when data is received
End Sub
Private Sub recMessage() 'gets message from remote logger
bytes = dataListener.Receive(modem
ba = New BitArray(bytes) 'places incoming data into the bitarray
hexstring = BytesToString(bytes, True)
updateMessage(hexstring, modemIP.ToString, ba.Length.ToString)
End Sub
Private Delegate Sub UpdateMessageDelegate(ByVa
Private Sub UpdateMessage(ByVal hex As String, ByVal IP As String, ByVal length As String)
If Me.InvokeRequired Then
Dim umd As New UpdateMessageDelegate(Addr
Me.Invoke(umd, New Object() {hex, IP, length})
Else
lblSizeInfo.Text = length 'gives length of the bit array
txtIP.Text = IP ' displays the modems IP
txtData.Text = hex 'displays the hex data from the modem
btnStart.Enabled = True
udpRecThread = Nothing
End If
End Sub
Private Sub btnClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnClose.Click
If Not IsNothing(udpRecThread) AndAlso udpRecThread.IsAlive Then
dataListener.Close() ' closes the ports
udpRecThread.Abort() ' closes the any threads
End If
Me.Close() ' closes the form
End Sub
Function BytesToString(ByVal bytes As Byte(), Optional ByVal AsHex As Boolean = True) As String
Dim sb As New System.Text.StringBuilder
Dim sw As New System.IO.StringWriter(sb)
Dim nl As String = System.Environment.NewLine
Dim count As Integer = 0
Dim blockSize As Integer = 16
Dim blockFormat As String
Dim itemFormat As String
If AsHex Then
blockFormat = "{0:X4}: "
itemFormat = " {0:X2}"
Else
blockFormat = "{0,5}: "
itemFormat = " {0,3}"
End If
For blockStart As Integer = 0 To bytes.Length Step blockSize
sw.Write(String.Format(blo
For index As Integer = blockStart To blockStart + blockSize - 1
If index < bytes.Length Then
sw.Write(String.Format(ite
End If
Next
sw.WriteLine()
Next
Return sw.ToString
End Function
End Class
ASKER
this works awesome! However, If I needed to add fucntionality in regards to sending a message (command to send data) to the modem once I get a "DD" command back from it, do I need to add another delegate? Here is the code that I have to send the command:
Public Sub sendCommand()
Try
dataSender = New System.Net.Sockets.UdpClie nt
Dim datagram As [Byte]()
Dim cmdString1 As String
Dim cmdString2 As String
Dim i As Integer = 0
Dim readCmd As String = "000000"
cmdString1 = txtLowBit.Text
cmdString2 = txtHiBit.Text
cmdString1 = readCmd + cmdString1 + cmdString2
cmdString2 = "0" + Convert.ToString(cmdString 1.Length - 4) + cmdString1
cmdString1 = cmdString2
lblTest.Text = cmdString1
datagram = HexToBytes(cmdString1)
dataSender.Send(datagram, datagram.Length - 4, "modem.modemc.com", udpOutPort)
lblTest.Text = "DONE! Command Sent: " + " " + cmdString1 + " " + i.ToString
txtData.Text = "Sent the message!" + " " + cmdString1 + " " + Convert.ToString(datagram. Length)
Catch ex As Exception
MessageBox.Show(ex.Message )
End Try
End Sub
Could you also break this down for me from the code above:
Me.Invoke(umd, New Object() {hex, IP, length})
Thanks!
Brad
Public Sub sendCommand()
Try
dataSender = New System.Net.Sockets.UdpClie
Dim datagram As [Byte]()
Dim cmdString1 As String
Dim cmdString2 As String
Dim i As Integer = 0
Dim readCmd As String = "000000"
cmdString1 = txtLowBit.Text
cmdString2 = txtHiBit.Text
cmdString1 = readCmd + cmdString1 + cmdString2
cmdString2 = "0" + Convert.ToString(cmdString
cmdString1 = cmdString2
lblTest.Text = cmdString1
datagram = HexToBytes(cmdString1)
dataSender.Send(datagram, datagram.Length - 4, "modem.modemc.com", udpOutPort)
lblTest.Text = "DONE! Command Sent: " + " " + cmdString1 + " " + i.ToString
txtData.Text = "Sent the message!" + " " + cmdString1 + " " + Convert.ToString(datagram.
Catch ex As Exception
MessageBox.Show(ex.Message
End Try
End Sub
Could you also break this down for me from the code above:
Me.Invoke(umd, New Object() {hex, IP, length})
Thanks!
Brad
Let's start with the Delegate/Invoke part first:
Private Delegate Sub UpdateMessageDelegate(ByVa l hex As String, ByVal IP As String, ByVal length As String)
Private Sub UpdateMessage(ByVal hex As String, ByVal IP As String, ByVal length As String)
If Me.InvokeRequired Then
Dim umd As New UpdateMessageDelegate(Addr essOf UpdateMessage)
Me.Invoke(umd, New Object() {hex, IP, length})
Else
lblSizeInfo.Text = length 'gives length of the bit array
txtIP.Text = IP ' displays the modems IP
txtData.Text = hex 'displays the hex data from the modem
btnStart.Enabled = True
udpRecThread = Nothing
End If
End Sub
First we use "Me.InvokeRequired" to determine if the caller is on the same thread as the main UI. All controls are created on the same thread as the form itself so it is safe to use "Me" instead of a particular control name. If the caller (the person who ran the UpdateMessage() method) is NOT on the same thread as the main UI then "Me.InvokeRequired" returns TRUE. This means that we need to Marshal the call onto the main UI thread. We do this using a Delegate and the Invoke() method.
A Delegate is simply a pointer (technically a reference in VB.Net) to a sub/function. So we next create a Delegate that points to the UpdateMessage() method using:
Dim umd As New UpdateMessageDelegate(Addr essOf UpdateMessage)
The method being pointed to (UpdateMessage) in the "AddressOf" part must have the same "parameter signature" as the Delegate (UpdateMessageDelegate) being created.
Next we Marshal the call onto the main UI thread using the "Me.Invoke" function. The Invoke() function takes the Delegate pointed to (along with its parameters) and runs it on the Thread containing the caller ("Me" in this case). You pass any necessary parameters with the Delegate by creating an Array of type Object containing the parameters. This is what is happening in this line:
Me.Invoke(umd, New Object() {hex, IP, length})
When "umd" is run via Invoke(), we will actually make a recursive call to ourself in UpdateMessage (albeit the recursive call is in a different thread now!).
When we enter UpdateMessage() again (because of recursion remember), we first test to see if the caller is on the main UI thread with "Me.InvokeRequired". This time, however, it should return FALSE because the "Me.Invoke" call should have marshaled the delegate onto the main UI thread.
As a result, we will drop down into the ELSE block where it is safe to update our GUI controls from.
I noticed that in your other question you are using this:
Control.CheckForIllegalCro ssThreadCa lls = False
To "properly" handle that situation you would use the same approach I have outlined here using Delegates and Invoke().
Hope that makes sense!....
Private Delegate Sub UpdateMessageDelegate(ByVa
Private Sub UpdateMessage(ByVal hex As String, ByVal IP As String, ByVal length As String)
If Me.InvokeRequired Then
Dim umd As New UpdateMessageDelegate(Addr
Me.Invoke(umd, New Object() {hex, IP, length})
Else
lblSizeInfo.Text = length 'gives length of the bit array
txtIP.Text = IP ' displays the modems IP
txtData.Text = hex 'displays the hex data from the modem
btnStart.Enabled = True
udpRecThread = Nothing
End If
End Sub
First we use "Me.InvokeRequired" to determine if the caller is on the same thread as the main UI. All controls are created on the same thread as the form itself so it is safe to use "Me" instead of a particular control name. If the caller (the person who ran the UpdateMessage() method) is NOT on the same thread as the main UI then "Me.InvokeRequired" returns TRUE. This means that we need to Marshal the call onto the main UI thread. We do this using a Delegate and the Invoke() method.
A Delegate is simply a pointer (technically a reference in VB.Net) to a sub/function. So we next create a Delegate that points to the UpdateMessage() method using:
Dim umd As New UpdateMessageDelegate(Addr
The method being pointed to (UpdateMessage) in the "AddressOf" part must have the same "parameter signature" as the Delegate (UpdateMessageDelegate) being created.
Next we Marshal the call onto the main UI thread using the "Me.Invoke" function. The Invoke() function takes the Delegate pointed to (along with its parameters) and runs it on the Thread containing the caller ("Me" in this case). You pass any necessary parameters with the Delegate by creating an Array of type Object containing the parameters. This is what is happening in this line:
Me.Invoke(umd, New Object() {hex, IP, length})
When "umd" is run via Invoke(), we will actually make a recursive call to ourself in UpdateMessage (albeit the recursive call is in a different thread now!).
When we enter UpdateMessage() again (because of recursion remember), we first test to see if the caller is on the main UI thread with "Me.InvokeRequired". This time, however, it should return FALSE because the "Me.Invoke" call should have marshaled the delegate onto the main UI thread.
As a result, we will drop down into the ELSE block where it is safe to update our GUI controls from.
I noticed that in your other question you are using this:
Control.CheckForIllegalCro
To "properly" handle that situation you would use the same approach I have outlined here using Delegates and Invoke().
Hope that makes sense!....
In answer to the other question: It depends on where you call sendCommand() from.
If you call sendCommand() in response to anything from the main UI (like a button click) then you should be OK.
Not sure on the flow here, but you could just check for "DD" in the UpdateMessage() sub, as long as you do it from the ELSE block:
Private Sub UpdateMessage(ByVal hex As String, ByVal IP As String, ByVal length As String)
If Me.InvokeRequired Then
Dim umd As New UpdateMessageDelegate(Addr essOf UpdateMessage)
Me.Invoke(umd, New Object() {hex, IP, length})
Else
lblSizeInfo.Text = length 'gives length of the bit array
txtIP.Text = IP ' displays the modems IP
txtData.Text = hex 'displays the hex data from the modem
btnStart.Enabled = True
udpRecThread = Nothing
If hex = "DD" Then
sendCommand()
End If
End If
End Sub
The ELSE block is already on the main UI thread so it would be safe to call sendCommand() from there.
It is safe to READ control values fom another thread. You just can't SET control values from another thread. So if you call sendCommand() from a thread other than the main UI, then Yes, you would need a Delegate to process these lines of code:
...
lblTest.Text = cmdString1
...
...
lblTest.Text = "DONE! Command Sent: " + " " + cmdString1 + " " + i.ToString
txtData.Text = "Sent the message!" + " " + cmdString1 + " " + Convert.ToString(datagram. Length)
...because they are setting control values from another thread.
If you call sendCommand() in response to anything from the main UI (like a button click) then you should be OK.
Not sure on the flow here, but you could just check for "DD" in the UpdateMessage() sub, as long as you do it from the ELSE block:
Private Sub UpdateMessage(ByVal hex As String, ByVal IP As String, ByVal length As String)
If Me.InvokeRequired Then
Dim umd As New UpdateMessageDelegate(Addr
Me.Invoke(umd, New Object() {hex, IP, length})
Else
lblSizeInfo.Text = length 'gives length of the bit array
txtIP.Text = IP ' displays the modems IP
txtData.Text = hex 'displays the hex data from the modem
btnStart.Enabled = True
udpRecThread = Nothing
If hex = "DD" Then
sendCommand()
End If
End If
End Sub
The ELSE block is already on the main UI thread so it would be safe to call sendCommand() from there.
It is safe to READ control values fom another thread. You just can't SET control values from another thread. So if you call sendCommand() from a thread other than the main UI, then Yes, you would need a Delegate to process these lines of code:
...
lblTest.Text = cmdString1
...
...
lblTest.Text = "DONE! Command Sent: " + " " + cmdString1 + " " + i.ToString
txtData.Text = "Sent the message!" + " " + cmdString1 + " " + Convert.ToString(datagram.
...because they are setting control values from another thread.
ASKER
I added the check in for DD, but it does not go into the sub. Maybe I could try using hex.length > 1 for the logic? I have tried to use the DD check in the IF Loop, but it for some strange reason will not invoke the sub. Ahhhhh... I love socket programming ;-( Thanks for all your help so far!
ASKER
Also, how is the data from from the recMessage sub passed to the update message? Sorry about the questions, but this is some stuff that we never even were warned about in school! Bad DeVry... Bad!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Hmmm, good question. It should be just DD. I will have to analyze this a little further. Thanks for the help up until now. I will award points as what you have done has solved my original problem (and then some) If you would like I can repost any additional problems in a new thread (for more points) just to keep it fair. Thanks again and keep up the great work!
Brad
Brad
You can make a request here:
https://www.experts-exchange.com/Community_Support/