Stream from TCPClient catching data from next packet - vb.net

I'm developing some software that listens for events from another program via TCP.
The events come in a string formatted like this: "value1;value2;value3"
The problem is that sometimes a single character from value1 gets read onto the previous event so i get something like this:
value1;value2;value3v
alue1;value2;value3
How can i make it know where each message begins and ends?
Here is the code:
Dim client As New TcpClient()
Sub listen()
Dim networkStream As Stream = client.GetStream()
While True
Dim bytes(client.ReceiveBufferSize) As Byte
Dim size As Integer = 0
size = networkStream.Read(bytes, 0, CInt(client.ReceiveBufferSize))
'process event here
End While
End Sub
This is all done on a thread of it's own.

Sender has to included the length of each data its sending so at receiver you will receive upto the provided length. Easier option would be to include NULL termination so that you can read it as individual string at receiver end.

Related

Asynchronous tcp client dilemma

I am trying to write an asynchronous TCP client with VB.NET to communicate with a thermal printer (Intermec Px4e), but I am facing an issue regarding the way I receive the data from it.
Previously I was using a synchronous client, but I had the problem regarding the response not coming instantly from the printer, so after sending data and then checking for available bytes I was able to read only the first bytes of the response.
This is the synchronous client I am actually using:
Private Function sendBytesEthernet(ByVal data As Byte(), ByVal withResponse As Boolean) As PrinterPacket(Of Byte())
Dim socketStatus As Net.Sockets.SocketError
Dim response As New PrinterPacket(Of Byte()) With {.StatusCode = CommandStatusCode.ERR, _
.Response = String.Empty, _
.Data = data}
Try
If Me.oggettoSocket.Client.Poll(2000, Sockets.SelectMode.SelectWrite) Then
Me.oggettoSocket.Client.Send(data, 0, data.Length, 0, socketStatus)
Else
Throw New Exception(socketStatus.ToString())
End If
If withResponse Then
Dim bytesAvailable As UInteger = Me.oggettoSocket.Available
If bytesAvailable > 0 Then
Dim dataBuffer(bytesAvailable - 1) As Byte
Dim bytesReceived As UInteger = Me.oggettoSocket.Client.Receive(dataBuffer, 0, dataBuffer.Length, Sockets.SocketFlags.None, socketStatus)
If socketStatus = Sockets.SocketError.Success Then
If bytesReceived > 0 Then
response.Response = System.Text.Encoding.UTF8.GetString(dataBuffer)
response.StatusCode = CommandStatusCode.OK
Return response
End If
Else
Throw New Exception(socketStatus.ToString())
End If
End If
End If
If socketStatus = Sockets.SocketError.Success Then
response.StatusCode = CommandStatusCode.OK
Else
Throw New Exception(socketStatus.ToString())
End If
Catch ex As Exception
Select Case ex.Message
Case "ConnectionReset"
Me.oggettoSocket.Client.Disconnect(True)
Me.openSocket()
End Select
response.Response = ex.Message
End Try
Return response
End Function
What happens is that If send a command to the printer that involves a big response from it, I receive the full response splitted in several packets, so with this function I am only able to read the first packet.
For example:
I send to the printer the command to list all the fonts available (FONTS[CRLF])
The printer responds, but it sends the first 54 bytes and then truncates the list.
The printer sends the second packet, this time 1560 bytes long, but it still truncates the list.
Lastly the printer completes the list with last 25 bytes.
Basically with my function I am only able to read the first 54 bytes, unless I don't use a loop to check the availability on the receive buffer in conjunction with a Thread.Sleep() call, since the client is synchronous and I have to wait for the printer to send every packet, but I find this method really inefficient.
At this point I concluded that I had to move to an asynchronous approach, due to the nature of the TCP protocol. I copied an example of async client on the MSDN and it works, but it doesn't fit my use case. Why?
Because I actually have a thread that constantly communicate with the printer through the socket, and I open the connection when the thread starts and close it when the thread ends. If use, for example, the BeginReceive callback used in the MSDN example, that call will never end until I close the connection client side. This cannot take place, since I keep the connection open to the printer and I can't establish a new TCP connection for every thread cycle (that would be a waste in terms of network traffic).
I hope I have explained the problem well.

TCP Listener connection is closed before acknowledgement/response can be sent

