Read ASync Network Stream - vb.net

I've done a fair amount of searching and I'm sure I'm close, but I'm having problems and hoping someone can help.
I have an ethernet barcode scanner I need to be listening to constantly. I've tried using NetworkStream.Read in a separate thread, but then found out there is a 'BeginRead' function for async network streams. Problem is I can't get it working at all.
Here's the code I've got:
Public Class ScannerConnect
Private client As TcpClient
Property server As String
Property port As Int32 = 2005
Private data As [Byte]()
Sub Connect()
Try
client = New TcpClient(server, port)
Catch e As ArgumentNullException
Console.WriteLine("ArgumentNullException: {0}", e)
Catch e As SocketException
Console.WriteLine("SocketException: {0}", e)
End Try
End Sub 'Connect
Sub ListenASync()
stream = client.GetStream()
data = New [Byte](256) {}
stream.BeginRead(data, 0, data.Length, AddressOf ReadASync, stream)
End Sub
Private Sub ReadASync(ar As IAsyncResult)
Dim buffer As Byte() = TryCast(ar.AsyncState, Byte())
Dim bytesRead As Integer = stream.EndRead(ar)
Dim message As String = Encoding.ASCII.GetString(buffer, 0, bytesRead)
MsgBox(message)
stream.BeginRead(buffer, 0, buffer.Length, AddressOf ReadASync, buffer)
End Sub
End Class
It crashes on
Dim message As String = Encoding.ASCII.GetString(buffer, 0, bytesRead)
with error
Array cannot be null.
Any ideas what I'm doing wrong?

You passed stream (a NetworkStream) as the AsyncState parameter to BeginRead().
You can't cast that to a Byte() in the EndRead callback.

Related

VB.NET TcpListener FORM NOT SHOWING

i'm using the above code to run a form with a tcplistener.
when the tcplistener recevie data from the client i need to write the data in in label1.text
i have tryed to use Shown instead of Load the form is showed but it the label text doesn't change.
How can i resolve this? any help will be appreciated.
thank you
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
TcpServer()
End Sub
Shared Sub TcpServer()
Dim server As TcpListener
server = Nothing
Try
Dim port As Int32 = 4000
Dim localAddr As IPAddress = IPAddress.IPv6Any 'IPAddress.Parse("192.168.61.9") 'IPAddress.Any
server = New TcpListener(localAddr, port)
server.Start()
Dim bytes(1024) As Byte
Dim data As String = Nothing
While True
Console.WriteLine("Waiting for a connection... ")
Dim client As TcpClient = server.AcceptTcpClient()
Console.WriteLine("Connected!")
data = Nothing
Dim stream As NetworkStream = client.GetStream()
Dim i As Int32
' Loop to receive all the data sent by the client.
i = stream.Read(bytes, 0, bytes.Length)
While (i <> 0)
' Translate data bytes to a ASCII string.
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i)
Form1.Label1.Text = data
' Process the data sent by the client.
'data = data.ToUpper()
data = "aaa"
Dim msg As Byte() = System.Text.Encoding.ASCII.GetBytes(data)
' Send back a response.
stream.Write(msg, 0, msg.Length)
Console.WriteLine("Sent: {0}" + data)
i = stream.Read(bytes, 0, bytes.Length)
End While
' Shutdown and end connection
client.Close()
Form1.Refresh()
End While
Catch e As SocketException
MsgBox("SocketException: {0}", e.ToString)
Finally
server.Stop()
End Try
Console.WriteLine(ControlChars.Cr + "Hit enter to continue....")
Console.Read()
End Sub 'Main
Run TcpServer() on a new thread, or make use of BackGroundWorker Control which is part of winForms controls.

Delay when displaying a message received by a Telnet client

