TCP/ip recive connection but locks up when trying to read data - vb.net

Please can someone tell me why my server locks up when my rclient connects and try to send data?
If I comment out:
Dim bytes(rclient.ReceiveBufferSize) As Byte
rstream.Read(bytes, 0, CInt(rclient.ReceiveBufferSize))
RString = Encoding.ASCII.GetString(bytes)
TextBox2.Text = RStringcode here
and just have
TextBox2.Text = ("Remote connected")
the server picks up on a connected client so it must be something i have done with the .read ?
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Imports System.IO.Ports
Dim tclient As New TcpClient
Dim tstream As NetworkStream
Dim rclient As New TcpClient
Dim rstream As NetworkStream
Dim sArray() As String
Dim Tricopter As New TcpListener(2000)
Dim Remote As New TcpListener(2001)
Dim myVal As String
Dim TricopterThread As New Thread(AddressOf TricopterThreadSub)
Dim RemoteThread As New Thread(AddressOf RemoteThreadSub)
Dim tre(tclient.ReceiveBufferSize) As Byte
Dim TState = False
Dim RState = False
Dim SerialSwitch = False
Dim Toggle = False
Dim TString As String
Dim RString As String
Dim Remo(rclient.ReceiveBufferSize) As Byte
Dim TSendText() As Byte
Private Sub RemoteThreadSub()
rclient = Remote.AcceptTcpClient
rstream = rclient.GetStream
RState = True
End Sub
Private Sub TricopterThreadSub()
tclient = Tricopter.AcceptTcpClient
tstream = tclient.GetStream
TState = True
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
CheckForIllegalCrossThreadCalls = False
Tricopter.Start()
Remote.Start()
Timer1.Start()
TricopterThread.Start()
RemoteThread.Start()
End Sub
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
myVal = ">" & TrackBar1.Value & "," & TrackBar2.Value & "," & TrackBar3.Value & "," & TrackBar4.Value & "/n"
Try
If TState = False Then
TextBox1.Text = ("No tricopter connected")
Else
TextBox1.Text = myVal
If Toggle = False Then
TSendText = Encoding.ASCII.GetBytes(myVal)
End If
If Toggle = True Then
TSendText = Encoding.ASCII.GetBytes(RString)
End If
tstream.Write(TSendText, 0, TSendText.Length)
End If
If RState = False Then
TextBox2.Text = ("No Remote connected")
Else
' TextBox2.Text = ("Remote connected")
Dim bytes(rclient.ReceiveBufferSize) As Byte
rstream.Read(bytes, 0, CInt(rclient.ReceiveBufferSize))
RString = Encoding.ASCII.GetString(bytes)
TextBox2.Text = RString
End If
Catch ex As Exception
End Try
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If Toggle = False Then
CheckBox1.CheckState = 1
Toggle = True
Else
CheckBox1.CheckState = 0
Toggle = False
End If
End Sub
End Class

You are assuming that reads return a specific amount of data such as ReceiveBufferSize. That is not true. A read returns at least one byte, that is all.
Just to clarify, TCP does not support message based transfer.
The correct way to read depends on the protocol. If the exact number of bytes expected is known you need to read in a loop until so many bytes are received (or use BinaryReader which does that for you).
For a line based protocol you can use StreamReader.ReadLine which again automates the looping.
ReceiveBufferSize is completely unrelated to how much data is available or will come.
DataAvailable is how much data can be read right now without blocking. But more data might come. It is almost always a bug to use it. Might return 0 at any time even if data comes in 1ms later.

