Reusable multithreaded tcp client and server classes with example project in VB.NET

7:01:00 am 2 Comments

  • A multithreaded server class that accepts multiple connections from a provided client class. Each client can send and receive files and text (byte data) simultaneously along 250 available channels.
ExamplePic.jpg

Introduction 

This example project contains three classes - TcpCommServer, TcpCommClient and CpuMonitor. With these classes, you will not only be able to instantly add TCP/IP functionality to your VB.NET application, but also most of the bells and whistles we're all looking for. With these classes, you will be able to connect multiple clients to the server on the same port. You will be able to easily: throttle bandwidth to the clients, and send and receive files and data (text?) along 250 provided channels simultaneously on a single connection.

Background 


When I first started looking for information or example code for tcp/ip communications in vb.net, I have to admit I was looking for it all. I was willing to accept the bits I needed to get started, but I was hoping, like you probably are, that I would find it all... some easily transportable classes that would allow me to do what I think most of us want when we first start looking into this - send and receive files, and our own data or text, all over a single connection, simultaneously. It's what we'll all need eventually, for one project or another when the time comes to build a client/server application. We'll also need to control the bandwidth at the server, or someone will use it all up downloading a large file... And oh - it would be nice if we could connect to the server on the same port from multiple clients, right?
I looked for a long time and never found anything right out of the box that would do what I need, so I decided to build it myself. Along the way I began to understand why people don't just freely give this kind of work away. It took me quite a bit of time coding, troubleshooting and testing this - but it is, after all, just a tool. It's what we do with it that will be worth something in the end.
So here it is.

Using the code 

First of all, both classes use delegates to do callbacks. All you have to do is pass the AddressOf YourCallbackSub while instantiating these classes, and then call start or connect Ie:
Dim _Server As New tcpCommServer(AddressOf YourCallbackSub)
_Server.Start(60000) 
Or:
Dim _Client As New tcpCommClient(AddressOf YourCallbackSub)
_Client.Connect("192.168.1.1", 60000) 
YourCallbackSub has to have the right signature  - you can see the right way to do it in the example. You can also specify the max bps while instantiating the server class. The default value is 9Mbps.
Throttling works along a 4k boundary if you set the bandwidth to be less then 1 meg, so you can use the traditionally expected bandwidth limits - ie: 64k, 96k, 128k, 256k, ect. After one meg, it's along a 5k boundary - so you can set it to more intuitive values: 2.5 meg, 5 meg, 9 meg, ect. For those who are interested - the throttling code checks the bandwidth 4 times a second.
Throttling will be important for you because these classes will do as much work as you let them - as fast as they can. On my dev machine, I was able to achieve transfer speeds of almost 300 Mbps copying one large file from server to client. But the cost of this was the client and the server's background threads using 100% of their respective CPU core's resources - not a good scenario for a server. On my dev machine, 9Mbps produced almost no perceptible cpu usage. In a production environment, I'm sure you will want to set this even lower.
To get a file from the server, there is a GetFile() method. Simply supply it with the path of a file on the server (a path local to the server). To send a file to the server, there's a SendFile() method. Both GetFile and SendFile are  handled by the classes without any other code needed by you - but if you'd like to track the progress if the incoming or outgoing file, you can poll the GetPercentOfFileReceived and GetPercentOfFileSent methods.

Channels 

We want to be able to do everything... right? And all over a single connection. You want to be able to send, say, a stream of screen shots, or video, or text, and not have to code your own way of separating one from another. The channel was my answer. every time you use the SendBytes method, you send that data with a channel identifier. When it comes out the other end in the callback, it arrives with the channel identifier you sent it with so you can tell your screenshot bytes from your text - if that's what you're sending. As I said above, you have 250 channels at your disposal (1 to 251).

The CpuMonitor class

Included in this project, you'll find the CpuMonitor class. This class is used to monitor the cpu usage of a thread. When running the example project, you will see some reporting of cpu usage in the status strip. This is the usage of the client's background thread - the thread actually doing the work of communicating with the server.

Points of Interest 

One thing worth noting here was an issue I noticed while testing these classes. As you would expect, I tested sending larger and larger files, and more and more connected clients all sending or receiving large files. At one point, I had one client pulling a >3 gig file while two other clients were also doing largish file transfers, and my bandwidth seemed to drop to almost nothing after 2 gig had been sent (almost nothing being between 1 and 30 Mbps - I was testing without throttling to see this effect). After much testing, I realized that this is a hardware limitation on my machine. I think it has to do with the hard drive cache. I was able to mitigate this effect by inflating the filestream's buffers in the FileWriter object in the client for large file transfers to the client... but if you notice this effect on your system, this is what it is. Yet another reason to throttle bandwidth.




Download TcpCommExampleProject - 39.7 KB

2 comments:

  1. that's bad.

    Lots of errors in code (not obvious errors, but errors with logic of code). This is good example how you shouldn't create network apps. However you can use it to see how you can use sockets

    ReplyDelete
  2. Hi. Read your whole article, and this sounds very useful. Do you have this in C# as well?

    ReplyDelete