Solved

how to use sockets and VB.net to send commands from a client to a server

Posted on 2008-10-01
23
1,962 Views
Last Modified: 2008-10-13
I have a server that we runs a program.  What I want to do is restart that program if it has crashed, but I need to do it remotely.  I have searched all over and found a couple of examples.  I even got it working to the point where I have 2 programs.. a server and a client.  The server listens and the client sends a message.  When the server gets that message,the server puts a message up saying data received etc.  Part of the problem I am having is the server only received one time, then freezes and I have to end it using visual studio.

In the end what I want is to have A button on the client that says "restart".  That send the command to the server, the server uses shell("mybatchfile.bat") or whatever... and then continues to listen for the next "restart" command

I am using Visual studio 2008 in Visual basic.NET
'-------------------- server code ------------------------------

Imports System.Net.Sockets

Imports System.Text

Class TCPSrv
 

    Function Main()

        ' Must listen on correct port- must be same as port client wants to connect on.
 

        Const portNumber As Integer = 8000

        Dim tcpListener As New TcpListener(portNumber)
 
 

        tcpListener.Start()

        Debug.WriteLine("Waiting for connection...")

        Try

            'Accept the pending client connection and return 

            'a TcpClient initialized for communication. 

            Dim tcpClient As TcpClient = tcpListener.AcceptTcpClient()

            Debug.WriteLine("command sent.")

            ' Get the stream

            Dim networkStream As NetworkStream = tcpClient.GetStream()

            ' Read the stream into a byte array

            Dim bytes(tcpClient.ReceiveBufferSize) As Byte

            networkStream.Read(bytes, 0, CInt(tcpClient.ReceiveBufferSize))

            ' Return the data received from the client to the console.

            Dim clientdata As String = Encoding.ASCII.GetString(bytes)

            MsgBox(clientdata)

            If clientdata.CompareTo("notepad") Then
 

                Shell("c:\windows\notepad.exe")

            End If

            Dim responseString As String = "Notepad started."

            Dim sendBytes As [Byte]() = Encoding.ASCII.GetBytes(responseString)

            networkStream.Write(sendBytes, 0, sendBytes.Length)
 

            'MsgBox("Message Sent /> : " + responseString)

            'Any communication with the remote client using the TcpClient can go here.

            'Close TcpListener and TcpClient.

            tcpClient.Close()

            tcpListener.Stop()
 

        Catch e As Exception

            MsgBox(e.ToString())
 

        End Try
 

    End Function
 
 

    Private Sub TCPSrv_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'Shell("c:\windows\notepad.exe")

        Me.Show()

    End Sub
 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bnStartListen.Click

        My.Settings.StopClicked = False

        bnStartListen.Text = "Started"

        MsgBox("Server Started")

        Main()

    End Sub
 

    Private Sub bnStop_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bnStop.Click

        My.Settings.StopClicked = True

        Main()

    End Sub

End Class
 

'-----------------------------CLIENT CODE ----------------------------

Imports System.Net.Sockets

Imports System.Text
 

Public Class frmServerClient
 

    Private Sub frmServerClient_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Me.Show()

        'MsgBox("sending data")

    End Sub
 

    Function Main()
 

        Dim tcpClient As New System.Net.Sockets.TcpClient()

        tcpClient.Connect("127.0.0.1", 8000)

        Dim networkStream As NetworkStream = tcpClient.GetStream()

        If networkStream.CanWrite And networkStream.CanRead Then

            ' Do a simple write.

            Dim sendBytes As [Byte]() = Encoding.ASCII.GetBytes("notepad")

            networkStream.Write(sendBytes, 0, sendBytes.Length)

            ' Read the NetworkStream into a byte buffer.

            Dim bytes(tcpClient.ReceiveBufferSize) As Byte

            networkStream.Read(bytes, 0, CInt(tcpClient.ReceiveBufferSize))

            ' Output the data received from the host to the console.

            Dim returndata As String = Encoding.ASCII.GetString(bytes)

            MsgBox("Host returned: " + returndata)

        Else

            If Not networkStream.CanRead Then

                MsgBox("cannot not write data to this stream")

                tcpClient.Close()
 
 

            Else

                If Not networkStream.CanWrite Then

                    MsgBox("cannot read data from this stream")

                    tcpClient.Close()
 
 

                End If

            End If

        End If
 

    End Function
 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        MsgBox("sending data")

        Main()

    End Sub

