How can I make my TCP Socket Server Async? - vb.net

I currently have made an all in one peer to peer 'chat' program. It currently uses a timer to receive messages, and a client to send them to other people running the program. I would really like to make this whole system async so I stop getting complaints of the main executable freezing and such.
I have pasted all of my code(VB.net) here: http://pastebin.com/EcrtCgVc
Could someone assist me in making this system faster, async, or done better.
If you would like a link to the dropbox of the full source, I can provide this also.

I would suggest you to take a look at this example project:
Asynchronous Server Socket Example
http://msdn.microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx
You can use it together with this example:
Asynchronous Client Socket Example
http://msdn.microsoft.com/en-us/library/bew39x2a%28v=vs.110%29.aspx
You may need to use async methods in your server code and you may use Delegate Sub to make your server run in another thread, so main thread(UI) will be still responsive.
http://msdn.microsoft.com/en-us/library/ms172879.aspx

Without looking at your code, I am also working with socket programming.
"Do not mix GUI code with business code."
Like jgauffin said, you should not combine code that run in the background with the GUI.
For example, if you're using a socket to accept data, or connections, you must run that method on a new thread.
Example...
Public Sub YourMainSub()
Dim threadOne As New System.Threading.Thread(AddressOf ListenCode)
threadOne.Start()
'Put other code here.....
End Sub
Public Sub ListenCode()
Dim newPerson = yourServer.Accept()
'Or
Dim newData = yourServer.Receive()
End Sub
Sorry if the code isn't exactly correct. I typed that by memory.

Related

How to do asynchronous function call in vba

I have created custom object in Python using pywin32.
while calling that object takes some times.
Following is my try at making that asynchronous with respect to application stat so that while macro is running application doesn't freez.
Sub demo()
demofunction
End Sub
Function demofunction()
Dim demoobj As Object
Do While demoobj Is Nothing
DoEvents
Set demoobj = CreateObject("Python.DemoObj")
Loop
Debug.Print demoobj.Hello
End Function
Obviously the code is not working in terms of asynchronous run. Is this even possible any how?
Above code however working fine while freezing the application for a while.
Vba is single threaded but you can play some tricks to make it appear multi-threaded, after all rising star web server Node.js serves off of one thread but gives impression of multi-threaded.
1) So, you could put some Python component behind a web server and then use MSXML2.XMLHTTP60 asynchronously. Private WithEvents oXHR As MSXML2.XMLHTTP60 comes in handy here. (Note you need to declare this as a member of a class to use WithEvents)
2) You could shell out to a process (which gives you another thread) and call the Python component that way, then you use some Windows API calls to periodically check if the process has completed.

Update GUI from another class in vb.net

I'm building a server and a client for a chat that runs on Tcp and Sockets, I want the client to handle more than one connection (to servers) so I made a class called "Client" to manage the async connection, so that I can run more instances at the same time like:
Dim ConnectionToServer1 as new Client
Dim ConnectionToServer2 as new Client
Since it's async when the the "connection" receives a message it generates an event (AsyncCallback) where I can convert the received bytes into a string.
The problem is: I've tried to set this string to a RichTextBox on my Form from inside the Client class, but nothing happens, I've tried to create a delegate in the form code but nothing works, the only way I was able to put the received message in the RichTextBox is by creating a public variable called LastMessage in the Client class where the last message is stored (every time it receives a message, the sub overrides the string), and then running a thread created by the Form which keeps checking for data (since the thread has been created by the form it has access to the controls, including the RichTextBox, right?)
Although I find this a bit clunky, is there any other way (through delegates maybe?) I can do it?
Here's some code:
Client class: http://pastebin.com/GF9um8Ss
Form code: http://pastebin.com/xW7mDj8j
Sounds like you started down all the right paths.
Now, on threaded applications one of the challenges that you will face is you can have tons of worker threads, but only the main, UI thread can actually make any updates to the UI. So keeping that in mind, if you have async code that needs to update the ui you will need to use what is effectively a delegate.
You can do this using tasks these days a lot easier, so read up on the Task Parallel Library, but essentially you need a delegate/task that is marshaled to run on the ui thread to handle the UI updates.
Set this global property as false
Control.CheckForIllegalCrossThreadCalls = false
this will let you edit any control of your form from any thread