I am writing a TCP listener (server) that needs to receive messages and send back an acknowledgement. Pretty basic stuff. There are literally dozens of examples out there, including MSDN, from which I copied much of my code. I can receive the message no problem. The problem comes when I try to send back the response. The sending client (Corepoint HL7 engine) reports the following error:
The connection was closed before a response was received
I have tested my service with my own TCP sending test app (written using code copied from MSDN) and it works. But when I receive messages from Corepoint, the response does not go back.
Below is my code. Does anyone have any idea why the NetworkStream.Write method is not actually sending the data (or why the client is not receiving it)? I've tried every idea I've found in other posts that are similar to my problem, and nothing is working. Am I doing something wrong, or is something wrong in the configuration of Corepoint?
Sub Main()
listenThread.Start()
End Sub
Private serverSocket As TcpListener
Dim listenThread As New Thread(New ThreadStart(AddressOf ListenForClients))
Private Sub ListenForClients()
Dim port As Int32 = '(pick a port #)
Dim localIP As IPAddress = 'enter your IP
serverSocket = New TcpListener(localIP, port)
serverSocket.Start()
While True 'blocks until a client has connected to the server
Dim client As TcpClient
If serverSocket.Pending Then
client = serverSocket.AcceptTcpClient
'tried these 2 settings with no effect
'client.NoDelay = True
client.Client.NoDelay = True
ProcessIncomingMessageSocketTCPClient(client) 'I was doing this in a separate thread but temporarily kept it on this thread to eliminate threading as the possible cause (but no luck)
client.Close()
Else
Threading.Thread.Sleep(1000) 'wait 1 second and poll again
End If
End While
End Sub
Private Sub ProcessIncomingMessageSocketTCPClient(ByRef objClient As TcpClient)
Dim strMessageText As String
Dim clientStream As NetworkStream
Dim msgBuffer(4096) As Byte
Dim numberOfBytesRead As Integer
Dim strChunk As String
Dim strCompleteMessage As New Text.StringBuilder
Dim sendBytes As Byte()
clientStream = objClient.GetStream()
Do
numberOfBytesRead = clientStream.Read(msgBuffer, 0, msgBuffer.Length)
strChunk = Encoding.ASCII.GetString(msgBuffer, 0, numberOfBytesRead)
strCompleteMessage.AppendFormat("{0}", strChunk)
Loop While clientStream.DataAvailable
strMessageText = strCompleteMessage.ToString
sendBytes = Encoding.ASCII.GetBytes("I received a message from you")
clientStream.Write(sendBytes, 0, sendBytes.Length)
objClient.Close() 'tried it with and without this line
End Sub
It turns out that nothing is wrong with my code. The TCP was and is working correctly. This application is an HL7 listener and I was missing the MLP wrapping around my ACK. As soon as I added that, the sending application accepted my ACK and all is good.

vb.net async stream write/read problems

I’m after some help with a problem i have been working on for the past few days but i can't seem to get it to work correctly.
I have multiple clients connecting to a server, the server needs to keep a list of connected clients and remove them from the list if the client disconnects, I have this bit working correctly.
Once a client is connected the server may call upon the client for some information about its state by sending a few bytes of data to which the client responds with 2 bytes of data. Based on this response the server will complete any one of number of different tasks.
I have had this working synchronously, though now I am trying to make the function asynchronous and I’m running in to an issue. The best I can tell my function that request the information from the client is completing before the client responds and is returning incorrect data, if I print the data received from the client I can see it is the correct information.
This is my 1st time working with asynchronous functions and connections so it’s highly likely I’ve got it completely wrong, I have looked at lots of example code but I can seem to find any that sheads any light on my issue.
This is what I have at the moment:
'create the collection instance to store connected clients
Private clients As New List(Of TcpClient)
'declare a variable to hold the listener instance
Private listener As TcpListener
'declare a variable to hold the cancellation token source instance
Private tokenSource As CancellationTokenSource
Private recvTsk As Task
Private Rreply As New List(Of Byte)
Private Async Sub startTCPListenerServer()
'get port from ini file
Dim netPort = SettingsIniFile.GetString("Setup", "NetworkPort", "")
While True
'create a new cancellation token source instance
tokenSource = New CancellationTokenSource
'create a new listener instance bound to the desired address and port
listener = New TcpListener(IPAddress.Any, netPort)
'start the listener
listener.Start()
While True
Try
Dim client As TcpClient = Await listener.AcceptTcpClientAsync
clients.Add(client)
Dim clientIP As String = client.Client.RemoteEndPoint.ToString
ListBox1.Items.Add(clientIP)
Try
'begin reading from the client's data stream
Using stream As NetworkStream = client.GetStream
Dim buffer(client.ReceiveBufferSize - 1) As Byte
Dim read As Integer = 1
'if read is 0 client has disconnected
While read > 0
recvTsk = New Task(Sub()
For i = 0 To read - 1
'data recived at this point is correct
Rreply.Add(buffer(i))
Next
End Sub, tokenSource.Token)
read = Await stream.ReadAsync(buffer, 0, buffer.Length, tokenSource.Token)
recvTsk.Start()
End While
'client gracefully closed the connection on the remote end
End Using
Catch ocex As OperationCanceledException
'the expected exception if this routines's async method calls honor signaling of the cancelation token
'*NOTE: NetworkStream.ReadAsync() will not honor the cancelation signal
Catch odex As ObjectDisposedException
'server disconnected client while reading
Catch ioex As IOException
'client terminated (remote application terminated without socket close) while reading
Finally
'ensure the client is closed - this is typically a redundant call, but in the
'case of an unhandled exception it may be necessary
'remove the client from the list of connected clients
clients.Remove(client)
client.Close()
ListBox1.Items.Remove(clientIP)
'remove the client's task from the list of running tasks
'clientTasks.Remove(client.Task)
End Try
Catch odex As ObjectDisposedException
'listener stopped, so server is shutting down
Exit While
End Try
End While
For i As Integer = clients.Count - 1 To 0 Step -1
clients(i).Close()
Next
tokenSource.Dispose()
End While
'signal any processing of current clients to cancel (if listening)
tokenSource.Cancel()
'abort the current listening operation/prevent any new connections
listener.Stop()
'End If
End Sub
Async Function sendToPod(message() As Byte, podNum As Integer) As Task(Of Byte)
If clients.Count = 0 Then
Else
Dim podIP As String
'get ip address as string from ini file
podIP = SettingsIniFile.GetString("NetworkSettings", "Pod" & podNum & "IP", "")
Dim currentClient As TcpClient = Nothing
For Each client As TcpClient In clients
Dim clientIP As String = (CType(client.Client.RemoteEndPoint, IPEndPoint).Address.ToString())
If clientIP = podIP Then
currentClient = client
End If
Next
If currentClient IsNot Nothing Then
'get the current client, stream, and data to write
Dim stream As NetworkStream = currentClient.GetStream
Dim buffer() As Byte = message
'wait for the data to be sent to the remote
Await stream.WriteAsync(buffer, 0, buffer.Length)
recvTsk.Wait()
Return Rreply(1)
End If
End If
End Function
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Try
Me.TextBox1.Text = Await sendToPod({"&HAB", "&HAD", "&HFF", "&HFF"}, 1)
Catch
End Try
End Sub
End Class

