TCP Client to Server communication - vb.net

All I'm looking for is a simple TCPClient/Listner example on Windows Form VB.Net. I'm a newbie and Microsoft TCPClient/Listner class examples are not what I am looking for. All I am looking is for the TCPClient to send a message and for a TCPListener to get the message and to send a message back "I got your message" ?
A little help would be great. I have some codes, but is only to send message to server and not back from server to client..
Any help will be very appreciated..

TCP communication is stream-based, which means it doesn't handle any packets. Due to this, messages that you receive might be either partial or lumped together.
You could for example send:
Hello!
How are you?
But you might receive:
Hello!How are you?
or:
Hello!How ar
e you?
(or something similar)
To fix this you must apply something called "length-prefixing". Length-prefixing (or length prefixing) means that before you send a message, you put its length (amount of characters/bytes) in the beginning of it. By doing so, the endpoint will know exactly how many bytes to read for each message. Thus there will be no problems with messages being partial or lumped together.
This is not the most straightforward thing to do as a beginner, as to get it to work properly on both sides you have to structure your code just right. So I've created two classes that will take care of this for you. See the examples below on how to use them for simple text message-based communication.
Link to source: http://www.mydoomsite.com/sourcecodes/ExtendedTcpClient.zip
Link to C# source : http://www.mydoomsite.com/sourcecodes/ExtendedTcpClient%20CSharp.zip
EDIT (2019-11-08)
Some time ago I made an upgraded version of this with a bit better code structure and error handling. For those of you interested, the new code can be downloaded here (VB.NET only):
https://www.mydoomsite.com/sourcecodes/ExtendedTcpClient%202.0.zip
Example usage
Note that in those examples Client does not refer to the client side, but to the TcpClient.
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 will need a TcpListener and a Timer to check for incoming connections.
Dim Listener As New TcpListener("0.0.0.0", 5555) 'Listen for any connection on port 5555.
Dim WithEvents Tmr As New System.Windows.Forms.Timer
Then you need to subscribe to the timer's Tick event.
Private Sub Tmr_Tick(sender As System.Object, e As System.EventArgs) Handles Tmr.Tick
End Sub
In there you check for incoming connections via the Listener.Pending() method. When you are to accept a connection you first declare a new
instance of the ExtendedTcpClient. The class requires to have a
form as its owner, in this application Me is the current form.
Then you use the ExtendedTcpClient.SetNewClient() method with
Listener.AcceptTcpClient() as its argument to apply the
TcpClient from the listener. Put this code in the Tmr_Tick sub:
If Listener.Pending() = True Then
Client = New ExtendedTcpClient(Me)
Client.SetNewClient(Listener.AcceptTcpClient())
End If
Now the client and server are connected to each other.
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
All received data are presented in an array of bytes.
In the PacketReceived sub you can 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.PlainText Then
TextBox1.AppendText("Message recieved: " & System.Text.Encoding.Default.GetString(e.Packet.Contents) & Environment.NewLine)
End If
System.Text.Encoding.Default.GetString() will convert a byte array to normal text.
In the PacketReceived sub you can also make it send "Message received" to the client.
Dim ResponsePacket As New TcpMessagePacket(System.Text.Encoding.Default.GetBytes("Message received."), TcpMessagePacket.PacketHeader.PlainText)
ResponsePacket.Send(Client.Client) 'Get the ExtendedTcpClient's underlying TcpClient.
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 do pretty much the same as the server side, though you won't be needing a TcpListener nor a Timer.
Dim WithEvents Client As New ExtendedTcpClient(Me) 'The current form as its owner.
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 text to the server you'd do something like this (in for example a button):
Dim MessagePacket As New TcpMessagePacket(System.Text.Encoding.Default.GetBytes(TextBox2.Text), TcpMessagePacket.PacketHeader.PlainText)
MessagePacket.Send(Client.Client)
TextBox2 includes the text you want to send.
Lastly, you will need to subscribe to the PacketReceived event here too to check for responses from the server. In there you receive text just like the server does.
Private Sub Client_PacketReceived(sender As Object, e As ExtendedTcpClient.PacketReceivedEventArgs) Handles Client.PacketReceived
If e.Packet.Header = TcpMessagePacket.PacketHeader.PlainText Then
TextBox1.AppendText(System.Text.Encoding.Default.GetString(e.Packet.Contents) & Environment.NewLine) 'Prints for example "Message received." from the server.
End If
End Sub
And now everything should be working!
Link to a complete example project (only client-to-server): http://www.mydoomsite.com/sourcecodes/TCP%20Messaging%20System.zip
Link to C# example: http://www.mydoomsite.com/sourcecodes/CSharp%20TCP%20Messaging%20System.zip
If you want to add more headers to the class (the headers indicate to you what kind of data each packet contains), 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

