This week, two problems forced me to return to my old habit of digging into the details of how things work. But the problems I encountered are so general, and the readily-available solutions so lacking, that I don't feel the time was wasted. The solutions I produced may be useful to others, so I've decided to publish them separately from the rest of my project. To that end, I've created a new project on SourceForge called KrumLib.
One of the problems I encountered was with BufferedReader, a commonly-used program component for reading text line-by-line. The trouble is that when it's used to read from a network stream, recoverable errors can lead to data loss. (Actually, I hesitate even to call them errors; socket timeouts are a normal part of network programming.) This problem seems likely to affect my protocol library, so I wrote my own version of BufferedReader to avoid it.
The other problem I faced was how to accept connections from Telnet clients. Telnet is an old protocol, predating the Web by about two decades, and virtually every computer has some sort of Telnet client installed. There are also plenty of Java Telnet libraries out there to choose from. But a quick look at several of them showed that most are designed around the assumption that they will be used to write Telnet clients, not Telnet servers.
Telnet is a symmetric protocol, meaning that it's basically the same both directions. Each end has a program component called a Network Virtual Terminal which negotiates options and handles commands from the other end. The two NVTs are practically identical, except that the client side intitiates connections and the server side waits for them. And strictly speaking, how the connection gets established isn't even part of the Telnet protocol. An NVT could just as well remain completely agnostic about who initiated the connection, or even who's on the other end. You ought to be able to just hand it an open connection and set it working. So it seems pretty silly for a Telnet library to be limited to initiating connections, but that's the way everyone writes them.
I probably could have modified one of the existing libraries to listen for connections, but I estimated that it would have taken me at least as long to choose the best one, study it, modify it, and republish my changes as it did to write my own. Of course, that's only true because I had very low expectations for my own. Telnet supports a wide variety of commands and options, and I was only interested in a few of them. I've called my solution BogusNVT because, as the documentation says, it's incomplete and wrong. But it'll do the job.
I'm ready to change my focus to working on the client and server. These two sub-projects present a sort of chicken-and-egg problem, in that I can't test either until the other is written. I expect this to be a bit of a hassle, but the foundation I've laid with the protocol library and KrumLib will make it easier. And any other components I write that are generally useful, or that are used by more than one sub-project, will be added to KrumLib.