vb.net: listbox.items.add() throws exception in same class

I'm not even sure I understand this situation enough to come up with a proper title. I come from a modest understanding of VB6 and having to climb a steep learning curve for VB 2010.
I am trying to create a multi-client server program that will communicate with my Enterprise iPhone app. I found a relatively simple example to build upon here: http://www.strokenine.com/blog/?p=218. I have been able to modify the code enough to make it work with my app, but with one glitch: I can't get access to the controls on the form to add items, even though the method is invoked within the form's class. (I tried this on the original code too, and it does the same thing. I don't know how the author managed to get it to work.)
Here's the code segment in question:
Public Class Server 'The form with the controls is on/in this class.
Dim clients As New Hashtable 'new database (hashtable) to hold the clients
Sub recieved(ByVal msg As String, ByVal client As ConnectedClient)
Dim message() As String = msg.Split("|") 'make an array with elements of the message recieved
Select Case message(0) 'process by the first element in the array
Case "CHAT" 'if it's CHAT
TextBox3.Text &= client.name & " says: " & " " & message(1) & vbNewLine 'add the message to the chatbox
sendallbutone(message(1), client.name) 'this will update all clients with the new message
' and it will not send the message to the client it recieved it from :)
Case "LOGIN" 'A client has connected
clients.Add(client, client.name) 'add the client to our database (a hashtable)
ListBox1.Items.Add(client.name) 'add the client to the listbox to display the new user
End Select
End Sub
Under Case "LOGIN" the code tries to add the login ID to the listbox. It throws an exception: "A first chance exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll" The listbox (all controls, for that matter) is in the same class, Server.vb and Server.vb [Design].
The data comes in from another class that is created whenever a client logs on, which raises the event that switches back to the Server class:
Public Class ConnectedClient
Public Event gotmessage(ByVal message As String, ByVal client As ConnectedClient) 'this is raised when we get a message from the client
Public Event disconnected(ByVal client As ConnectedClient) 'this is raised when we get the client disconnects
Sub read(ByVal ar As IAsyncResult) 'this will process all messages being recieved
Try
Dim sr As New StreamReader(cli.GetStream) 'initialize a new streamreader which will read from the client's stream
Dim msg As String = sr.ReadLine() 'create a new variable which will be used to hold the message being read
RaiseEvent gotmessage(msg, Me) 'tell the server a message has been recieved. Me is passed as an argument which represents
' the current client which it has recieved the message from to perform any client specific
' tasks if needed
cli.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf read, Nothing) 'continue reading from the stream
Catch ex As Exception
Try 'if an error occurs in the reading purpose, we will try to read again to see if we still can read
Dim sr As New StreamReader(cli.GetStream) 'initialize a new streamreader which will read from the client's stream
Dim msg As String = sr.ReadLine() 'create a new variable which will be used to hold the message being read
RaiseEvent gotmessage(msg, Me) 'tell the server a message has been recieved. Me is passed as an argument which represents
' the current client which it has recieved the message from to perform any client specific
' tasks if needed
cli.GetStream.BeginRead(New Byte() {0}, 0, 0, AddressOf read, Nothing) 'continue reading from the stream
Catch ' IF WE STILL CANNOT READ
RaiseEvent disconnected(Me) 'WE CAN ASSUME THE CLIENT HAS DISCONNECTED
End Try
End Try
End Sub
I hope I am making sense with all this. It all seems to bounce back and forth, it seems so convoluted.
I've tried using Me.listbox1 and Server.listbox1 and several other similar structures, but to no avail.
I'm reading a lot about Invoke and Delegates, but would that be necessary if the method and the control are in the same class? Or do I have a fundamental misperception of what a class is?
Many thanks for any help I can get.
Private Delegate Sub UpdateListDelegate(byval itemName as string)
Private Sub UpdateList(byval itemName as string)
If Me.InvokeRequired Then
Me.Invoke(New UpdateListDelegate(AddressOf UpdateList), itemName)
Else
' UpdateList
' add list add code
ListBox1.Items.Add(itemName)
End If
End Sub
Add above, then replace:
ListBox1.Items.Add(client.name)
to
UpdateList(client.name)
Does it work? check the syntax, may have typo as I type it.