End Class

Open in new window

0
Comment
Question by:revo1059
  • 12
  • 11
23 Comments
 
LVL 25

Expert Comment

by:SStory
ID: 22651371
It looks like to me that you are reading one time and then closing.
0
 
LVL 25

Accepted Solution

by:
SStory earned 500 total points
ID: 22651720
0
 
LVL 1

Author Comment

by:revo1059
ID: 22654111
I have been playing with it for a few days now.  I have gotten it to do what I want, however, after the first command is sent, the program uses 100% of the cpu.  I have not been able to find out why
0
 
LVL 1

Author Comment

by:revo1059
ID: 22654423
Here is the new code I am using


I separated the listener so it starts the command, and then listens again.  like I said, the problem I think the problem I am having is that it is not closing its thread.

anyway, any code examples would be very helpful
Imports System.Net

Imports System.Net.Sockets

Imports System.IO

Imports System.Threading
 

Public Class MainClass

    Shared Listener As New TcpListener(11000)

    Shared Stream As NetworkStream

    Dim listnerStarted As Boolean

    Public Shared Sub AcceptData()

        Try

            Console.WriteLine("TCP Server Waiting for a connection...")

            Dim Client As TcpClient = Listener.AcceptTcpClient()

            Console.WriteLine("Connection accepted.")

            Console.WriteLine(New String("-", 40))

            Console.WriteLine()

            Stream = Client.GetStream()

            Dim ReceiveThread As New Thread(AddressOf ReceiveData)

            ReceiveThread.IsBackground = True

            ReceiveThread.Start()

            Dim w As New BinaryWriter(Stream)
 

            Dim Text As String
 

            Text = Console.ReadLine()

            w.Write(Text)
 

            Client.Close()
 

        Catch Err As Exception

            Debug.WriteLine(Err.ToString())

        End Try

    End Sub

    Public Shared Sub Main()

        'Dim listnerStarted As Boolean
 

        Listener.Start()
 

        AcceptData()
 

    End Sub
 

    Private Shared Sub ReceiveData()

        Dim r As New BinaryReader(Stream)
 

        Do

            If Stream.DataAvailable Then

                If r.ReadString = "notepad" Then

                    Shell("c:\windows\notepad.exe")

                    'ProcessThread.Abort()

                    AcceptData()

                End If

                Debug.WriteLine("RECEIVED: " + r.ReadString())

            End If

        Loop
 
 

    End Sub
 

    Private Sub bnStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bnStart.Click

        Main()
 

    End Sub
 

    Private Sub MainClass_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 

    End Sub

End Class

Open in new window

0
 
LVL 25

Expert Comment

by:SStory
ID: 22654571
After telling you about the demo, I downloaded both and watched them. It an indepth how to on Client Server sockets using TCP/IP.  They even build a chat client.  You should watch it and learn.  You may see a lot of things you haven't considered.   Sounds like a deadlock or threading issue of some sort.

I'd just check out this tutorial.  The guy doing it has been famous in the VB community for years...from the VBPJ Magazine days. It is well done.
0
 
LVL 1

Author Comment

by:revo1059
ID: 22658198
The problem is, I am not trying to build a chat program.  I want to be able to launch a batch file on a remote server from a vb application that I am writing. I will watch the videos when I have time.
0
 
LVL 25

Expert Comment