I am trying to implement a Telnet client in VB.NET. I am following this code as example:
The program I'm implementing works as follows:
I click the button "Open Telnet" to open the Telnet session.
I write an instruction (string) in the text box on the left and then I click on Button1 to send the message to a Telnet server (an electronic board with an embedded Ethernet port).
The answer sent by the Telnet server is displayed in the text box on the left.
The problem I'm having with both the example and the implementation I'm doing is that the messages are displayed delayed. For example, if I send the string 1FFFFFF + vbCrLf I am supposed to receive a message from the server saying Unknown HMI code!. I have checked with Wireshark that the message is sent by the Telnet server just after I sent the instruction with the VB.NET program but it is shown in the text box on the right only if I click Button1 a second time (no matter what is written in the text box on the left).
Could you please tell me if there is something I'm missing in the code?
Below is my code:
Imports System
Imports System.IO
Imports System.Net.Sockets
Imports System.Security.Cryptography.X509Certificates
Imports System.Text
Imports System.Threading
Imports System.Net.Http
Imports System.Net.Security
Imports System.Net.IPAddress
Imports System.Net
Public Class Form1
' Create a TcpClient.
Dim client As New TcpClient
Dim stream As NetworkStream
' Function to write/read a TCP stream.
Shared Sub Connect(server As [String], message As [String])
Try
' Translate the passed message into ASCII and store it as a Byte array.
Dim data As [Byte]() = System.Text.Encoding.ASCII.GetBytes(message)
' Send the message to the connected TcpServer.
Form1.stream.Write(data, 0, data.Length)
Console.WriteLine("Sent: {0}", message)
' Buffer to store the response bytes.
data = New [Byte](256) {}
' String to store the response ASCII representation.
Dim responseData As [String] = [String].Empty
' Read the first batch of the TcpServer response bytes.
Dim bytes As Int32 = Form1.stream.Read(data, 0, data.Length)
responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes)
Console.WriteLine("Received: {0}", responseData)
Form1.TelnetRx.Text += responseData + vbCrLf
Form1.TelnetRx.Refresh()
Catch e As ArgumentNullException
Console.WriteLine("ArgumentNullException: {0}", e)
Catch e As SocketException
Console.WriteLine("SocketException: {0}", e)
End Try
Console.WriteLine(ControlChars.Cr + " Press Enter to continue...")
Console.Read()
End Sub
' Function to open a Telnet session.
Public Function OpenTelnetSession(server As String, Port As Int32) As Boolean
Dim ipAddress As IPAddress = Parse(server)
client.Connect(ipAddress, Port)
stream = Me.client.GetStream()
Return True
End Function
' Button to send a message.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Connect("192.168.8.110", TCP_Order.Text + vbCrLf)
End Sub
' Button to open the Telnet session.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles OpenTelnet.Click
OpenTelnetSession("192.168.8.110", 10001)
End Sub
' Button to close the Telnet session.
Private Sub CloseTelnet_Click(sender As Object, e As EventArgs) Handles CloseTelnet.Click
End Sub
End Class
That because you are not reading the entire buffer of the response, you are just taking 256 bytes from it:
data = New [Byte](256) {} ' <-
Also you have to free the resource by closing the streams and the TcpClient once you receive the response. Always create the disposable objects by the Using statement to guarantee that.
Synchronous Example
The example below connects to an endpoint in synchronous blocking mode, the caller thread is blocked until a response is returned from the endpoint or an exception is thrown (connection timeout for example.)
Private Function Connect(server As String, port As Integer, Msg As String) As String
Using client As New TcpClient(server, port),
netStream = client.GetStream,
sr = New StreamReader(netStream, Encoding.UTF8)
Dim msgBytes = Encoding.UTF8.GetBytes(Msg)
netStream.Write(msgBytes, 0, msgBytes.Length)
Return sr.ReadToEnd
End Using
End Function
and the caller:
Private Sub TheCaller()
Dim resp As String = Nothing
Try
Dim server = "192.168.8.110"
Dim port = 10001
Dim msg = $"1FFFFFF{ControlChars.CrLf}{ControlChars.CrLf}"
resp = Connect(server, port, msg)
Catch ex As ArgumentNullException
resp = ex.Message
Catch ex As SocketException
resp = ex.SocketErrorCode.ToString
Catch ex As Exception
resp = ex.Message
Finally
If resp IsNot Nothing Then
UpdateStatus(resp)
End If
End Try
End Sub
Asynchronous Example
You may want to use an asynchronous operation since you are developing a WinForms application, and I don't think you want to block the UI thread. Here you need to call the Async methods of the TcpClient and the read/write streams:
Private Async Function ConnectAsync(server As String,
port As Integer, msg As String) As Task(Of String)
Using client As New TcpClient
Await client.ConnectAsync(server, port)
Using netStream = client.GetStream,
sw = New StreamWriter(netStream, Encoding.UTF8) With {.AutoFlush = True },
sr = New StreamReader(netStream, Encoding.UTF8)
Await sw.WriteLineAsync(msg)
Return Await sr.ReadToEndAsync()
End Using
End Using
End Function
and an Async caller:
Private Async Sub TheCaller()
Dim resp As String = Nothing
Try
Dim server = "192.168.8.110"
Dim port = 10001
Dim msg = $"1FFFFFF{ControlChars.CrLf}{ControlChars.CrLf}"
resp = Await ConnectAsync(server, port, msg)
Catch ex As ArgumentNullException
resp = ex.Message
Catch ex As SocketException
resp = ex.SocketErrorCode.ToString
Catch ex As Exception
resp = ex.Message
Finally
If resp IsNot Nothing Then
UpdateStatus(resp)
End If
End Try
End Sub
The UpdateStatus in the code snippets is just a method to append the responses into a TextBox..
Private Sub UpdateStatus(txt As String)
StatusTextBox.AppendText(txt)
StatusTextBox.AppendText(ControlChars.NewLine)
End Sub