EDIT: For those of you who have downloaded/tested this already, I made a bugfix to the classes so you'll need to redownload the sources if you're going to use them again.
If you want to perform proper data transfer you'll need to use a more reliable method than just simply reading random data. And as usr pointed out: the TcpClient.ReceiveBufferSize property does not tell you how much data there is to receive, nor how much data there is sent to you. ReceiveBufferSize is just a variable indicating how many bytes you are expected to get each time you read incoming data. Read the MSDN page about the subject for more info.
As for the data transfer, I've created two classes which will do length-prefixed data transfer for you. Just import them to your project and you'll be able to start using them immediatelly. Link: http://www.mydoomsite.com/sourcecodes/ExtendedTcpClient.zip
Example usage
Server side
First declare a new variable for ExtendedTcpClient, and be sure to
include WithEvents in the declaration.
Dim WithEvents Client As ExtendedTcpClient
Then you just need to use a normal TcpListener to check for
incoming connections. The TcpListener.Pending() method can be
checked in for example a timer.
When you are to accept a new TcpClient, first declare a new
instance of the ExtendedTcpClient. The class requires to have a
form as it's owner, in this application Me is the current form.
Then, use the ExtendedTcpClient.SetNewClient() method with
Listener.AcceptTcpClient() as it's argument to apply the
TcpClient from the listener.
If Listener.Pending() = True Then
Client = New ExtendedTcpClient(Me)
Client.SetNewClient(Listener.AcceptTcpClient())
End If
After that you won't be needing the timer anymore, as the
ExtendedTcpClient has it's own thread to check for data.
Now you need to subscribe to the PacketReceived event of the
client. Create a sub like so:
Private Sub Client_PacketReceived(sender As Object, e As ExtendedTcpClient.PacketReceivedEventArgs) Handles Client.PacketReceived
End Sub
In there you can for example output the received packet as text into
a TextBox. Just check if the packet header is PlainText and then
you can convert the received packets contents (which is an array of
bytes, accessed via e.Packet.Contents) to a string and put it in
the TextBox.
If e.Packet.Header = TcpMessagePacket.PacketHeader.PainText Then
TextBox1.AppendText("Message recieved: " & System.Text.Encoding.Default.GetString(e.Packet.Contents) & Environment.NewLine)
End If
Lastly, when closing the form you just need to disconnect the client.
Private Sub ServerWindow_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
If Client IsNot Nothing Then Client.Disconnect()
End Sub
And that's it for the server side.
Client side
For the client side you will only be needing a normal TcpClient (unless you don't want to receive data there too).
Dim Client As New TcpClient
Then connect to the server via the IP and port you've given the listener.
Client.Connect("127.0.0.1", 5555) 'Connects to localhost (your computer) at port 5555.
Now if you want to send plain text to the server you'd do something like this:
Dim MessagePacket As New TcpMessagePacket(System.Text.Encoding.Default.GetBytes(TextBox2.Text), TcpMessagePacket.PacketHeader.PainText)
MessagePacket.Send(Client) 'Client is the regular TcpClient.
And now everything should be working!
Link to a complete example project: http://www.mydoomsite.com/sourcecodes/TCP%20Messaging%20System.zip
If you want to add more headers to the class, just open TcpMessagePacket.vb and add more values in the PacketHeader enum (located in the region called Constants).
Hope this helps!
Screenshot from the example project
(Click the image for larger resolution)

Related

Trigger richtextbox textchanged event until it detects certain text

I am working on serialport that can send and received certain commands. I would like to implement a retry feature which will allow me (the client) to resend data until the device (server) received and send a response to me.
Because of that I created a simple code that can illustrate this kind of function.
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
RichTextBox1.AppendText(Environment.NewLine & "Sample")
End Sub
Private Sub RichTextBox1_TextChanged(sender As Object, e As EventArgs) Handles RichTextBox1.TextChanged
Console.WriteLine("Trigger textchanged")
Dim totalLines As Integer = Me.RichTextBox1.Lines.Length
Dim lastLine As String = Me.RichTextBox1.Lines(totalLines - 1)
Dim CSTAT_Check As Boolean = lastLine Like "*Sample*"
If CSTAT_Check = True Then
RichTextBox1.AppendText(Environment.NewLine & "Sample")
End If
End Sub
End Class
The way it works is like this, I will clicked the button to append a sample string to richtextbox then the richtextbox textchange_event will be triggered causing it to resend the sample string to itself and will causes it to trigger another textchange_event and so on and so forth until the device received the sample string which in return the device (server) will send a sample_accepted string to my device (client) and because the textchanged_event doesnt detect the sample string in the last line of richtextbox it will no longer send another sample string to richtextbox.
It's little hard to understand so I will create a simple diagram
Client (Me) Server (Device)
Send sample string Doesn't detected
Send sample string again Doesn't detected again
Send sample string again Doesn't detected again
Send sample string again Doesn't detected again
Send sample string again Detected sample will send sample_accepted
Client Will no longer send sample string because the server detected it already.
The problem in my code is it seems like it doesn't trigger the textchanged_event again after its first trigger.
???If you change a property inside the code that responds to that property being changed, another changed event will not fire.??? You need to manually trigger the textchanged event after you make the changes.
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
RichTextBox1.AppendText(Environment.NewLine & "Sample")
End Sub
Private Sub RichTextBox1_TextChanged(sender As Object, e As EventArgs) Handles RichTextBox1.TextChanged
Console.WriteLine("Trigger textchanged")
Dim totalLines As Integer = Me.RichTextBox1.Lines.Length
Dim lastLine As String = Me.RichTextBox1.Lines(totalLines - 1)
Dim CSTAT_Check As Boolean = lastLine Like "*Sample*"
If CSTAT_Check = True Then
RichTextBox1.AppendText(Environment.NewLine & "Sample")
RichTextBox1_TextChanged(sender, New EventArgs())
End If
End Sub
End Class

Serial read value does not change

I am trying to read serial data that Arduino is sending but it keeps giving me over and over the first string that it receives even if I change the Arduino output. Why does it give me only first value and not update?
Module Module1
Sub Main()
Dim com4 As IO.Ports.SerialPort = Nothing
com4 = My.Computer.Ports.OpenSerialPort("COM4")
Dim incoming As String
Do
incoming = com4.ReadLine()
Console.WriteLine(incoming)
Threading.Thread.Sleep(250)
Loop
End Sub
End Module
It could be, that depending on your arduino sketch, it sends the same data out for some time, filling up the receive buffer, which is what gets read for some time in your Do loop.
Try using events
dim withevents com4 as serialport.......
sub datareceivedhandler (sender as object, e as eventargs) handles com4.datareceived
dim incoming as string = com4.readline
console.writeline(incoming)
end sub

how to send data to remote device over udp?

i am trying to establish connection between calamp lmu (gps tracking device) and my server.
i am using following code to send and receive data.
i am using timer1 to receive data from device
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
Try
Dim receiveBytes As [Byte]() = receivingUdpClient.Receive(remoteEP)
Dim returnData As String = BitConverter.ToString(receiveBytes)
txtLog.Text &= returnData.ToString & vbCrLf
Dim rep As New IPEndPoint(remoteEP.Address, C_DEVICE_LISTNING_PORT)
sktSocket = New Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
sktSocket.Connect(remoteEP.Address, 20510)
sktSocket.SendTimeout = 100
'Dim udc As New UdpClient
'udc.Send(sendBytes, sendBytes.Length, rep)
'receivingUdpClient.Client.Send(sendBytes)
Catch ex As Exception
End Try
End Sub
following two variables are class level:
Dim remoteEP As New IPEndPoint(IPAddress.Any, 0)
Dim sktSocket As Socket
i am using following code to send data to device:
Private Sub butSend_Click(sender As Object, e As EventArgs) Handles butSend.Click
Dim sendBytes() As Byte = Encoding.ASCII.GetBytes(txtCommand.Text)
sktSocket.Send(sendBytes)
End Sub
my code to receive data works fine. when device sends data timer1 displays it in a textbox. But when i send data to the ip address returned by receivingUdpClient.Receive, it does not reach to the device. however wireshark shows that data has been sent.
any help will be appriciated.
i solved my problem myself. actually nothing was wrong in the code. actually i was connecting the device using forwarded port. it was hindering inward traffic from device to my system. Assigning static ip direct to my system solved the problem.

Sending Commands to Telnet

I have a Form that I am making that lets me select a line and reset all of the port on the line. This is done through Telnet. I understand how to Socket and send the IP Address I wish to work with but what I do not understand is sending the commands to log in and reset the ports.
The set up is that when one of more check boxes are selected for the different line it calls on private sub to run a line before starting on the next one.
I have been web searching for a few days not. The last code I tried was the following:
Dim TelnetClient As TcpClient
Dim ThisStream As NetworkStream
Dim SendBuffer(128) As Byte
Dim ReadBuffer(128) As Byte
Dim ReturnVal As String
Dim ReturnLength As Integer
TelnetClient = New TcpClient("Ip Address", 23)
ThisStream = TelnetClient.GetStream
SendBuffer = System.Text.Encoding.ASCII.GetBytes("Username")
ThisStream.Write(SendBuffer, 0, SendBuffer.Length)
ReturnLength = ThisStream.Read(ReadBuffer, 0, ReadBuffer.Length)
ReturnVal = System.Text.Encoding.ASCII.GetString(ReadBuffer)
SendBuffer = System.Text.Encoding.ASCII.GetBytes("Password")
ThisStream.Write(SendBuffer, 0, SendBuffer.Length)
I have been going around in circle trying to understand this.
I have tried doing the Telnet through cmd.exe but I keep coming back with errors and abandoned that route.
I have also seen using code to find words in the Telnet.
Example:
If message.ToString.EndsWith("login:") Then
Await WriteStringAsync("username", stream
But not 100% sure on how to fully adapt it to what I can use.
Any help is appreciated.
Thank you.
Edit: Addition Info.
I have the following at the top of the code list
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
I am new to using Telnet with vb.net. However, why is it so hard to do this in vb.net and in Cmd.exe it only takes six commands?
Okey mate , here is the code you are looking for , but focus :
Dim Full_Stop As String = ""
Dim TelnetClient As New TcpClient
Private Sub StartButton_Click(sender As Object, e As EventArgs) Handles StartButton.Click
TelnetClient.Connect("IP ADDRESS", 23) 'Connecting to the IP Given
Send_Sub("Start Connection Command")
Dim thr As New Threading.Thread(AddressOf Receive_thread)
thr.Start()
End Sub
Private Sub StopButton_Click(sender As Object, e As EventArgs) Handels StopButton.Click
Full_Stop = "Stop"
TelnetClient.Close()
End Sub
Sub Send_Sub(ByVal msg As String)
Dim byt_to_send() As Byte = System.Text.Encoding.ASCII.GetBytes(msg)
TelnetClient.Client.Send(byt_to_send, 0, byt_to_send.Length, SocketFlags.None)
End Sub
Sub Receive_thread()
re:
If Full_Stop = "Stop" Then Exit Sub 'If you set Full_Stop string to "Stop" the thread will end
If TelnetClient.Client.Available < 0 Then 'Check if there is any Data to receive
Dim byt_to_receive(TelnetClient.Available - 1) As Byte
TelnetClient.Client.Receive(byt_to_receive, 0, byt_to_receive.Length, SocketFlags.None)
Dim String_From_Byte As String = System.Text.Encoding.ASCII.GetString(byt_to_receive)
If String_From_Byte = "login:" Then 'If the telnet asks you to Enter the login name the Send_Sub will do the job
Send_Sub("username")
ElseIf String_From_Byte = "password:" Then 'If the telnet asks you to Enter the Password the Send_Sub will do the job
Send_Sub("password")
End If
End If
GoTo re 'this will NOT allow the thread to End by sending it back to re: statement, unless the Full_Stop is "Stop"
End Sub

Visual Basic UDPClient Server/Client model?

So I'm trying to make a very simple system to send messages from a client to a server (and later on from server to client as well, but baby steps first). I'm not sure exactly how to use UDPClient to send and receive messages (especially to receive them), mostly because I don't have anything triggering the ReceiveMessage() function and I'm not sure what would.
Source Code is at this link, go to File>Download. It is already built if you want to just run the exe.
So my question is basically: How can I easily use UDPClient, how can I get this system to work and what are some tips for executing this kind of connection? Anything I should watch out for (threading, issues with code,etc)?
Source.
You need first need to set up two UdpClients. One client for listening and the other for sending data. (You'll also need to pick a free/unused port number and know the IP address of your target - the machine you want to send data to.)
To set up the receiver,
Instantiate your UdpClient variable with the port number you chose earlier,
Create a new thread to avoid blocking while receiving data,
Loop over the client's receive method for as long as you want to receive data (the loop's execution should be within the new thread),
When you receive one lot of data (called a "packet") you may need to convert the byte array to something more meaningful,
Create a way to exit the loop when you want to finish receiving data.
To set up the sender,
Instantiate your UdpClient variable with the port number you chose earlier (you may want to enable the ability to send broadcast packets. This allows you to send data to all listeners on your LAN),
When you need to transmit data, convert the data to a byte array and then call Send().
I'd suggest that you have a quick skim read through this.
Here's some code to get you started off...
'''''''''''''''''''''''Set up variables''''''''''''''''''''
Private Const port As Integer = 9653 'Port number to send/recieve data on
Private Const broadcastAddress As String = "255.255.255.255" 'Sends data to all LOCAL listening clients, to send data over WAN you'll need to enter a public (external) IP address of the other client
Private receivingClient As UdpClient 'Client for handling incoming data
Private sendingClient As UdpClient 'Client for sending data
Private receivingThread As Thread 'Create a separate thread to listen for incoming data, helps to prevent the form from freezing up
Private closing As Boolean = False 'Used to close clients if form is closing
''''''''''''''''''''Initialize listening & sending subs'''''''''''''''''
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Me.Load
InitializeSender() 'Initializes startup of sender client
InitializeReceiver() 'Starts listening for incoming data
End Sub
''''''''''''''''''''Setup sender client'''''''''''''''''
Private Sub InitializeSender()
sendingClient = New UdpClient(broadcastAddress, port)
sendingClient.EnableBroadcast = True
End Sub
'''''''''''''''''''''Setup receiving client'''''''''''''
Private Sub InitializeReceiver()
receivingClient = New UdpClient(port)
Dim start As ThreadStart = New ThreadStart(AddressOf Receiver)
receivingThread = New Thread(start)
receivingThread.IsBackground = True
receivingThread.Start()
End Sub
'''''''''''''''''''Send data if send button is clicked'''''''''''''''''''
Private Sub sendBut_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles sendBut.Click
Dim toSend As String = tbSend.Text 'tbSend is a textbox, replace it with whatever you want to send as a string
Dim data() As Byte = Encoding.ASCII.GetBytes(toSend)'Convert string to bytes
sendingClient.Send(data, data.Length) 'Send bytes
End Sub
'''''''''''''''''''''Start receiving loop'''''''''''''''''''''''
Private Sub Receiver()
Dim endPoint As IPEndPoint = New IPEndPoint(IPAddress.Any, port) 'Listen for incoming data from any IP address on the specified port (I personally select 9653)
While (True) 'Setup an infinite loop
Dim data() As Byte 'Buffer for storing incoming bytes
data = receivingClient.Receive(endPoint) 'Receive incoming bytes
Dim message As String = Encoding.ASCII.GetString(data) 'Convert bytes back to string
If closing = True Then 'Exit sub if form is closing
Exit Sub
End If
End While
End Sub
'''''''''''''''''''Close clients if form closes''''''''''''''''''
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
closing = True 'Tells receiving loop to close
receivingClient.Close()
sendingClient.Close()
End Sub
Here are a few other exmples: Here, here, here and here.
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