Receiving and Processing HEX data from Serial

I'm new to this forum so I'm not sure if this is really the place to put this - feel free to move to a more appropriate forum if required.
Okay so I'm writing a simple application in VB 2010 that sends and receives HEX data to a serial device. Now I have the "sending" part down pat, however, I'm having difficulty with the receiving part. The device sends a constant stream of HEX data back to the application every second; I need the application to look at certain bytes of the received HEX string and do things accordingly within IF Statements (e.g. If byte 10 of the string is equal to "&10" then do ... ).
I'm not sure how to get this data, parse the correct byte out of it and then do something with it. :/ The code I've attempted to use for this (but does not work) is as follows:-
Private Sub SerialPort1_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles SerialPort1.DataReceived
Dim statusget() As String
Dim str1 As String
str1 = SerialPort1.ReadExisting()
statusget = str1.Split(",")
If statusget(10) = "&01" Then
btn_tx1.BackColor = Color.Red
btn_tx2.BackColor = SystemColors.Control
btn_tx3.BackColor = SystemColors.Control
btn_tx4.BackColor = SystemColors.Control
btn_tx5.BackColor = SystemColors.Control
btn_tx6.BackColor = SystemColors.Control
btn_tx7.BackColor = SystemColors.Control
btn_tx8.BackColor = SystemColors.Control
End If
End Sub
Additionally, I have a Rich-Text box that displays all data received from the device so I can monitor it. It works fine, except for the fact that it returns in a weird encoding (not HEX) such as "uZ!!!??".
Thank you for any help you can provide. :)
Edit - Code for Sending data:
Private Sub btn_tx1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_tx1.Click
Dim buftx1() As Byte = {&HF0, &H5A, &HF, &H5, &H1, &H11, &H0, &H0, &H0, &H0, &H0, &H0, &H30, &H37, &HFF}
SerialPort1.Write(buftx1, 0, buftx1.Length)
End Sub
Avoid thinking about "hex" data, as hex is merely a way to format binary data into a string that's easy to read by humans.
Clearly, your device is using a binary protocol; your Click event handler is proof of that. Which invariably means that it also returns binary data, not text. Which makes using SerialPort.ReadExisting() wrong, that reads text. The result of that is the garbage that you are now seeing, particularly the question marks. Those are produced because it doesn't know how to convert the bytes it receives to ASCII characters.
You must instead use SerialPort.Read() to fill a Byte(). Be sure to pay attention to the return value of Read(); that tells you how many bytes you got. The result will be less than the number of bytes you asked for. Keep calling Read() until you get the full response from the device according to the protocol rules.
Once you got the full response you can then process it using the data that the device returned. Displaying it in a RichTextBox is not terribly useful, other than for debugging purposes. You can use the BitConverter.ToString(Byte()) overload to generate a hex string from the bytes. Be sure to use Control.BeginInvoke() in your DataReceived event handler, as you cannot update the UI directly since the event runs on a worker thread.
If you need help with the protocol rules then contact the vendor of the device for support.