How do I correctly use the OpenWriteAsync method to POST a file to an URL?

I have been using the UploadFileAsync method to complete a file upload to remote server until it encounters a file above around 1gb where the RAM will spike to this filesize and crash the application.
A little research has to lead me to the OpenWriteAsync function to write as a filestream instead of attempting to read the whole file at once however I can't for the life of me find a VB.NET example I can get working, I've tried to convert from C# and use what I've found and here's what I have:
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal
value As T) As T
target = value
Return value
End Function
Private Sub PushData(ByVal input As Stream, ByVal output As Stream)
Dim buffer As Byte() = New Byte(4095) {}
Dim bytesRead As Integer
While (InlineAssignHelper(bytesRead, input.Read(buffer, 0,
buffer.Length))) <> 0
output.Write(buffer, 0, bytesRead)
End While
End Sub
Private Sub UploadFile(ByVal fileName As String, ByVal data As Stream)
Dim ub As New UriBuilder("http://someurl.com/uploader")
Dim c As New WebClient
AddHandler c.OpenWriteCompleted, AddressOf
MyOpenWriteCompletedEventHandler
c.OpenWriteAsync(ub.Uri)
End Sub
Public Sub MyOpenWriteCompletedEventHandler(ByVal sender As Object, ByVal
e As OpenWriteCompletedEventArgs)
PushData(Data, e.Result)
e.Result.Close()
Data.Close()
End Sub
On the OpenWriteComplete event handler, what variable do I pass as Data?
Also which handler does the openwriteasync function require to monitor the current progress, does this method also trigger UploadProgressChanged event in the webclient object?
Thanks for your time.
EDIT:
private void UploadFiles(MyFileInfo mfi, System.IO.FileStream data)
{
UriBuilder ub = new
UriBuilder("http://localhost:3793/receiver.ashx");
ub.Query = string.Format("filename={0}&name={1}&address={2}&email=
{3}&golfid={4}", mfi.Name, fixText(txtName.Text), fixText(txtAdress.Text),
fixText(txtEmail.Text), fixText(txtGolfID.Text));
//ub.Query = string.Format("filename={0}", mfi.Name);
WebClient wc = new WebClient();
wc.OpenWriteCompleted += (sender, e) =>
{
PushData(data, e.Result, mfi);
e.Result.Close();
data.Close();
lbl.Text = "Fil(er) uppladdade!";
};
wc.OpenWriteAsync(ub.Uri);
}
Here was the original C# code for the part I tried to convert - I have looked all over for a working VB.NET example it really seems one doesn't exist!
Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
target = value
Return value
End Function
Private Sub PushData(ByVal input As Stream, ByVal output As Stream)
Dim buffer As Byte() = New Byte(4095) {}
Dim bytesRead As Integer
While (InlineAssignHelper(bytesRead, input.Read(buffer, 0,
buffer.Length))) <> 0
AddLog("Writing..")
output.Write(buffer, 0, bytesRead)
End While
End Sub
Private Sub UploadFileStream(uploadURL As String)
Dim ub As New UriBuilder(uploadURL)
AddLog("Uploading to: " & uploadURL)
ub.Query = String.Format("file1={0}", "D:\test.flv")
Dim c As New WebClient
AddHandler c.OpenWriteCompleted, AddressOf OpenWriteCallback
'AddHandler c.UploadProgressChanged, AddressOf OpenWriteProgress
c.OpenWriteAsync(ub.Uri)
End Sub
Public Sub OpenWriteCallback(ByVal sender As Object, ByVal e As
OpenWriteCompletedEventArgs)
Dim fs As FileStream = File.OpenRead("D:\test.flv")
AddLog("cb")
PushData(fs, e.Result)
If fs Is Nothing Then
fs.Close()
AddLog("done")
MsgBox(e.Result)
End If
End Sub
Given your comments I have come up with this however, it seems to say it's writing but when looking at outgoing traffic there is no upload going on or any response from the server, until it stops or crashes with a memory error, any ideas why this doesn't work?
Thank you

