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
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 includeWithEvents
in the declaration.Then you just need to use a normal
TcpListener
to check for incoming connections. TheTcpListener.Pending()
method can be checked in for example a timer.When you are to accept a new
TcpClient
, first declare a new instance of theExtendedTcpClient
. The class requires to have a form as it's owner, in this applicationMe
is the current form.Then, use the
ExtendedTcpClient.SetNewClient()
method withListener.AcceptTcpClient()
as it's argument to apply theTcpClient
from the listener.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:In there you can for example output the received packet as text into a
TextBox
. Just check if the packet header isPlainText
and then you can convert the received packets contents (which is an array of bytes, accessed viae.Packet.Contents
) to a string and put it in theTextBox
.Lastly, when closing the form you just need to disconnect the client.
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).Then connect to the server via the IP and port you've given the listener.
Now if you want to send plain text to the server you'd do something like this:
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 thePacketHeader
enum (located in the region calledConstants
).Hope this helps!
Screenshot from the example project
(Click the image for larger resolution)
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.