My insights tend to get pretty tedious. But, FWIW, enclosed is how I modified juce_socket.cpp and juce_socket.h for my experiment (porting an existing applet from iOS to JUCE and running on all JUCE platforms).
As it happens, the existing applet speaks several UDP protocols (we do networking a lot). The stuff I set out to work around where:
- Connect drops existing socket (binding is lost)
- Port 0 was disallowed (system assigns)
- Binding is always to IP_ANY
- Address can’t be shared
In a broadcast protocol, like ArtNet or LP Net discovery, there can be several different apps listening on the same machine, so address sharing was a must. The binding to IP_ANY was also a must fix, since on an iPhone, you’ll never broadcast anything, since you won’t bind to the WiFi adapter by default, same with many multi adapter setups on Windows and Mac.
Port 0 was just a ‘nice’ thing to work out, since there are times you can let the system assign the port and that is just one more possible conflict you can ignore. The connect/rebind thing was sort of in the middle. In a simple case, I could work around, but it made some stuff complicated, and I like simple in the code at the top (hence my enjoyment of JUCE!)
I tried to stick with the original model as I could understand it, and maintain backwards compatibility. First, I added a flag to one of the DatagramSocket constructors so that address sharing can be enabled for the socket, and then defaulted it to off. Next I allowed a port of 0 to be passed to the bind function, and I added an optional parameter for the local address to bind to to the constructor and to the DatagramSocket bind (defaulting to IP_ANY, so the behavior is unchanged if not specified).
I didn’t want to pass a uint32 for the local address, so I made a IpAddress class, much like your MACAddress class, pretty much a simple container, with a static member for collecting all the IPv4 addresses from all the interfaces on the given platform. I didn’t bother with IPv6, because it is a non issue in UDP/broadcast applications. That stuff is virtually all same segment.
Those changes got me up and running. I was able to broadcast, negotiate, etc., but I found that populating the structures was a pain, since most the protocols use network byte ordering and htonl, etc. require the socket headers, which are platform specific, so I made another class, Socket, which some static helpers and a helpful constant or two, so that I could implement the basic protocols without including any extra headers in the app. If that was elsewhere in the framework, I’m sorry, I took a quick look, but it’s big and I’m new.
Then, for fun, I went ahead and added to more methods to DatagramSocket so that you can subscribe and drop from multicasts, which is a better way for broadcast protocols to work. I went ahead and tested it be implementing a CITP test, and interacted with a media server.
The code isn’t terribly conforming, sorry, that wasn’t my main goal. I didn’t blatantly disregard the existing style, but I didn’t agonize over it either. Also, since the Introjucer blows it away each run, I stuffed it all in the one .cpp/.h pair, instead of spreading it out more logically. On a couple of things, I would have liked to be more conforming, but just didn’t have time. For example, one of the three variations of IpAddress::findAllIpAddresses uses the old style BSD ioctl. To be really portable, you have to be prepared for a weird overrun case. I grabbed that from something I had done before and didn’t have time to go back and rework HeapBlock in and debug it.
I’ve tried the new classes and UDP protocols on top of them on Windows, Mac, iOS, and Android. My Linux box is currently toast, sorry. The only Linux part that concerns me is the findAllIpAddresses, that uses the new ioctl/SIG protocol. It should be identical to Android, but should…
I didn’t make any changes to the StreamingSocket side. You were already turning Nagle off (TCP_NODELAY) and allowing address sharing on listen. There are probably a couple of things I would tweak to better support simple server stuff, but it was fun keeping it in a nice simple model.
Understand, this isn’t production code. It is an experiment on the side (though I did some basic testing so I won’t be embarrassed when I make my pitch for single platform at work!) - so you won’t be getting any ‘why don’t you merge…’ pouts!
BUT, I just didn’t see a way to address normal UDP/Broadcast applications without something changing. If nothing else, the always binding to IP_ANY (the lowest numeric interface for sending) is killer. Not just on iPhone, but on multi adapter machines in general. I figured that it was at least tossing over the wall as food for thought for when and if you revisit Socket support.
I still owe you an applet with some minor cosmetics, and my notes on getting Android up, but this little side track into a JUCE port has sucked up all my available ‘side’ bandwidth.