by:SStory
ID: 22658663
It doesn't matter what you want to do.  The basic operation is the same.  You will be sending bytes from point A to point B.  The first part of the video is about that...in building a simple telnet client.  You can use this with some special bytes to say--this is a command--followed by the text for the batch to be launched. The listener could look for that and react and actually run the batch file.

Does this make sense?
0
 
LVL 1

Author Comment

by:revo1059
ID: 22662577
Yes it does,  I will download the source and watch the videos when I get home from work today.  Thank you for all your help.  If I get stuck, would you be willing to help me with the code?
0
 
LVL 25

Expert Comment

by:SStory
ID: 22671342
I will help you with what I can. That's what EE is about.  None of us know it all. My point is that I started doing something similar to what you ware wanting to do.  You can pass anything you want to...but in the end it is just bytes.  If you wanted to make a class that stored item1, item2,item3
item1 being MessageType which is an integer or byte.  You could output a delimited string that had parts on one end (converting from string to bytes). Then on the other end do the opposite.. This means you can send anything you can come up with as Text if you can encode it to bytes and decode it on the other end.  In that manner it is all basically a telnet type deal.  Send stuff, receive stuff...do something with the stuff.

POP3 has a protocol (a  language)
SMTP has another protocol (a language)  
You transmit
HELO SOMEMACHINE.SOMEWHERE.COM to the server (just bytes)
it comes back with a response.

Everything works this way.  The protocol just tells you what is expected and how you have to talk to the server and what responses to expect.  You can make your own for sure.  Underlying it all...is transferring and receiving  a series of bytes.

Example:

if you defined messages types as
typTEXT = 0
typCMD=1
.
.
.
typN=N

and you wanted to send a command to be executed on the server.

then the client could take it and put it in a delimited string
1|mybatch.bat

Call an encoder to encode that to bytes, then transmit it.
On the other end when you get messages, see what that first field is, by getting all bytes,  converting to a string and then splitting to field1 and seeing if it is a 0, 1, 2, .... or N
React accordingly.

If it is a 1, we know a command follows so we take field2 and execute it on the machine.
system.diagnostic.process.start(whatevercommand)

Make sense?
0
 
LVL 1

Author Comment

by:revo1059
ID: 22674013
I am trying the source code from the video.  I can start the server, and start the client, when I put my computers IP address and click connect, I get an error:

System.Net.Sockets.SocketException was unhandled
  ErrorCode=11001
  Message="No such host is known"
  NativeErrorCode=11001
  Source="System"
  StackTrace:
       at System.Net.Dns.GetAddrInfo(String name)
       at System.Net.Dns.InternalGetHostByAddress(IPAddress address, Boolean includeIPv6, Boolean throwOnFailure)
       at System.Net.Dns.GetHostEntry(String hostNameOrAddress)
       at SocketsClient.Form1.btnConnect_Click(Object sender, EventArgs e) in D:\downloads\SocketsClient\SocketsClient\Form1.vb:line 23
       at System.Windows.Forms.Control.OnClick(EventArgs e)
       at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
       at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
       at System.Windows.Forms.Control.WndProc(Message& m)
       at System.Windows.Forms.ButtonBase.WndProc(Message& m)
       at System.Windows.Forms.Button.WndProc(Message& m)
       at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
       at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
       at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
       at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32 dwComponentID, Int32 reason, Int32 pvLoopData)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
       at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
       at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
       at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
       at Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[] commandLine)
       at SocketsClient.My.MyApplication.Main(String[] Args) in 17d14f5c-a337-4978-8281-53493378c1071.vb:line 81
       at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)
       at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
       at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
       at System.Threading.ThreadHelper.ThreadStart()
  InnerException:
0
 
LVL 25

Assisted Solution

by:SStory
SStory earned 500 total points
ID: 22679815
OK.  If you'll look at the error you posted it says error Line 23 Form1.
Line 23 is a DNS resolution to get the IP from the host name. If you put in your IP in that text box then it is going to fail.  You need to put in your computer name or change how the code works.