vb.net, Termie and threading and "Object reference not set to an instance of an object"

I'm porting a subset of Termie (C# version of Termite) to vb.net. I've worked with this app in C# and adapted it to do some work for me successfully. This time though, I need to fold it into a vb.net app and I'm running into the "Object reference not set to an instance of an object" error when the app receives from the serial port.
Here's the code that fails
Private Sub ReadPort()
While _keepReading
If _serialPort.IsOpen Then
Dim readBuffer As Byte() = New Byte(_serialPort.ReadBufferSize) {}
Try
' If there are bytes available on the serial port,
' Read returns up to "count" bytes, but will not block (wait)
' for the remaining bytes. If there are no bytes available
' on the serial port, Read will block until at least one byte
' is available on the port, up until the ReadTimeout milliseconds
' have elapsed, at which time a TimeoutException will be thrown.
Dim count As Integer = _serialPort.Read(readBuffer, 0, _serialPort.ReadBufferSize)
Dim SerialIn As [String] = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count)
DataReceived(SerialIn)
Catch generatedExceptionName As TimeoutException
Catch ex As Exception
MsgBox(ex.Message)
End Try
Else
Dim waitTime As New TimeSpan(0, 0, 0, 0, 50)
Thread.Sleep(waitTime)
End If
End While
End Sub`
DataReceived(SerialIn) throws the error.
Here's how it's designated as a delegate
'begin Observer pattern
Public Delegate Sub EventHandler(param As String)
Public DataReceived As EventHandler
Public StatusChanged As EventHandler
I'm likely not getting something obvious but the various flavors of threading creation and interaction are fuzzy to me and I haven't been able to get past my error.
Any ideas why this throws the ubiquitous "Object reference not set..." error. And yes, I do get that the object is not set. ;)
Here's what I'm attempting to call:
Friend Delegate Sub StringDelegate(ByVal strData As String)
Public Sub ReceiveFromServer(ByVal strDataIn As String)
If Me.InvokeRequired() Then
' InvokeRequired: We're running on the background thread. Invoke the delegate.
Me.Invoke(New StringDelegate(AddressOf ReceiveFromServer), New Object() {strDataIn})
Else
Dim i As Integer = 0
End If
End Sub
Since getting done ultimately trumps maintaining consistency, I went ahead and restructured the delegate implementation. The routine listening on the serial port now looks like this:
Public Delegate Sub DataReceived(ByVal MsgString As String)
Public Delegate Sub EventHandler(param As String)
(EventHandler is used elsewhere)
And:
Private Sub ReadPort()
Dim MessagePump As DataReceived
MessagePump = AddressOf Serial_Port_UI.ReceiveFromServer
While _keepReading
If _serialPort.IsOpen Then
Dim readBuffer As Byte() = New Byte(_serialPort.ReadBufferSize) {}
Try
' If there are bytes available on the serial port,
' Read returns up to "count" bytes, but will not block (wait)
' for the remaining bytes. If there are no bytes available
' on the serial port, Read will block until at least one byte
' is available on the port, up until the ReadTimeout milliseconds
' have elapsed, at which time a TimeoutException will be thrown.
Dim count As Integer = _serialPort.Read(readBuffer, 0, _serialPort.ReadBufferSize)
Dim SerialIn As [String] = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count)
MessagePump.Invoke(SerialIn)
'DataReceived(SerialIn)
Catch generatedExceptionName As TimeoutException
Catch ex As Exception
MsgBox(ex.Message)
End Try
Else
Dim waitTime As New TimeSpan(0, 0, 0, 0, 50)
Thread.Sleep(waitTime)
End If
End While
End Sub
This all produces the desired strings into the target Form/class like this:
Public Delegate Sub StringDelegate(ByVal strData As String)
Public Sub ReceiveFromServer(ByVal strDataIn As String)
If Me.InvokeRequired() Then
' InvokeRequired: We're running on the background thread. Invoke the delegate.
Me.Invoke(New StringDelegate(AddressOf ReceiveFromServer), New Object() {strDataIn})
Else
Dim i As Integer = 0
End If
End Sub
Thanks Hans for the help.

The IAsyncResult object was not returned from the corresponding asynchronous method on this class

I have software that connects over TCP/IP to 4 differents devices (Weigher and 3 barcode scanners). About once or twice a day, I get an error that make the software crash:
System.ArgumentException: The IAsyncResult object was not returned
from the corresponding asynchronous method on this class. Parameter
name: asyncResult at
System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
I really don't understand why and I can't figure it out.
Here the code to connect to device
client = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
port = RemotePort
ipHostInfo = Dns.Resolve(RemoteHostName)
ipAddress = ipHostInfo.AddressList(0)
Dim remoteEP As New IPEndPoint(ipAddress, port)
client.Connect(remoteEP)
If client.Connected Then
Dim state As New StateObject
state.workSocket = client
currentAsyncResult = client.BeginReceive(state.buffer, 0, state.BufferSize, 0, AddressOf sockDataArrival, state)
RaiseEvent onConnect()
End If
The StateObject class:
Public Class StateObject
Public workSocket As Socket = Nothing
Public BufferSize As Integer = 256
Public buffer(BufferSize) As Byte
Public sb As New StringBuilder()
End Class
And the dataArrival event:
Private Sub sockDataArrival(ByVal ar As IAsyncResult)
Dim state As StateObject = CType(ar.AsyncState, StateObject)
Dim client As Socket = state.workSocket
Dim bytesRead As Integer
If ar IsNot currentAsyncResult Then
Exit Sub
End If
Try
bytesRead = client.EndReceive(ar)
Catch
Exit Sub
End Try
Try
Dim Data() As Byte = state.buffer
If bytesRead = 0 Then
client.Shutdown(SocketShutdown.Both)
client.Close()
RaiseEvent onDisconnect()
Exit Sub
End If
ReDim state.buffer(state.BufferSize)
currentAsyncResult = client.BeginReceive(state.buffer, 0, state.BufferSize, 0, New AsyncCallback(AddressOf sockDataArrival), state)
RaiseEvent onDataArrival(Data, bytesRead)
Catch
RaiseEvent onError(Err.Description)
Exit Sub
End Try
End Sub
Is there anything I'm doing wrong here? How can I fix this error?
thanks a lot for your time and help