I setup a listener for notification from postresql through npgsql in vb.net. The sub just opens a new ssl connection and starts a new thread with a loop waiting for notification. Here below is the code
Public Sub StartListening()
If mConnString Is Nothing Then
Main_Form.WriteMessage("Not connected")
End If
Try
connection = New NpgsqlConnection(mConnString) With {
.ProvideClientCertificatesCallback = New ProvideClientCertificatesCallback(AddressOf MyProvideClientCertificates)
}
connection.Open()
If connection.State = ConnectionState.Open Then
Using command = New NpgsqlCommand("listen my_notification", connection)
command.ExecuteNonQuery()
End Using
End If
AddHandler connection.Notification, New NotificationEventHandler(AddressOf OnNotification)
Dim thread As New Thread(
Sub()
While True
connection.Wait()
End While
End Sub
)
thread.Start()
Catch ex As Exception
Main_Form.WriteMessage("Error:" + ex.Message)
End Try
End Sub
Private Sub MyProvideClientCertificates(ByVal clienteCertis As X509CertificateCollection)
Dim cert As X509Certificate2 = New X509Certificate2("mycertificate.pfx")
clienteCertis.Add(cert)
End Sub
Everything was working fine until I introduced SSL connection: it fails on connection.Wait() saying
Wait() with timeout isn't supported when SSL is used, see
https://github.com/npgsql/npgsql/issues/1501
Since actually I don't need a timeout I tried setting timeout=0 and commandtimeout=0 in connection string but error still remains and this is what I see in error stacktrace
in Npgsql.NpgsqlConnector.Wait(Int32 timeout)
in Npgsql.NpgsqlConnection.Wait(Int32 timeout)
in Npgsql.NpgsqlConnection.Wait()
Could anyone help?
I found a solution. I write here just in case some else has the same problem.
I changed Wait() with WaitAsync().
To avoid continuous looping also added Await and declare the sub as Async.
Below is the code
....
Dim thread As New Thread(
Async Sub()
While True
Await connection.WaitAsync()
End While
End Sub
)
thread.Start()
....
Related
I am working in VB and when the end user clicks on a button, I want to show a “Please wait” window (no progress update necessary) and trigger the process in the background on another thread as it might take a while to complete. I understand BackgroundWorker is the recommended approach for this, and although I have previously set up a BGW on a windows forms application, I am new to WPF and having a hard time understanding the differences.
In order to reuse some of my code, I have written a function (“DbOperation”) that executes a SQL stored procedure (from parameter) and returns a data table (or NULL if no output), and it seems to work well everywhere except in concert with my BGW.
I understand UI should only be affected by the main thread, and background threads should not touch the UI, and the BGW thread should call the long-running process.
Private Sub btnProcessReports_Click(sender As Object, e As RoutedEventArgs) Handles btnProcessReports.Click
'Set up background worker
bgw.WorkerReportsProgress = False
bgw.WorkerSupportsCancellation = False
AddHandler bgw.DoWork, AddressOf bgw_DoWork
AddHandler bgw.RunWorkerCompleted, AddressOf bgw_RunWorkerCompleted
'Show please wait window
f.Label1.Text = "Importing web reports. Please wait."
f.Show()
'Start the work!
bgw.RunWorkerAsync()
End Sub
Private Sub bgw_DoWork(sender As Object, e As DoWorkEventArgs)
'Set current thread as STA
Thread.CurrentThread.SetApartmentState(ApartmentState.STA)
'Trigger job
Dim Qry As String = "EXEC [msdb].[dbo].sp_start_job N'Import Online Payment Portal Reports';"
DbOperation(Qry)
End Sub
Public Function DbOperation(ByVal qry As String, Optional ByVal type As DatabaseQueryReturnType = DatabaseQueryReturnType.NonQuery) As Object
Dim objDB As New Object
Dim dt As DataTable
Dim da As SqlDataAdapter
OpenConnection()
Dim cmd As New SqlCommand(qry)
cmd.Connection = con
If type = DatabaseQueryReturnType.DataTable Then
dt = New DataTable
da = New SqlDataAdapter(cmd)
Try
da.Fill(dt)
Catch ex As Exception
MessageBox.Show("Error retrieving data from database: " & ex.Message.ToString)
End Try
objDB = dt
dt.Dispose()
CloseConnection()
Return objDB
ElseIf type = DatabaseQueryReturnType.NonQuery Then
Try
cmd.ExecuteNonQuery()
Catch ex As Exception
MessageBox.Show("Error executing nonquery: " & ex.Message.ToString)
End Try
objDB = Nothing
CloseConnection()
Return objDB
End If
Return objDB
End Function
When I try running the program, it gives me “The calling thread must be STA, because many UI components require this.” on the line that calls my function. After a bit of research, I learned that you have to call SetApartmentState() before the thread is started, but this doesn’t make sense since the recommended code seems to be:
Thread.CurrentThread.SetApartmentState(ApartmentState.STA)
After adding this line, I then get “Failed to set the specified COM apartment state” on that line of code.
I have also tried commenting out the messagebox lines in the function in case it was trying to open the messageboxes on the UI thread, but it had no impact and I still got the same errors.
What am I missing in my code to get the function to run on the BGW thread?
I have an application which connects to a Zebra QLn420 label printer through a TCPClient connection to the printer's static IP address and port number.
We have been having some issues with the software "stalling" and skipping over some of the labels, and after investigating (by adding some logging capabilities to the software to write out to a text file each time it checks the connection and actually reconnects) I have noticed that the connection does not seem to stay open for more than one second (not a solid number, it can vary from less than a second to 5 seconds or more from my tests).
While testing at my desk, this is almost a non-issue because the signal strengths are so strong that it reconnects instantly, but out in the warehouse where this is actually used, in some areas the signal isn't so strong. The function which handles the connection will try to reconnect for up to 5 seconds twice, then display an error message and ask if they want to retry connecting.
I'm curious if anyone has experienced anything like this, and what can be done to resolve it. I understand that the software will lose connection with the printer occasionally, especially in the weaker signal areas. But I would like to find a way to keep the connection open until either I close it or the signal is too weak to keep it going, as I feel this has a lot to do with the "missing" labels and definitely impacts the speed of the software.
Below is my connection code (a combination of my own code and some snippets found online):
Public Class LabelPrinter
Private strIP as String = "xxx.xxx.xxx.xxx"
Private intPort As Integer = "6101"
Private client As System.Net.Sockets.TcpClient
Private blnConnected As Boolean = False
Private blnValidIP As Boolean = False
Private intTimeoutLength As Integer = 5000
Private TimeoutTime As New Timers.Timer
Private clntSockParams As ClientSocketParameters
Private Structure ClientSocketParameters
Public addrs As String
Public prt As Integer
End Structure
Public Sub New(Picker As Picker, newIP As String, newPort As Integer, Optional PrinterTimeout As Integer = 5000)
If newIP <> "" Then strIP = newIP
If newPort <> 0 Then intPort = newPort
intTimeoutLength = PrinterTimeout
If newIP = "" Or newPort = 0 Then Exit Sub
Dim ipAddress As System.Net.IPAddress = Nothing
If Not System.Net.IPAddress.TryParse(strIP, ipAddress) Then Exit Sub
End Sub
Public Function Connect() As Boolean
Try
'connect to printer via TcpClient, need ip address and port number
'connects without thread, hangs program for 10-20 seconds if printer is not turned on, replaced with code below to thread the connection and set timeout
'If client Is Nothing OrElse Not client.Connected Then
' client = New System.Net.Sockets.TcpClient
' client.Connect(strIP, intPort)
'End If
'RRB 02/10/15 - added for loop to try to connect twice each attempt instead of only once
For i As Integer = 1 To 2
If client Is Nothing OrElse Not client.Connected Then
'uses ClientSocketParameters structure to pass to recursive function ConnectionReturned()
clntSockParams = New ClientSocketParameters
clntSockParams.addrs = strIP
clntSockParams.prt = intPort
'create client and call BeginConnect (attempts to connect on separate thread until TimeoutTime has elapsed)
client = New System.Net.Sockets.TcpClient
client.SendTimeout = intTimeoutLength
client.ReceiveTimeout = intTimeoutLength
'setup timer with timeout length and start, if timer goes past intTimeoutLength, the Timeout() function is called which closes everything and leaves client = Nothing
AddHandler TimeoutTime.Elapsed, AddressOf Timeout
TimeoutTime.Interval = intTimeoutLength
TimeoutTime.Start()
client.BeginConnect(strIP, intPort, New AsyncCallback(AddressOf ConnectionReturned), clntSockParams)
'keeps the program from doing anything else until BeginConnect either succeeds or fails (due to connect on separate thread)
Do While TimeoutTime.Enabled
System.Threading.Thread.Sleep(500)
Loop
End If
'if TimeoutTime is elapsed and client is Nothing, connection didn't happen, throw an error
If client Is Nothing Then
blnConnected = False
Else
blnConnected = True
Exit For
End If
Next
Catch ex As Exception
blnConnected = False
End Try
Return blnConnected
End Function
Private Sub ConnectionReturned(ByVal ar As System.IAsyncResult)
'this method is called from the client.BeginConnect line in Connect(), make sure timer is running
If TimeoutTime.Enabled Then
'ensure client is initialized
If client Is Nothing Then client = New System.Net.Sockets.TcpClient
'keep calling ConnectionReturned until client.Connected is true
If client.Connected Then
TimeoutTime.Stop()
Else
Dim actualParameters As ClientSocketParameters = DirectCast(ar.AsyncState, ClientSocketParameters)
client.BeginConnect(actualParameters.addrs, actualParameters.prt, New AsyncCallback(AddressOf ConnectionReturned), clntSockParams)
End If
End If
End Sub
Private Sub Timeout(ByVal sender As Object, ByVal e As EventArgs)
'this method is only called if TimeoutTime elapsed, which means no connection was made. close the client object if needed, set to Nothing, and stop TimeoutTime
If TimeoutTime.Enabled Then
Try
client.Close()
Catch ex As Exception
End Try
client = Nothing
TimeoutTime.Stop()
End If
End Sub
Public Sub Disconnect()
'need to make sure StreamWriter connection and TcpClient connection to printer are closed
Try
client.Close()
Catch ex As Exception
End Try
client = Nothing
blnConnected = False
End Sub
End Class
Please let me know if more information is needed. I'm also open to alternative connection types, so long as the static IP and port can be used (over wifi connection).
EDIT: I've modified the code in my program to use the System.Net.Socket class, and the connection seems to hold steady in my initial tests. What would cause this connection method to work where TCPClient doesn't seem to, in the same location?
I get this error. I can not catch it where it comes from. All methods made with try catch but still I get this error from time to time. I used code:
Private Sub add2ComboBox_called(ByVal text As String)
Try
If Me.InvokeRequired Then
Dim args() As String = {text}
Me.Invoke(New Action(Of String)(AddressOf add2ComboBox_called), args)
Return
End If
ComboBox_called.Items.Add(text)
Catch ex As Exception
log_it(ex.StackTrace)
End Try
End Sub
and also this one:
Public Sub clear_do_not_open()
Dim FILENAME As String = path & "\DO_NOT.OPEN"
Dim index As Integer
Try
Using fs As New IO.FileStream(FILENAME, IO.FileMode.Truncate, IO.FileAccess.Write, IO.FileShare.ReadWrite), _
tl As New TextWriterTraceListener(fs)
index = Trace.Listeners.Add(tl)
Trace.Write("")
Trace.Listeners(index).Flush()
Trace.Flush()
End Using
Trace.Listeners.RemoveAt(index)
Catch ex As Exception
log_it(ex.StackTrace)
End Try
End Sub
How do I make this to press Retry by default if it appears? Wait for a second and press Retry again? If not like this then how do I know that this message pops up? If catch the moment it shows up I could then just restart the program or send an email to myselt so I could come to PC where program runs and I could handle it. Now message pops up and program stops while its on.
In my current project I'm sending and receiving textmessages to/from a serversocket/clientsocket (TCP), much like a chat (My project is written in VB.NET). This works as long as I'm converting the bytes sent into strings and presenting them in a msgbox().
This code handles that part:
Try
client = ar.AsyncState
client.EndReceive(ar)
client.BeginReceive(bytes2, 0, bytes2.Length, SocketFlags.None, New AsyncCallback(AddressOf OnRecieve), client)
Try
Dim message As String = System.Text.ASCIIEncoding.ASCII.GetString(bytes2)
MsgBox(message)
Array.Clear(bytes2, bytes2.GetLowerBound(0), bytes2.Length)
Catch ex As Exception
MsgBox("Error writing received message")
End Try
Catch ex As Exception
MsgBox("Error receiving message from server")
End Try
So far so good. However when I try to change "MsgBox(message)" into label1.text = message I get the error: "Error writing received message". My questions, then, is why this happens and what can I do to correct it so that I can have my sockets receiving information that can be added to textboxes and other things in the UI?
Thanks in advance for any help that you can provide
Use a delegate and BeginInvoke() to properly marshal the call to the main UI thread:
Private Sub OnRecieve(ar As IAsyncResult)
Try
client = ar.AsyncState
client.EndReceive(ar)
Try
Dim message As String = System.Text.ASCIIEncoding.ASCII.GetString(bytes2)
NewMessage(message)
Catch ex As Exception
MsgBox("Error writing received message")
Finally
Array.Clear(bytes2, bytes2.GetLowerBound(0), bytes2.Length)
End Try
client.BeginReceive(bytes2, 0, bytes2.Length, SocketFlags.None, New AsyncCallback(AddressOf OnRecieve), client)
Catch ex As Exception
MsgBox("Error receiving message from server")
End Try
End Sub
Private Delegate Sub MessageDelegate(ByVal msg As String)
Private Sub NewMessage(ByVal msg As String)
If Me.InvokeRequired Then
Me.BeginInvoke(New MessageDelegate(AddressOf NewMessage), New Object() {msg})
Else
Label1.Text = msg
End If
End Sub
I'm working on a remote control tool. The client runs a program to locally send commands to servers in order to control them.
However, the client doesn't know the server's IP address and vice versa.
I decided to use UDP broadcasting (please tell me if there's a better way to do this, I tried using multicast but I didn't really understand it). When started, the client (which controls the servers) broadcasts a message to tell the servers that a new client connected. Then (or when the server is started), the servers broadcast their own IP addresses. When the client receives an IP address, it tries to connect via TCP.
Unfortunately, I ran into problems with that. I randomly got An existing connection was forcibly closed by the remote host exceptions and I wasn't able to find out why.
The exception occurred in my client program when listening for UDP broadcasts.
Now, I'm looking for a better way to find the clients.
Should I use broadcast or multicast?
How would I implement that?
EDIT: It wouldn't be a problem to use multiple ports. However, I need to be able to run a client AND a server on a single computer.
Here's the code I was using
Client (controls servers)
'Variables
Private UdpBroadcaster As UdpClient
Private UdpBroadcasterEndpoint As New IPEndPoint(IPAddress.Broadcast, 4334)
'Sub New()
Try
UdpBroadcaster = New UdpClient(4333)
UdpBroadcaster.EnableBroadcast = True
Catch
MsgBox("Error creating UDP client! Port already in use?", ...)
End Try
'Called when the application starts
Private Sub StartUdpListener()
Dim ListenerUdp As New Thread(AddressOf UdpListener)
ListenerUdp.IsBackground = True
ListenerUdp.Start()
End Sub
'Started as thread in StartUdpListener()
Private Sub UdpListener()
Try
Do
'The next line fails with the error I described (An existing connection was forcibly closed by the remote host)
Dim ReceivedBytes() As Byte = UdpBroadcaster.Receive(UdpBroadcasterEndpoint)
Dim ReceivedString As String = System.Text.Encoding.UTF32.GetString(ReceivedBytes)
'The following three lines will just connect to the received hostname
Dim ScanThread As New Thread(Sub() ScanSingle(ReceivedString))
ScanThread.IsBackground = True
ScanThread.Start()
Loop
Catch
If Not UdpBroadcaster Is Nothing Then
UdpBroadcaster.Close()
UdpBroadcaster = Nothing
End If
InvokeStatus("UDP connection lost, please try again later.")
End Try
End Sub
'Called when the application starts and when the user manually clicks the "UDP Scan" button
Private Sub StartBroadcastUdpThread()
Dim UdpBroadcastThread As New Thread(Sub() BroadcastUdp())
UdpBroadcastThread.IsBackground = True
UdpBroadcastThread.Start()
End Sub
'Started as thread in StartBroadcastUdpThread()
Private Sub BroadcastUdp()
If UdpBroadcaster Is Nothing Then
Try
UdpBroadcaster = New UdpClient(4333)
UdpBroadcaster.EnableBroadcast = True
Catch
MsgBox("Error creating UDP Client.", MsgBoxStyle.Critical, "Error")
Application.Exit()
Return
End Try
End If
Dim BroadcastBytes() As Byte = System.Text.Encoding.UTF32.GetBytes("Client-Identify")
UdpBroadcaster.Send(BroadcastBytes, BroadcastBytes.Length, UdpBroadcasterEndpoint)
InvokeStatus("UDP request sent successfully")
End Sub
Servers (controlled by client)
'Variables
Private UdpBroadcaster As UdpClient
Private UdpBroadcasterEndpoint As New IPEndPoint(IPAddress.Broadcast, 4333)
'Main method
Public Sub Main()
Try
UdpBroadcaster = New UdpClient(4334)
UdpBroadcaster.EnableBroadcast = True
StartUdpListener()
StartBroadcastUdpThread()
Catch
Console.WriteLine("Failed to create server. Port already in use?")
End Try
End Sub
'Called in Main()
Private Sub StartUdpListener()
Dim ListenerUdp As New Thread(AddressOf UdpListener)
ListenerUdp.IsBackground = True
ListenerUdp.Start()
End Sub
'Started as thread in StartUdpListener()
Private Sub UdpListener()
Try
Do
Dim ReceivedBytes() As Byte = UdpBroadcaster.Receive(UdpBroadcasterEndpoint)
Dim ReceivedString As String = System.Text.Encoding.UTF32.GetString(ReceivedBytes)
If ReceivedString.Equals("Client-Identify") Then
StartBroadcastUdpThread()
End If
Loop
Catch
If Not UdpBroadcaster Is Nothing Then
UdpBroadcaster.Close()
End If
End Try
End Sub
'Called when the application is started or a "Client-Identify" command is received
Private Sub StartBroadcastUdpThread()
Dim UdpBroadcastThread As New Thread(Sub() BroadcastUdp())
UdpBroadcastThread.IsBackground = True
UdpBroadcastThread.Start()
End Sub
'Started as thread in StartBroadcastUdpThread()
Private Sub BroadcastUdp()
Dim BroadcastBytes() As Byte = System.Text.Encoding.UTF32.GetBytes(Dns.GetHostName)
UdpBroadcaster.Send(BroadcastBytes, BroadcastBytes.Length, UdpBroadcasterEndpoint)
End Sub
Thanks in advance!
Thank you for your answers. I fixed it by removing the feature to manually call StartBroadcastUdpThread() in my client.
I still don't understand why this happens though. I use exactly the same code for both client and server, except the ports are swapped. The TCP server doesn't crash even if the StartBroadcastUdpThread() method is called multiple times, the client does. By the way, the problem occurs regardless of whether the client or server is started first.
Even if I don't really understand why broadcasting the second time stops the client from receiving broadcasts - it's fixed for now. Thanks for you help!
I would suggest using Zeroconf to find the server and clients, and then use a TCP socket to communicate between the two. You can see an example implementation on key-value pair zeroconf announcements here: https://github.com/Eyescale/Lunchbox/blob/master/lunchbox/servus.cpp
Minimal UDP server basis:
Imports System.Threading
Shared client As UdpClient
Shared receivePoint As IPEndPoint
client = New UdpClient(2828) 'Port
receivePoint = New IPEndPoint(New IPAddress(0), 0)
Dim readThread As Thread = New Thread(New ThreadStart(AddressOf WaitForPackets))
readThread.Start()
Public Shared Sub WaitForPackets()
While True
Dim data As Byte() = client.Receive(receivePoint)
Console.WriteLine("=" + System.Text.Encoding.ASCII.GetString(data))
End While
End Sub