In other words:
 Dim address As IPAddress = _
                System.Net.Dns.GetHostEntry(txtAddress.Text).AddressList(0)

could be changed to do this if you want to require an IP
 Dim address As IPAddress = IPAddress.Parse(txtAddress.Text)

Perhaps more intelligently would be to look at what is in txtAddress.text and if the first char is a number assume an IP otherwise do a host lookup.  From the code I've given you above you should be able to make any of these happen.
0
How to improve team productivity

Quip adds documents, spreadsheets, and tasklists to your Slack experience
- Elevate ideas to Quip docs
- Share Quip docs in Slack
- Get notified of changes to your docs
- Available on iOS/Android/Desktop/Web
- Online/Offline

 
LVL 1

Author Comment

by:revo1059
ID: 22682781
Thank you, that did work.  This server is very cool, but it is above and beyond what I need.  I have tried very hard to look through the code and find where the text comes in so that I can put an if/then statement on it.  I have not been able to find that code.  If i find that, I think I can make the rest work.  
0
 
LVL 1

Author Comment

by:revo1059
ID: 22683500
Ok, I searched harder and found what I was looking for.  I tried to put a simple command in there to get cmd to run (just for testing) but it didnt work.  The mesgbox displays test, but cmd does not run. If you can help me with this last question, I will be very grateful

if you have the source, this is in chatserver.vb under the Private Members Region

thank you so much for all your help so far.
Private Sub _broadcast(ByVal buffer As Byte(), ByVal sender As ClientContext, ByVal Echo As Boolean)

        '-- By locking the buffer, we make sure nobody can read it until we've put it in all the queues

        Dim thecommand As String

        theCommand = System.Text.ASCIIEncoding.ASCII.GetString(buffer)

        MsgBox(thecommand)

        If thecommand.Equals("test") Then

            Shell("c:\windows\system32\cmd.exe")

        End If

        '-- everything that was here before

        'SyncLock buffer

        '-- Add this data to each client's queue. This will be read and sent in _processQueues()

        'For Each client As ClientContext In clients

        'If client IsNot sender Then

        'client.SendQueue.Enqueue(buffer)

        'End If

        'Next

        'If Echo Then

        ' sender.SendQueue.Enqueue(buffer)

        ' End If

        ' End SyncLock

    End Sub

Open in new window

0
 
LVL 25

Expert Comment

by:SStory
ID: 22686107
OK. Wrong place. Broadcast is sending the message out.  You should put it back the way that it was.

The source uses multithreading and async programming.  In the client source from the demo.  Line 66 (form1.vb) is where data is received into a data variable.

You should put all other code back like it was.

To test, using Carl's example, do the above and then inside of btnSend_Click
Change SendText value to some fake command like "DOTHIS"

Back after Line 66, process the incoming string.  It is bit more complicated as if you had two clients and a server running you'd want to make sure it didn't process this on the sending client as well.

The rest is up to your imagination in packing your string with useful information that you can separate back out into parts that are logical....a delimited string works well.

HTH
0
 
LVL 1

Author Comment

by:revo1059
ID: 22686261
I made lots of changes.  I took out all those boxes on the main form, and made them into settings instead (like my.setting.[settingname] ) I am going to use a xml file to let the user select the server at the start.  Each server will have a different port, and a password at some point.  So that being the case, do I need to broadcast the message sent because there is only one person connected to each server.  
0
 
LVL 1

Author Comment

by:revo1059
ID: 22686308
Let me explain a little more.  There is a potential for there to be more than one of these servers that I want to control. They are game servers, and you an run more than one on a given computer.   When the server (the program I am writing ) starts up, they are going to choose from a dropdown list, which game server they want to monitor.  I set the max connections to 1 in the code, that is why I do not need to broadcast.  Above my previous post, you said that in Form1.vb I should set the text of the command.  That is fine... I am going to have a Button that sais "Restart" or something like that and maybe a few others.  if not in that sub Broadcast, where on the server side do I read the command "restart" and then launch my file?
0
 
