I took a glance at your socket code, and I've already found a massive problem.
From line 200:
size = recv(id, dbuff, 65536, 0);
Your receiving 65536 bytes of data.
I had 2 choices to go with...either read 1 byte at a time until I got a WSAEWOULDBLOCK error, or attempt to grab as much as I could in 1 shot and deal with whatever size returns. Reading 1 byte at a time (or even XXX byte-blocks at a time) in succession until a WSAEWOULDBLOCK _could_ possibly cause a stall (constantly reallocating memory for each new block on top of the successive reads and calling WSAGetLast each roll). I opted the choice to read in 64k (the maximum packet size) in 1 shot and be done. This is not set in stone, I can easily reduce the limits, or even make the dllInit function use an argument for the size of the backbuffer.
What happens if two "32768 byte" messages were stored on the sockets data queue?
Depends on what protocol it is...for UDP, each packet is separate entities and each "receive" of 64k will only grab the 32k each. TCP is a stream protocol, you are not guaranteed to receive the same size you sent...which means you could get 16k followed by 48k, or 32k/32k, or 64k/0k...this was the other _slight_ error that 39ster had not accounted for in his original.
What you see in the send/receive functions are the _raw_ options format, which buffers the data without caring about size of actual messages. I have plans for other formats, including filtering which I have gone over.
You would essentially be "receiving" them all as one packet which could be very bad for a game.
Your game is _suppose_ to be programmed as a continued read of data ANYWAY...something like:
Why else do you think 39ster put that function in there for? What do you think nagle does? These are no coincidences.
Your method also requires a constant 65536 byte buffer allocated.
Again, this can be reduced....figured 64k was no biggy, but I'll keep this on the front of my mind to change in the next update, 16k should be a fair size.
I didn't really look at your sending/buffer set up, but the best way would be to have a 2 byte header at the beginning of every packet containing the size of the packet.
39ster tried this as well, which was the problem I had with his TCP scheme. Let me show you a hypothetical example:
Johnny decides to send a message, "hello brian", to brian
This message is packaged as .25. (the byte code for "sending message to someone"), .31881. (the int code for _brian_s ID from the server), and "hello brian\0" (the text)
You "send" this to the 39dll, which you think it will just send it.....nooooo
1. it allocates space for the size of the buffer + 2 bytes (takes some time)
2. it writes 2 bytes to this new buffer denoting it's payload size
3. it memcopies the sent buffer to it (takes some time)
4. then it sends it
That's not that bad right? What if, for some reason (because in hypotheticals, things happen), the connection between 2 points of the cloud that Johnny and Brian both "communicate" over, can only take 48 byte payloads. This becomes a problem because our package is:
.17.25.31881."hello brian\0" + 40 bytes IP header
59 bytes total
IP is fragmentable, so guess what happens when this reaches our dead zone? In order for IP to fragment, it must add it's IP header to every piece of data in the split (along with some other things, but I'll keep it simple for now). This means our package has just turned into 3 packages:
40 bytes IP header + .17.25.31881."h"
40 bytes IP header + "ello bri"
40 bytes IP header + "an\0"
Let us now reach even further and say that these packages get to Brian's computer in this fashion, and GM "reads" them like so:
1. read in 2 bytes (at this point, it only has the first message) = .17.
2. read in 17 bytes of data = .25.31881."h" (recv function returns 6, because only those 6 bytes are currently in the window)
3. GM is given this message, which is not even finished
4. next call to "receivemessage" will read 2 bytes (which is the next message "el" is a pretty big number of bytes to read)
5. read in "el" bytes of data = "lo bri" (only 6 were available at this moment in the window)
Obviously the internet doesn't have a 48 byte MTU anywhere...but there are places that do have a 1k MTU, and UDP is sometimes limited to even 572 bytes MTU (which is truncated, not fragmented in most cases). This might not be a problem in a tiny game with a few updates...but the server might send 4k data chunks per second per person just to update what OTHER people are doing, it adds up. When IP starts to fragment these huge chunks, your clients will start destroying data without even knowing, and GM will get back junk code messages.
I _do_ have plans to have a size format of "wait to receive" which will send back a "do not have a message yet" (0) until the 'bin' buffer for that socket has reached the amount given by its 2-byte header, then it will memmove up to the next position and read the next 2-byte header. This would severely dampen the speed of your communications, so it should only be used on things like chat channels and other not-time-crucial things, but it would be MUCH better than the current system.
Edited by sabriath, 22 October 2010 - 10:53 PM.