Is it possible to host a webserver in VBA?

I want to host a web server and want to use VBA to do it. Is this possible? I'm just doing this to prove someone wrong and really want to make this program.
So is it possible to make a really simple web server (just listens for get requests)? Help would be very much appreciated.
EDIT
I'm trying something like this
Sub startServer()
Set wunsock = CreateObject("OSWINSCK.Winsock")
wunsock.LocalPort = 80
wunsock.Listen
End Sub
Sub wunsock_ConnectionRequest(ByVal requestID As Long)
If sockMain.State <> sckClosed Then
sockMain.Close
End If
sockMain.Accept requestID
End Sub
Private Sub wunsock_DataArrival(ByVal bytesTotal As Long)
Dim strData As String
sockMain.GetData strData, vbString
txtStatus.Text = txtStatus.Text & _
strData & vbCrLf
End Sub
However it doesn't work?
Although this is a rather old question, I'd still like to mention that I built an Excel hosted REST webserver using plain VBA macros and some Winsock C calls, as proposed by Daniel A. White.
I added this as an answer instead of a comment, since it's built as a modular library, so you can adjust it to your needs, and others might need exactly this kind of library. It can serve both worksheets, basic files and also create custom hooks using an IWebController to listen on specific routes (which was mentioned by OP in a comment):
http://github.com/michaelneu/webxcel
To use it, you'll have to either import the classes/modules into your workbook, or let the build script create a new one for you. See Main.bas on how to start the server from within VBA.
http://www.ostrosoft.com/oswinsck.asp#inst
is a winsock type of library which can be used from VBA. It is possible to do what you are looking to do though is not the most efficient thing to do.
I do applaud your tenacity hope it works out for you.
I'm not sure I fully understand the question. Generally, you don't "host a web server", you host a web site.
But if you can do TCP sockets with VBA, then you can make an incredibly simple web server by following the HTTP standard protocol.
Edit: based on your comment, yes you can make a simple web server as long as you can open up a TCP socket.
Well, at the risk of violating the spirit of the question, you can always use VB's support for library functions and just create a library binding to one of a number of C-language web server options (such as http://www.acme.com/software/micro_httpd/, http://www.gnu.org/software/libmicrohttpd/ or http://code.google.com/p/mongoose/). You'd have to make DLLs out of the selected web server but that is reasonably easily done and this will work just fine in VBA.

How to Cache Real-time Data?

I'm working on a windows forms application (.NET 4.0).
My form contains a 'Fast Line' chart using the Microsoft chart control included in VS2010.
The chart gets filled with about 20,000 datapoints.
My application then starts receiving market data from a server via DDE (Dynamic Data Exchange) in real-time and adds it the chart.
Note: I have no control over the server and so I have to deal with DDE only even though it's an outdated technology. VS doesn't support DDE anymore and so I use the Ndde library which works like a charm.
First we connect to the server, create an advise loop, and then subscribe to the OnAdvise event to receive notifications of new data:
Dim client As DdeClient = New DdeClient("ServerApplication", "Bid")
Private Sub StartDDE()
client.Connect()
client.StartAdvise("EURUSD", 1, True, 60000)
AddHandler client.Advise, AddressOf OnAdvise
End Sub
Now we can put the commands to update the chart inside the event:
Private Sub OnAdvise(ByVal sender As Object, ByVal args As DdeAdviseEventArgs)
Dim myPrice As Double = args.Text
Chart1.Series("Bid").Points.AddY(myPrice)
End Sub
You get the idea.
THE PROBLEM:
This works fine for a few seconds until the chart crashes throwing the exception:
"Collection was modified; enumeration operation may not execute."
I spent a lot of time researching what may be the cause of this in my particular case, and I've come to the conclusion that it's because the chart is receiving data quicker than it can handle. It's already loaded with a lot of data and needs a certain time (less than a second) to add the received data in a new DataPoint and invalidate (refresh) itself. Whereas the server often sends data values very quickly (for example 5ms in between). So I've tried the following:
System.Threading.Thread.Sleep(800)
Chart1.Series("Bid").Points.AddY(myPrice)
thus pausing the application to give time to the chart to finish its work before adding a new point, and guess what? The application now works for minutes before throwing the exception. (altering the value in Sleep() doesn't help any further however)
The only help I could find online is an old post of someone mentioning that you should put incoming data on a cache queue, with one new data value released from the cash at a time (every time the chart finishes working).
My question is how would you do this?
Other suggestions are welcome!
This is most likely an issue caused by attempting to modify a UI element from a thread other than the UI thread.
The way you have it coded now the DdeClient.Advise event handler is being executed on a worker thread managed by the library. See, DDE sucks and because it sucks it has these requirements that it has to run on a thread with a message pump.1 To make the library compatible with other types of applications besides windows forms I coded it in such a manner that it will create a dedicated thread with a message loop and marshal all of the operations onto that thread by default.
But, you can override this behavior by specifying an ISynchronizeInvoke instance manually in the DdeClient constructor. The library will then use whatever thread is hosting the ISynchronizeInvoke instance for all of its DDE operations. All Form and Control instances implement ISynchronizeInvoke so it is easy enough to tell the library to use the main UI thread.
Dim client As DdeClient = New DdeClient("ServerApplication", "Bid", yourForm)
If you tell the library to use your Form instance then the Advise event handlers will be executed on the same thread hosting that Form; the UI thread.
By the way, I realize that you have no control over the server, but I would at least begin talking with the vendor of the software to use more modern (not 20 years old) mechanisms for doing interprocess communications.
1It also has the unfortunate requirement of thread affinity which made dealing with the garbage collector a real pain.
Get real ;) DDE is slow, graphics is slow. Do not do them in the same thread.
Try that:
Create a second thread that handles DDE, queues the items.
The chart thread then pulls the updates and draws them.
Now, here comes the point:
ONLY the ui thread is allowed to modify the chart control. Yes, sucks. No, not negotiable. - old UI rule since the dawn of time.
Threads needs locking ;)