use callback to allow multiple connections to socket?

I am trying to create a VB.Net server program that has an external link to process data. The server will wait on port 7878 for a request to be made from a client. When the server receives a request it will send a string containing the state of all variables and send a string every second containing values for only changed variables. The server does not need to receive any information from the client for processing. I have successfully implemented everything with the exception of allowing for multiple requests at once. The basic program looks like this:
Class Server
Public Sub Main()
'thread start for ListenForRequests
End Sub
Public Sub ListenForRequests()
'code to set up socket
'bind socket
'listen
Using newConnection as socket.accept()
'do stuff
newConnection.send'stuff
End Using
End Sub
End Class
Can anyone tell me a SIMPLE way to implement a callback for async code? I think I need to move my 'do stuff and send stuff code into a new sub and fire a new thread upon socket.accept, but I can't figure out how to pass the newConnection through to a new thread.

Cannot get port to close

I am trying to fix an app that uses TCP to receive files. Unfortunately, I do not have the source code for the sending application. The problem that I am having is that after the first file is received, the second one is sent by the sending application, but it is not being picked up by the receiving application.
I believe the issue is that the socket is not being closed after receiving the file. I have a method that should close it, but _socket.Connected = false, so nothing is done. However, if I check the port, it is still bound, even after the socket is closed.
Private Sub CloseSocket(ByVal disconnect As Boolean)
If Not (_socket Is Nothing) Then
If (_socket.Connected = True) Then
_socket.Shutdown(SocketShutdown.Both)
If(disconnect) Then
_socket.Disconnect(True)
End If
End If
_socket.Close()
_socket = Nothing
End If
End Sub
I realize that I have not included much code, but the Listen method is quite large and convoluted. I can get additional code if it will provide insight. Any assistance would be greatly appreciated.
EDITED
The code that is used to check the port status is below:
Private Shared Function TcpIpGetPortStatus(ByVal port As Int32) As String
Dim properties As NetworkInformation.IPGlobalProperties
Dim listeners As Net.IPEndPoint()
Dim local As Net.IPEndPoint
Dim connections As NetworkInformation.TcpConnectionInformation()
Dim t As NetworkInformation.TcpConnectionInformation
Dim portStatus As String
portStatus = "Disconnected"
properties = NetworkInformation.IPGlobalProperties.GetIPGlobalProperties()
listeners = properties.GetActiveTcpListeners()
Try
' Cycle through all listening TCP connections and find the one that is associated with port.
For Each local In listeners
If (local.Port = port) Then
portStatus = "Connected"
Exit For
End If
Next local
' Port wasn't in the listening state so check if it is established.
If (portStatus = "Disconnected") Then
properties = NetworkInformation.IPGlobalProperties.GetIPGlobalProperties()
connections = properties.GetActiveTcpConnections()
' Cycle through all active TCP connections and find the one that is associated with port.
For Each t In connections
If (t.LocalEndPoint.Port = port) Then
Select Case t.State
Case NetworkInformation.TcpState.Established
portStatus = "Connected"
Exit For
End Select
End If
Next t
End If
Catch ex As Exception
' Handle Exception...
End Try
Return portStatus
End Function
This function will return "Connected" when called. I've traced it through, and found that the port I was using was still bound. Again, thank you for all help.
ADDITION
I just ran this between two separate systems, using WireShark to capture the data. There is a Sender and Receiver that I do not have sourcecode for, and am trying to integrate with, and the Sender and Receiver that I am updating the code to get it to communicate properly with the existing ones. The stream sent is identical in both cases (except for datetime in the ACK). If I send a second message from the existing sender to the Receiver being upgraded, the message is sent to the port, but the Receiver in development is never notified, which hangs both programs.
After pouring over the code that was provided to me, I finally realized that it was closing and disconnecting the wrong socket. Rather than calling Shutdown and Disconnect on the socket that was used to receive the message, it was called on the listening socket.
Once i realized this, I was able to close the correct socket, and the application began to work. Now I just need to go through and correct all of the issues that this caused with the underlying code, and things should be good.
Thank you to all of the people who responded. You got me thinking in the right direction.

