Migrating Visual Basic 6 Network code to Visual Basic .NET or C#
Network applications in VB6 was centred on the Winsock
ActiveX control, which although simple to use had many drawbacks, which
prevented it from use in high performance clients or servers. In .NET, these
performance and scalability bottlenecks are negated by new features built into
the .NET framework, including enhanced threading capabilities, and native
socket handling.
This article show why you should not develop real-world
network applications in VB6, instead, opt for C# or VB.NET. Complete .NET
source code, and further explanation of the models referred in this article can
be found in the book Network
Programming in .NET
(at Amazon UK)
(at Amazon US)
The main differences between Visual Basic 6 networking code
and .NET are as follows:
- VB6
cannot handle multiple network requests simultaneously, .NET can.
- The
Winsock ActiveX control has a much larger memory footprint than a .NET
socket. Therefore, VB6 network applications are more memory intensive.
- .NET
supports overlapped I/O, and can therefore read and write on the same
socket simultaneously. VB6 does not support overlapped I/O, .NET supports
overlapped I/O through asynchronous sockets.
It is for these reasons that it is imperative to avoid the
temptation of using Visual Studio .NET’s Visual Basic to VB.NET upgrade tool to
migrate VB6 network code to VB.NET.
Using Winsock in VB.NET is like using a tractor engine in
a Ferrari!, you will severely limit performance, reliability and scalability by
doing so.
Concurrent network request handling
The root cause of VB6’s inability to correctly handle
multiple network requests is that Visual Basic 6.0 had no native means of multithreading. VB 6 apartment threaded objects
and programs are limited to execution in an STA (Single Threaded Apartment).
This means that your exe or object can spin up as many threads as it wants, but
only one thread in the apartment can execute at a time. Therefore there can be
no concurrent execution of threads. Even if you use an out of process COM
object you will only get two threads, one for your calling program and one for
the COM exe, and each of them will be in an STA. Developers may have noticed
that If one event fires, and goes into a DoEvents loop, i.e.
Do until (SomeVariable=true)
Doevents
Loop
If
another event fires during execution of a DoEvents Loop, then this will
interrupt the loop, and the code execution will not return to the loop until
the event completes. If the event attempts to call DoEvents itself, then the
application will hang. Furthermore, a DoEvents loop uses 100% processor time.
VB.NET
and C#, support an additional threading model, the free threading model, which
forces an object to execute in an MTA (Multi Threaded Apartment), this
threading model allows multiple concurrent threads of execution.
Memory leaks in network code
To demonstrate the memory footprint difference between VB6
and .NET, two functionally identical routines were written in VB6 and C#, to
create 1000, 5000 and 10,000 sockets, and the impact on memory and time to
instantiation were monitored.
In VB6, the following code was used:
Private Sub Form_Load()
startTime = Timer
For I = 1 To 10000
Load Winsock1(I)
Next
MsgBox (Timer - startTime)
End Sub
And the results were:
3,276Kb for 1,000 sockets, after 0.46875 seconds
20,932Kb for 5,000 sockets, 2.53125 seconds.
42,984Kb for 10,000 sockets, after 5.34375 seconds
In C#, the following code was used to achieve the same
effect:
private void Form1_Load(object sender, System.EventArgs e)
{
DateTime dt = DateTime.Now;
TimeSpan ts;
Socket[] s = new Socket[10000];
ts = DateTime.Now - dt;
MessageBox.Show("Elapsed:" + ts.Milliseconds);
}
8Kb for 1,000 sockets.
36Kb for 5,000 sockets.
60Kb for 10,000 sockets.
The time elapsed in each case was zero. As it is plain to
see, .NET is very significantly more efficient and memory sparing.
The standard means of handling multiple clients in a VB6
server was to create one Winsock object per client. Once a client closed the
connection, the Winsock object could be freed. ActiveX components, such as
WinSock have quite a substantial memory footprint, much more so than that
of .NET’s socket object, therefore if
clients would be expected to maintain persistent connections, and one could
expect numerous clients, then a system could be overwhelmed with demands to
create more and more Winsock objects.
Furthermore, rapid instantiation and de-instantiation of
Winsock objects is prone to memory leaks, such as KB 171996 “Winsock Function Calls Generate Non-Paged
Pool Memory Leak”
One means by which some developers have overcome memory
leaks in vb6, was to initially create a set number of sockets, i.e. 10, and
then allocate each one of the Winsocks to each new connecting client, once a
client disconnected, the Winsock would not be de-allocated, but could be reused
by other clients. This model would not support more than 10 concurrently
connected clients, although it is quite analogous to the use of a Thread Pool
in .NET.
The technique of using a Thread Pool to manage concurrent
network connections in .NET is discussed in detail in the book, Network
Programming in .NET
(Buy at Amazon UK)
(Buy at Amazon US)
Overlapped I/O
Overlapped I/O, (IO completion ports) are a high performance
means of reading and writing simultaneously on the same socket. It is managed
at system level, thus offering maximum performance. This technique has only
been available since the advent of Winsock 2.0 in Windows NT, 2000, and XP.
Previously, it was only possible to implement I/O completion
ports or Overlapped I/O through the use of the Windows API, and quite
substantial code. Placing this level of network programming firmly in the
realms of expert VC++ programmers, and would be non-trivial, if not impossible
to develop in VB6.
.NET’s Asynchronous socket handling, through the use of
AyncCallBack, uses IO completion ports ‘under the hood’, and thus affords all of
the performance bonuses of using this model, with none of the complexity.
This model is described in detail, with source code in the
book, Network
Programming in .NET
(at Amazon UK)
(at Amazon US)