Track installs of software

Despite my lack of coding knowledge I managed to write a small little app in VB net that a lot of people are now using. Since I made it for free I have no way of knowing how popular it really is and was thinking I could make it ping some sort of online stat counter so I could figure out if I should port it to other languages. Any idea of how I could ping a url via vb without actually opening a window or asking to receive any data? When I google a lot of terms for this I end up with examples with 50+ lines of code for what I would think should only take one line or so, similar to opening an IE window.
Side Note: Would of course fully inform all users this was happening.
Just a sidenote: You should inform your users that you are doing this (or not do it at all) for privacy concerns. Even if you aren't collecting any personal data it can be considered a privacy problem. For example, when programs collect usage information, they almost always have a box in the installation process asking if the user wants to participate in an "anonymous usage survey" or something similar. What if you just tracked downloads?
Might be easier to track downloads (assuming people are getting this via HTTP) instead of installs. Otherwise, add a "register now?" feature.
You could use something simple in the client app like
Sub PingServer(Server As String, Port As Integer)
Dim Temp As New System.Net.Sockets();
Temp.Connect(Server, Port)
Temp.Close()
End Sub
Get your webserver to listen on a particular port and count connections.
Also, you really shouldn't do this without the user's knowledge, so as others have said, it would be better to count downloads, or implement a registration feature.
I assume you are making this available via a website. So you could just ask people to give you their email address in order to get the download link for the installer. Then you can track how many people add themselves to your email list each month/week/etc. It also means you can email them all when you make a new release so that they can keep up to date with the latest and greatest.
Note: Always ensure they have an unsubscribe link at the end of each email you send them.
The guys over at vbdotnetheaven.com have a simple example using the WebClient, WebRequest and HttpWebRequest classes. Here is their WebClient class example:
Imports System
Imports System.IO
Imports System.Net
Module Module1
Sub Main()
' Address of URL
Dim URL As String = http://www.c-sharpcorner.com/default.asp
' Get HTML data
Dim client As WebClient = New WebClient()
Dim data As Stream = client.OpenRead(URL)
Dim reader As StreamReader = New StreamReader(data)
Dim str As String = ""
str = reader.ReadLine()
Do While str.Length > 0
Console.WriteLine(str)
str = reader.ReadLine()
Loop
End Sub
End Module
.NET? Create an ASMX Web Service and set it up on your web site. Then add the service reference to your app.
EDIT/CLARIFICATION: Your Web Service can then store passed data into a database, instead of relying on Web Logs: Installation Id, Install Date, Number of times run, etc.