Serial port in code, Visual Basic 2010

I got an Arduino hooked up to the PC and would like to read the serial port of it.
I read the serial port in Visual Basic with com.Readline, but it won't read everything.
It looses around 2-3 lines that were sent over the serial port to the Visual Basic code.
They get "lost".
If I go even with higher Baud rates (57600) it loses even more lines...
I use this to read:
Private Sub com9_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles com.DataReceived
Dim returnStr As String
returnStr = com.ReadLine
ReceiveSerialData(returnStr)
End Sub
Is the program too slow to get all data or what is the problem?
SerialPort.DataReceived Event has an interesting comment which may point you in the right direction:
The DataReceived event is not guaranteed to be raised for every byte received. Use the BytesToRead property to determine how much data is left to be read in the buffer.
Note that the page(s) also mention the fact that the buffering behavior complicates things...

Console CTRL-C handling when using serial port fails

Using VB.Net I've added a CTRL-C handler:
AddHandler Console.CancelKeyPress, AddressOf QuitHandler
Which does the following:
Private Sub QuitHandler(ByVal sender As Object, ByVal args As ConsoleCancelEventArgs)
Console.WriteLine("Quitting...")
args.Cancel = True
Quit = True
End Sub
I then have a main loop which just runs until Quit=True.
This all works until I start reading from the serial port:
Private Sub port_DataReceived(ByVal sender As Object, ByVal e As System.IO.Ports.SerialDataReceivedEventArgs) Handles Port.DataReceived
at which point the CTRL-C handler gets ignored for about 30secs at which point the console app just terminates without going through the cleanup code.
Why?
I don't know VB, but my guess would be that you're attempting to read data from the serial port that isn't there (err, the port is there, the data isn't); as a result, your program is blocking ("hanging") until the read attempt times out after 30 seconds.
If I'm correct, you need some way to poll your serial input without blocking, or (better) to get an asynchronous sub called when data actually appears.
You should make sure to understand how Timeouts work, since you are reading serial port on pooling. Your serial thread will always be running and trying to read something.
A best approuch would be to read data just when its availiable, then your serial thread would have time to breath.
You can also try to use DoEvents.

Get "real" IP address with VB.NET?

