PyroNet (lowlevel NIO Wrapper)

Minecraft. The code I’ve written has a couple of bugs because I misunderstood NIO when implementing it, and the code isn’t really smooth any more.

FWIW: (or if SVN is your thing)

https://sourceforge.net/projects/jawnae-iowa/

After some ‘good advice’, the project is both renamed and relocated:

http://code.google.com/p/priobit/

Cool, Google Code is much better. I’m pretty done with SourceForge and their ugly new site. I still have a project over there I need to move over.

I have some trouble navigating your code. I keep looking for things similar to my code and not finding them. :wink: I’m reading my code, trying to remember gotchas, then trying to find it in your code.

I remember that when you do socketChannel.close(), it doesn’t completely close the socket. You need to do one more select to properly clean up the socket resources, so be careful not to close and exit your select loop without doing the extra select. Unfortunately I don’t remember how the problem manifests. Maybe if you close then try to open on the same port you’ll find it is still open, or check with netstat.

I noticed you are always using OP_WRITE to do writes. Apparently it is better to try the write and only use OP_WRITE if the write is partial. Apparently the write will almost always succeed the first time and is much more efficient.

Line 92 in NioServer, non-blocking accept can return null.

You might want to use setTcpNoDelay to turn off Nagle.

I saw on your old homepage you buffer some number (1400?) of bytes before sending? What happens if I send less than that? Does it timeout waiting for more and then send? How long is the timeout?

When writing my serialization lib, I found this comparison project very helpful. It gave me reasonable goals for my performance. Maybe it would be cool to setup a comparison project for Priobit/KryoNet/MINA/JGN/etc. We’d need to come up with a few tests and implement them in the different projects. I’d like to issue a preemptive “not it” for writing the MINA tests! :persecutioncomplex:

SF is so slow, it’s not funny.

[quote=“Nate,post:44,topic:33699”]
Same for me. Your code is not like mine at all.

[quote=“Nate,post:44,topic:33699”]
Sockets always linger, even if you close() then, use setSoLinger(), or terminate the JVM. This is OS level stuff that you can’t control. Try to make ~5000 TCP connections (in Windows) – it will tell you it can’t make any more. Then terminate the JVM, relaunch it, and you’ll find that you still can’t make any new Sockets. You simply have to wait a few minutes.

Good idea.

[quote=“Nate,post:44,topic:33699”]
this is inside the onReadyToAccept() method, which will only be invoked when OP_ACCEPT was flagged. Therefore I’m quite confident it will never return null.

Already the case. All NioClients go through a setup procedure in the constructor, where that is set: NioClient.configure

That would be silly, eh? :slight_smile: It will send it immediately.

I’ll give that a look later.

Not what I meant. Let me find some text on it rather than vague waving my hands around… :wink: I think the SelectableChannel javadoc has all the vagueness we need… it states a channel cannot be deregistered directly, but that its key must be canceled, which can only happen in a select. So when you close a socket, it cancels the keys, which are actually canceled in the next select, which then deregisters the channel. The symptom is that your sockets will stay in close_wait.

Just going by the javadocs. I’m not confident of anything NIO-related. :slight_smile:

Major Minor overhaul!

Updates:

  • eager write: NioClient.enqueue(...) tries to write the data immediately to the channel

  • Replaced ‘ReadFutures’ with ‘ByteFilter’ which is much more flexible, allowing to write very basic ‘state machines’ that can yield results on (for example) fixed packet length, 2-byte-length + payload, byte[] pattern delimiters (like “\r\n” for line based protocols: HTTP, FTP, SMTP, POP3, etc, etc)

  • non-copy, non-split, non-merge ByteStream class that acts like a FIFO byte-queue, replacing ByteBufferStreamer that could potentially merge/split/merge on successive incomplete writes

Check out the SVN repository
http://code.google.com/p/priobit/source/browse/trunk/+priobit/# priobit/jawnae/priobit

To listen for infinite packets (2b+payload) using ByteFilterPacket


   @Override
   public void connectedClient(final NioClient client)
   {
      client.enqueueByteFilter(new ByteFilterPacket()
      {
         @Override
         public void onReady(ByteBuffer buffer)
         {
            byte[] payload = new byte[buffer.remaining()];
            buffer.get(payload);
            System.out.println("Received: " + new String(payload));

            // listen for next packet
            this.reset();
            client.enqueueByteFilter(this);
         }
      });
   }

