I’m working on client-server code, and usually, client and server are different JVM processes connected through Internet sockets. So far, all is well.
Now I want a “local mode”, where client and server run within the same JVM, but still in different threads. (This is both to make testing easier, and to offer players the option to run a solo game on their own if they want.)
That scenario can be done using Internet sockets, simply by using localhost, but it’s needlessly running each message through serialization, sender network stack, loopback interface, receiver network stack,and deserialization. Plus, it requires configuring a port (okay, that could be done using an ephemeral port, but Spidermonkey isn’t telling me the port it uses if I tell it to use port 0 so it autoselects one).
So… what I’d like to have is a Client and a Server subclass that simply hands over a clone() of the Message.
I’d like to use that for another kind of test: network delay simulation. Put the messages into a queue and deliver them after some delay. Introduce jitter, dropped packets, out-of-order delivery, and all the things that make UDP such a fun challenge and TCP so laggy sometimes.
I know that there are software packages to deal with that at the network level, but most are rather unpolished and not suitable for use inside unit tests. Running a network simulation inside of Java would simplify this kind of thing greatly.
Is something like that available in Spidermonkey?
Or maybe inside the JDK? Or as a Java library?
No, this kind of loopback is not supported. For regular games, it is unnecessary and a generally poor design. (Some people ask for this to do single player games without changing their code but if the network layer was properly abstracted then it is unnecessary to involve it at all for single player.)
As the performance profile will be very different than running over a socket, I recommend still using sockets for performance testing. Some performance can be simulated by adding a general message listener early that pauses, etc. for random amounts of time. It’s not perfect but because of the way SM synchs on connections it actually does simulate network delays pretty accurately. The imperfect part is that the messages actually are still being received and queued… so if your network protocol is particularly chatty then you can build up quite a backlog. Also, while it simulates latency for specific messages it does not properly simulate a general latency without some additional logic to only delay messages based on time+delay instead of constant delay.
If you really have your heart set on implementing a loopback then you might be able to do it by writing your own kernel and connector implementation. Something that may be both a kernel and a connector that you then pass to the client and server when creating them. You’d bypass the Network utility methods, of course. As I recall, all those methods are doing is wrapping up the “create kernel/create connector” logic up to make it easier.
I have looked into Kernel and related classes, and saw that they require that the connection objects have port numbers. Which would be entirely pointless (and if the code somewhere assumes that different connections use different port numbers, I’m greatly out of luck anyway).
Nevermind; I’ll simply use the loopback interface and ephemeral ports. Client and server can still run inside the same JVM even if I do that.
In the long term, I might want to find or write a library that proxies connections through localhost and adds things like lag, jitter, and dropped packets. Would be a great way to test networking issues in isolation from other trouble sources (such as ISP traffic shaping, the tester at the other end doing something dumb, or the JDK at the other end doing something dumb).
I have looked into Kernel and related classes, and saw that they require that the connection objects have port numbers. Which would be entirely pointless (and if the code somewhere assumes that different connections use different port numbers, I'm greatly out of luck anyway).
I’m really not sure what you are talking about. Neither Connector nor Kernel have anything with a port on it. You will have to be more specific.
You’re right. I was in a hurry and typing from memory, so I got that wrong.
I probably stared too much at the kernel.tcp and kernel.udp packages, there doesn’t seem to be any port in the entire kernel package (not counting subpackages).
Ah. Right. I gravitated around Connection. Also, I didn’t find the connection factories so I didn’t know where a, say, kernel.jvmthread connection could be injected.
See line 119 of DefaultServer:
// Note: it does bug me that channels aren’t 100% universal and
// setup externally but it requires a more invasive set of changes
// for “connection types” and some kind of registry of kernel and
// connector factories. This really would be the best approach and
// would allow all kinds of channel customization maybe… but for
// now, we hard-code the standard connections and treat the +2 extras
I didn’t find it the first time through, but I guess that’s the reason why I ended my hunt for places where to adapt the code ended inconclusively with a “might be possible to too much time to find out how to do it, let’s just stick with localhost”.
(Caveat: My checkout is a few weeks old.)
The ports are arbitrary in this case. If you didn’t have real network connections then it would either serve as a way of matching up the channels or you’d completely ignore it. Channels are kind of a critical feature for games that use TCP.
I designed SM to allow swapping out other types of transports at a low level. This comment represents the one concession I had to make for channels… because the expedient way was 100x easier than the “right” way… and shouldn’t actually cause any problems other than from a “design purity” perspective. Instead of ports think of them like “channel UIDs” or something.