I'm looking for a function that will give me my real IP, not my local IP. the function i currently have, returns the ip in network and sharing center which is 192.168.2.100
But if I go to whatismyip, then it gives my real IP.
How could I get this using VB.NET?
thanks
To Combine the answers above"
Create a php file and paste this in it:
<?php
echo $_SERVER['REMOTE_ADDR'];
?>
save it as curip.php and upload it to your server.
In your VB.net project create a module.
Declare the imports section at the very top
Imports System.Net
Imports System.IO
And create your function:
Public Function GetIP() As String
Dim uri_val As New Uri("http://yourdomain.com/curip.php")
Dim request As HttpWebRequest = HttpWebRequest.Create(uri_val)
request.Method = WebRequestMethods.Http.Get
Dim response As HttpWebResponse = request.GetResponse()
Dim reader As New StreamReader(response.GetResponseStream())
Dim myIP As String = reader.ReadToEnd()
response.Close()
Return myIP
End Function
Now anywhere in your code you can issue
Dim myIP = GetIP()
as use the value from there as you wish.
There's no way to do this just with VB.Net. You need to find a website that will tell you (perhaps one of your own?) or you need to interface with your router.
If you have a web site that is capable of running any sort of web page applications you can create a web page that simply outputs the client's (as in the computer connecting to the page) IP address.
I have one of my own:
http://etoys.netortech.com/devpages/ip.asp
Though I can't guarantee it will always be there which is why you should make your own.
From there it's a simple matter of using a HttpWebRequest to download the content of the page.
I'm probably just being crotchety here, but I can't help but think that your "real" IP address is the one returned by ifconfig (ipconfig) on your local machine. 192.168.2.100 in your case. Whatever NAT translation or tunneling goes on between you and the remote endpoint shouldn't matter, and if it does, there's a good chance you're doing something that could make your application only work in your current environment.
I would use a publicly available site which returns your public IP address in response.
The key factors here are:
Availability of the service. Running your own service guarantees full control over it and knowledge of when it is available and when it's not. But in some cases it might be just too much work for such a simple task.
Minimizing additional clutter contained in the response. There are plenty sites allowing you to get your public IP address, but they often do that in the form of a HTML page. Extracting the small fragment of the page containing the IP address might need additional code.
Keeping in mind these two factors I would recommend this URL: http://wtfismyip.com/text It has the advantage of returning only the IP address in textual form. There are also versions for:
JSON: http://wtfismyip.com/json
XML: http://wtfismyip.com/xml
Pick the format that is easier for you to parse.
You DON'T need to use a php file just use a site that shows your ip like ip-adress.com and then get the ip from there with webrequest and then use GetBetween function.
have fun :)
Using PHP it can be done simply:
As "shaiss" shared the PHP code, this is the VB.net code:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim myip As HttpWebRequest = HttpWebRequest.Create("http://YourHosting.com/curip.php")
Dim grab As HttpWebResponse = myip.GetResponse()
Dim stream As Stream = grab.GetResponseStream
Dim SR As New StreamReader(stream)
TextBox1.Text = SR.ReadToEnd()
End Sub
End Class
This is just an example with:
1 - Form
1 - TextBox
1 - Button(s)
Hope this helps!
I had the same question and searched a bit only to remember that I can do this with WebBrowser!
Private Sub getExtIP() Handles activeProjectsWB.DocumentCompleted
If gotIP = False And populateProjectCollectionBLN = True Then
If activeProjectsWB.ReadyState = WebBrowserReadyState.Interactive Or activeProjectsWB.ReadyState = WebBrowserReadyState.Complete Then
Dim unformattedExtIP As String = activeProjectsWB.Document.GetElementsByTagName("title").Item(0).OuterHtml
Dim onlyIPAddress As String = String.Empty
For Each character In unformattedExtIP
Dim result As Integer = 0
If Not (Integer.TryParse(character, result) = 0) Or character = "." Then
onlyIPAddress = onlyIPAddress & character
End If
Next
extIP = onlyIPAddress
gotIP = True
End If
End If
End Sub
This subroutine only is triggered when you navigate to a webpage using WebBrowser.Navigate().
The gotIP boolean exists because I have another subroutine that activates on any document completion. I don't want it to be triggered more than once. If you are unfamiliar with WebBrowser, you check to make sure the web page is loaded enough with ReadyState. If you don't, you may get an exception (because content isn't loaded).
You can use whichever site you like. This site is good because it puts your IP address in the title. That's good because there's going to be one title tag. In the event you can't use this site (or one that has a unique tag with your content within it), use a For Each loop.
For Each instance As HtmlElement In activeProjectsWB.Document.GetElementsByTagName("InsertTagHere")
'do something to find the tag that contains your IP address
Next