To listen for infinite text lines (up to 4096 bytes long) using ByteFilterEndsWith


   @Override
   public void connectedClient(final NioClient client)
   {
      client.enqueueByteFilter(new ByteFilterEndsWith("\r\n".getBytes(), 4096)
      {
         @Override
         public void onReady(ByteBuffer buffer)
         {
            byte[] line = new byte[buffer.remaining() - ("\r\n".length())];
            buffer.get(line);
            System.out.println("Received line: " + new String(line));

            // listen for next line
            this.reset();
            client.enqueueByteFilter(this);
         }
      });
   }

(pump up)
Topic title needs a refresh, project’s current name is Pyronet ;D Good name if you ask me.

Yes, a surprisingly good name! ::slight_smile: :stuck_out_tongue:

KevGlass had been harrassing me about Priobit, and he liked PyroNet. Although I knew it was way too similar to KryoNet, I just couldn’t help myself and gave in.

Besides that, as absolutely nobody is using it, I’m not even updating the SVN repo anymore.

Everything is on pyronet now, isn’t it? I’m going to read your source code…

Do you plan to implement something similar to RMI?

Did you read the summary?
http://code.google.com/p/pyronet/

PyroNet is a fast, minimalistic, lowlevel abstraction layer over the high performance Java NIO API. The aim of the API is not to provide new functionality, but to reduce the complexity of interfacing with non-blocking I/O. The PyroNet API takes care of the boilerplate code, while not forcing any protocol on the developer. Then API is event driven and uses a single Selector per PyroNetwork. You will receive event notifications through a listener interface.

Yes but JGN has something like RMI, I was wondering whether your API has such a mechanism but you’re right, it is only a low-level API.

Excuse me for the long time without updates.

If anybody is using it (I know at least 1 person besides me is using it… yay) here is a complete rewrite.

The barely high-level stuff is removed from the core, and can be bolted on top. The core is basically nothing-but-a-nio-wrapper, with the classes at its core:
=> PyroSelector => PyroServer => PyroClient

Usability wise, the most important feature is being able to listen for events per server or client instance.
`
selector.addListener(PyroSelectorListener);
=> public void taskCrashed(Runnable, Throwable);
=> public void serverBindFailed(IOException);
=> public void clientBindFailed(IOException);

server.addListener(PyroServerListener);
=> public void acceptedClient(PyroClient);

client.addListener(PyroClientListener);
=> public void connectedClient(PyroClient);
=> public void unconnectableClient(PyroClient);
=> public void droppedClient(PyroClient, IOException);
=> public void disconnectedClient(PyroClient);
=> public void receivedData(PyroClient, ByteBuffer);
=> public void sentData(PyroClient, int);
`

Technically, the most important feature is that PyroNet now supports ‘worker selectors’, transparantly! That means that you can have a pool of NIO selectors over which the load is spread. This is useful for high-I/O, otherwise saturating a single CPU core.

It also fixes minor issues with SocketChannel.close() that happened every other thursday.

The JAR includes sourcecode: http://indiespot.net/files/pyronet_20100426.jar

An embedded usecase/unittest is a multithreaded SOCKS4 proxy server. Setup your browser to use a proxy, and browse away!

Will update the SVN in a few days.

Finally found time to update the SVN, and made some stability updates too.

Socket timeouts for connect/read/write/close, now also work reliably.

To my amazement nonblocking channels screw up on those occasionally too, leading to resource leaks. PyroNet now efficiently tracks timeouts itself.

client.setTimeout(millis);

Nice work here, very useful.

Thanks for open-sourcing it.

By the way, are the files listed here different versions or just old releases?
http://code.google.com/p/pyronet/downloads/list

Thanks!

Regarding the downloads: just pick the highest number :slight_smile: GoogleCode is very protective of my data, up to the point where I cannot (seem to) overwrite my own files. The latest version should match the SVN version.

Oh, and before exploring/using this NIO wrapper, you should really ask yourself whether you actually need NIO. The non-blocking nature makes your code hard to debug, full of callbacks and plain complex compared to old I/O… and if you’re not handling thousands of connections, it’s mainly a (fast) royal PITA.

I find I use it less and less myself :persecutioncomplex: There is actually one project remaining that I use it for: a cluster of datamining servers that is handling incoming data at 500 (new) connections per second. The Unix Load is 0.02, yay.

If you are not absolutely sure you need it, don’t use it!