Download the code, put a test file into server's directory, run both, in the client command line type FILE filename.ext
For some bad english along with some explanations READ ON
If you're new to UDP, I suggest you reading UDP Send and Receive using threads in VB.NET by Kumudu Gunasekara. I used his work as a take-off point, and wish to thank him. There's also a TinyUDP component article on Code Project, which also looks very promising. What I'm doing here is going a bit more in depth, to show a possible way of UDP communication.
As you may already heard UDP is well-know as "unreliable" protocol. Unreliable means that you have no guarantees that the packets you send will ever reach their destination. UDP also doesn't guarantees that individual packets of a transmission will arrive in the same order they were sent. There is also a problem of duplicate messages (you sent 1 and 3 arrived, woah!). If any kind of reliability for the information transmited is needed, it must be implemented in upper layers - i.e. in your application.
So your first task as UDP coder is to design a low-level protocol, which will be handling datagram transfers for you. Two things it must do: ensure delivery and ensure delivery-in-correct-order. On top of that protocol you may later add a protocol for actual communications (i.e. chat message commands, nickname changes, etc).
I do not recommend you to use my code in your real-world client/server applications. Just see how it works (not too good at times!) and build a better one.
As mentioned before, this design uses 2 protocols, low-level or delivery protocol for datagram transmissions and and high-level or actual protocol for application communications. Both protocols benefit from BinNumerization proccess, which I must explain before we go on.
BinNum, UnBinNum and packet delimiting
Imagine a task: transfer 2 vairables A and B (they containt numbers 34 and 257) over a network. What are the possible ways to do this?
Encode like this (most common way): 34|257
Numbers are delimited by a special ASCII character, called.. delimiter. Some times CrLf is used for such purpose. The main downfall of this method is a guy, who comes to your chat with a name like 'TheE||vis' . And even more serious problem lies in a sphere of file transfer. Binary files tend to use all kind of bytes in them, you know.
Delimiting is the most obious way to do things, and because of that, I strongly recommend avoiding it.
Encode like this (sometimes used in ASCII protocols): 0003400257
Each number uses 5 digits to represent itself and adds trailing zeros. This way, dividing the string into 2 parts will give you 00034 and 00257. Downfalls: you lose some bytes on stupid zeros.
Encode like this : 2.343.257
What's actually going on here is a length of string representing your number delimited from the actual number with an ASCII character. This way, parser reads up everything you need before the dot (will get "2"), then eats out exactly 2 characters ("34"), which will leave him with a "3.257" string, and all he has to do is.. repeat.
By the way, if you're developing a protocol, consider the above scheme! This is ofcourse more imortant on TCP, where the packets are streamed and cutting/fitting chunks is a number one priority.
That's easy. Each number is encoded as a byte. Or as a word. This is almost a perfect way to transfer data. The only downfall of which is: I can encode 34 into a byte, but have no way to fit 257 into a byte, so, I have to use a word for it. If variable B (257) becomes 255 someday, it will spare 3 extra bytes for it's word encoding.
This method requires some work on the sending and receiving sides, but NEVER spares even a single byte with unnessecery data. The number is encoded as byte, if it is below 248 and as a string with it's length added if it's above. 255-248 = 7 digit numbers maximum. You can adjust those values for your needs. But be sure to do this on both sides!
Public Function UnBinNum(ByRef S$, Optional ByVal EatOut As Boolean = True) As Integer
Dim l As Int16
Dim nval As Long
Dim h As String
If Asc(Left(S$, 1)) < 249 Then
nval = Asc(Left(S$, 1))
If EatOut = True Then S$ = Right$(S$, Len(S$) - 1)
l = Asc(Left$(S$, 1)) - 248
h = Mid$(S$, 2, l)
If Len(S$) < l + 1 Then nval = -1 : GoTo ErrorS
nval = HexToDecimal(h)
If EatOut = True Then S$ = Right$(S$, Len(S$) - l - 1)
Function BinNum(ByVal NUM) As String
Dim h As String, l As Byte
If NUM > 248 Then
h = Hex(NUM)
l = Len(h) + 248
If l > 255 Then MsgBox("L:" & l & "...." & h & "..." & Len(h))
Return (Chr(l) & h)
Yeah, I use those 2 in VB6 too. They also rely on
HexToDecimal function found in the source (written not by me). Also note, that
UnBinNum takes it's argument ByRef!
Anyways, this implementation is not perfect (one of the downfalls is that you can't use negative numbers), but should give you a fair idea and prove, that the concept IS perfect. Well, at least almost: you still lose some bytes when encoding numbers in range 249-255 (they are encoded as strings, not as bytes).
LOW-Level Delivery Protocol
Each packet begins with 2 bytes and 1 BinNum representing the ClientID, PacketType and Sequence. The rest is a HIGH-level data and shouldn't be used on a low level.
All three are vital. Since there is no way to determine a single connection in UDP (all you have to deal with is: IP, PORT, DATA that came) clientID comes in place.
On my server example IP+ClientID form a unique client, while IP+Port are only used while there's no ClientID at all. But that happens only during the handshake.
Client begins to transmit the NIL packet (made of three empty bytes, or 2 empty bytes and 1 empty BinNum, which is the same at the moment). Basicly client is saying:
I'm new client, no ClientID, no transmission history, no previous experience, please give me a ClientID, I could work with.
The server assigns a new ClientID to a pair of IP+port, and sends a welcoming message (one from the high level protocol).
PacketTypes & Delivery
The most usefull PacketType is INF (byte 2). It suggests a real data inside. With each INF received, receiver should send back the packet with type ACK (byte 0) or BUF (byte 1) which mean basicly the same: PACKET RECEIVED, DO NOT RESEND.
The sender keeps sending the INF from the queue until it gets an ACK (or a BUF). What is the differnce between ACK and BUF you ask? BUF is sent whenever the packet is out of sequence (too old or came from the future), and might be not implemented by your client (if you have no buffering mechanism), but the receiver MUST ACK every incoming packet.
Function Compose(ByVal RAWDATA as String)
outSeq += 1
RAWdata = Chr(clientID) & Chr(typ) & BinNum(outSeq) & RAWdata
Dim dlg As New UDPMaster.DGram
dlg.IP = IP
dlg.Port = port
dlg.data = UDPMaster.StringToBytes(RAWdata)
If seqNum = mCl.inSeq Then udp.Send(mCl.IP, mCl.port, mCl.ComposeACK)
mCl.inSeq += 1
There is also a 255 or TERMINATION PacketType, which is sent upon disconnection. It is not mandatory, cause UDP is connection-less, and applications usually know their ways to tell disconnected clients (i.e. long time without a single ACK, or a simple ping time out).
Each packet you queue to send is assigned a Sequnence number, which increases with every such action. Your ClientID as also packed inside the header as the first byte. The PacketType is INF, ACK or BUF. Only INF packets contan actual data, the rest are for signaling and managment.
Server could send a NIL packet to the client, which means, that they have to perform a handshake again. This option should be used after a long disconnection time.
HIGH-level actual protocol
Nothing fancy here. It's mostly ASCII, with simple uppercased words as commands. Availible commands are "WHO" to list all connected clients, "SAY text" to transmit said text to everyone, and "FILE filename" to request a file transfer.
I've extracted the most generic bit to a
UDPMaster class, which can serve both as a client and a server.
UDPMaster also contains
DGram class which is a structure to hold incoming datagrams.
Here's how you use it to make a client (OR! a server).
Dim udp as new UDPMaster(2002)
If udp.hasnews then
Dim dgram as UPDMaster.Dgram
Console.WriteLine ("DATAGRAM received ") ;
Console.WriteLine ("Sender: " & dgram.IP & ":" & dgram.port)
udp.send(drgam.IP, dgram.port, "REPLY")
Both client and the server are built on top of that code, but that's it. The
does not contain any of the protocol specific features described above. It just reports new datagrams (or sends yours). Working with packets, deciding which goes to where, buffering them and acknowledging - all that is done on a higher level.
The other class shared by both programs is
client class. No particular reason for this, it was just convnient to use: this class stores IP, data, ping times etc, of 1 given client, so instead of declaring all those variables on the client side, I just stole the class from the server and declared a single instance. This makes files UDPMaster.vb and Helper.vb, found in both sollutions, identical.
I've left alot of comments, but they more point to something, then explain anything (my comment righting skills are very low). To get a better understanding of what is going on you may uncomment all of those <code>BetCon</code> lines, they do the reporting to the console.
Only implement at server and should ONLY be seen as TEST-DEMO-DIRECTION-ETC. The pressure parameter (each client has one) is an ammount of ticks (1/1000 of second) the server will wait for ACK before retransmitting the data. Lowering this value will increase the pressure and the ammount of packets sent.
I've tried to implement a slow start method (as seen in TCP), but with not much luck. The default pressure is 1000, which is then reduced to a suitable value, depending on packetloss.
The calculation of packetloss is made every 10 packet sends. The server divides ammount of sent packets to the ammount of received ACKs. If packet loss is high it will reduce pressure, and vice versa.
Round trip times
Time it takes for a packet to travel to it's destination and back. Calcualted by 2 timestamps (sent, received). This value should be aproximated (that's what gurus suggest), and that's exactly what those commented lines of code do (found in
Done method of
I hope, you'll be able to build a desent pressure control system, cause I kinda failed on that task. If you're really up to this task, try reading TCP/IP RFCs, they provide alot of intersting techniques.
1. It is possible to achive better speeds with threading. I'm not any good with threads, so it's up to you. In current design a client first reads all the data, then ACKS - this could lead to some serious bottlenecks.
2. File transfer speed varies every time, I start those applications, but after some time it begins to go down. I beleive, it's caused by a bug I was unable to locate, or maybe something in my logic is completly broken :)
3. If you have any suggestions or corrections PLEASE tell me. I'm really interested in this topic.
4. I also don't know a way how to protect a server from abusive clients. In TCP you'd just disconnect an abusive client.. In UDP he's not connected at all at the first place! You can easily ignore his messages, but they still keep coming.