LVL 25

Expert Comment

by:SStory
ID: 22690282
Just look for the place where it is decoded from bytes to text. At that point it is text.  Do whatever you want to with it then.

You could have multiples servers, or you could have one server that determined from a game identifier, which game the message was for and handled all games. This is all up to you.  The point is the basic idea is to pass a delimited text string on one end and get it back out as the same on the other end. string.Split("|") to get the parts back if "|" is your delimiter and then use it to do whatever you want to.

You are always sending and receiving.  Broadcast on a chat app is to everyone connected to the server.  I would assume you would broadcast from your server to all clients listening to it.  The clients can ignore the message if you add some ID to it.
0
 
LVL 1

Author Comment

by:revo1059
ID: 22695490
remember I am going to have one of these listening servers for each one of my game servers.  The client will connect to the server listener and push a button that says restart.  That sends the word "restart" to the server and it launches whatever command the user has setup.

I have still not found anywhere but inside that _broadcast sub where I can read what command has been sent from the client
0
 
LVL 25

Expert Comment

by:SStory
ID: 22703769
I originally mentioned this tutorial because I thought it would give you the techniques to do whatever you wanted to.  I think it does.  In any game some machine has to be the "controller" or "server" and the other(s) is/are the client(s).  No matter what it is still just sending and receiving, and responding.  The techniques for sending data and receiving asynchronously as well as the server piece to receive  are there.  
0
 
LVL 25

Assisted Solution

by:SStory
SStory earned 500 total points
ID: 22703805
In ChatServer.vb appears to be the place that things happen for the server.

It says
<quoted>
    ''' <summary>
    ''' Sub called on a worker thread to process incoming and outgoing data in a loop
    ''' </summary>
    ''' <remarks></remarks>
    Private Sub _processQueues()
</quoted>

It is using:
" client.Socket.Send"  to send to the client
0
 
LVL 1

Author Comment

by:revo1059
ID: 22704970
Like I said above, I have put the code for executing the file that I want in the _broadcast function

        Dim str As String
        Dim enc As New System.Text.ASCIIEncoding()
        str = enc.GetString(buffer)

        MsgBox(str)
        If (str.Equals("restart")) Then
            Try
                Shell("c:\windows\notepad.exe")
            Catch ex As Exception
                MsgBox(ex.Message)
            End Try
        End If

No matter what, the command does not start if I do it this way.  If i put 'Shell("c:\windows\notepad.exe")' outside the if, it does work.  I have read so many articles about comparing strings, but nothing I do works.

I am not a programmer by nature.  The examples you gave me do have parts of what I need, but to me they are to complicated and I don't understand the different parts well enough to be able to pull out just the parts I need.  I do appreciate all your help, and just need this one last part to work the way I want.

Any more help you can give would be appreciated
0
 
LVL 1

Author Comment

by:revo1059
ID: 22705111
I have an update.  I don't know why this works but it does.  Instead of using if str = "restart" .... I used

if str.contains("restart") then ....

No clue why it works but it does.  

Once again,  thank you SStory for all your help and extra effort
0
 
LVL 25

Expert Comment

by:SStory
ID: 22705771
Probably there are some extraneous chars behind or in front of the word.

Probably str.trim="restart" would also work
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

In my previous two articles we discussed Binary Serialization (http://www.experts-exchange.com/A_4362.html) and XML Serialization (http://www.experts-exchange.com/A_4425.html). In this article we will try to know more about SOAP (Simple Object Acces…
Creating an OSPF network that automatically (dynamically) reroutes network traffic over other connections to prevent network downtime.
This video discusses moving either the default database or any database to a new volume.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

757 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

23 Experts available now in Live!

Get 1:1 Help Now