smart-socket: a small Java AIO framework for long-lived TCP connections
A High Performance Java AIO framework
At a glance
- What is it?
- smart-socket is a Java AIO framework that reimplements the NIO.2 engine for throughput and memory behaviour on connections that stay open for a long time, and asks you to implement just two interfaces, a Protocol and a MessageProcessor. It is Apache 2.0 licensed, needs JDK 8 or newer, and is explicitly not a drop-in replacement for Netty.
- Who is it for?
- Adopt smart-socket if you are writing a Java service that holds a large number of long-lived TCP connections and you want an engine you can read and audit rather than a framework with a large configuration surface. Do not adopt it expecting Netty compatibility or the Netty ecosystem, because the readme states plainly that it is not a drop-in replacement, and check the plugin list before you commit, since SSL, rate limiting and metrics are extension modules rather than core.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 18 days ago.
- What is it written in?
- Mainly Java, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why not just use NIO.2
The positioning in the readme is direct: Java's native AIO, NIO.2, provides a clean asynchronous programming model, but in real-world networking workloads it often falls short.
Three specific problems are named. Threading and I/O scheduling limits under high connection counts. Memory cost that scales poorly with long-lived connections. And stability issues observed under stress on some platforms.
Those three are the whole justification for the project, and they are narrower than they first look. The claim is not that NIO.2 is slow in general, it is that its behaviour degrades in the specific regime of many connections that stay open. A request-response HTTP server that opens and closes a socket per request does not hit any of those problems, so it has no reason to switch.
What smart-socket does about it is to keep the developer-friendly AIO style while reimplementing the underlying engine for better efficiency and reliability. The stated benefits are higher throughput and lower resource usage for massive long-lived TCP connections, a very small readable core that is easy to understand and audit, and a minimal API surface where you only need to implement two interfaces.
The auditable core is worth pausing on, because that is an unusual selling point for a networking framework. Code you have to read before you can trust it has a different value proposition from code you have to configure, and for a component sitting in the path of every byte of your traffic, the first of those is often the more valuable.
The readme is also careful about what this is not. It is not a drop-in replacement for Netty, and it is described as a focused, lightweight alternative when you want less complexity and tighter control over the networking layer. The project is Java, Apache 2.0 licensed, published on Maven Central under the io.github.smartboot.socket group, and the last push was on 2026-10-06.
Two interfaces and a quick start
The API surface is the selling point, so the quick start is worth reading end to end.
The dependency, for Maven, is the aio-pro artifact, and the readme notes that you should use aio-core instead when you do not need the extension plugins and want a smaller footprint:
<dependency>
<groupId>io.github.smartboot.socket</groupId>
<artifactId>aio-pro</artifactId>
<version>2.1.3</version>
</dependency>The first interface is Protocol, which decodes bytes from the read buffer into a message of type T, and returns null when the frame is incomplete. The example is a length-prefixed string protocol with a four byte length and a payload:
import io.github.smartboot.socket.Protocol;
import io.github.smartboot.socket.transport.AioSession;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
public class StringProtocol implements Protocol<String> {
@Override
public String decode(ByteBuffer buffer, AioSession session) {
if (buffer.remaining() < Integer.BYTES) {
return null; // need more data
}
buffer.mark();
int length = buffer.getInt();
if (length > buffer.remaining()) {
buffer.reset(); // half-packet
return null;
}
byte[] bytes = new byte[length];
buffer.get(bytes);
return new String(bytes, StandardCharsets.UTF_8);
}
}The null return is the contract, and the example shows why it matters. A read buffer can hand you a partial frame, and the protocol has to be able to say so without consuming the bytes, which is why the code marks the buffer and resets it when the length exceeds what is available. Getting that wrong is the classic source of a protocol implementation that loses messages.
The second interface is MessageProcessor, which handles decoded messages and the connection lifecycle events. The example writes the response back through the session's write buffer, writing the length as an int, then the body, then flushing:
import io.github.smartboot.socket.MessageProcessor;
import io.github.smartboot.socket.transport.AioSession;
import io.github.smartboot.socket.transport.WriteBuffer;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class EchoProcessor implements MessageProcessor<String> {
@Override
public void process(AioSession session, String msg) {
System.out.println("Received: " + msg);
byte[] body = msg.getBytes(StandardCharsets.UTF_8);
WriteBuffer wb = session.writeBuffer();
try {
wb.writeInt(body.length);
wb.write(body);
wb.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}Then the server and the client, and that is the whole quick start. A working async TCP echo service with a custom protocol.
The components, and the state events that catch people out
The core concepts table is short, and each entry has one job.
Protocol decodes bytes from the read buffer into a message of type T, returning null when the frame is incomplete. MessageProcessor handles decoded messages and the connection lifecycle events, which arrive through stateEvent. AioQuickServer bootstraps and runs a TCP server, and AioQuickClient does the same for a client. AioSession is the per-connection abstraction, and you use its writeBuffer method to send data. WriteBuffer is the buffered outbound API, with writeInt, write and flush among the operations.
The state events are the part that deserves attention, because they are where a processor that only handles successful messages goes wrong. MessageProcessor.stateEvent is invoked for the important lifecycle points, and the list is specific. NEW_SESSION fires when the connection is established. INPUT_SHUTDOWN means the read side closed. SESSION_CLOSING and SESSION_CLOSED cover the teardown. DECODE_EXCEPTION and PROCESS_EXCEPTION report failures in your own code paths, and INPUT_EXCEPTION and OUTPUT_EXCEPTION report failures on the I/O side.
Two of those are worth expanding. DECODE_EXCEPTION means your Protocol threw, which is a bug in your framing code, and the session is presumably still alive in a bad state. PROCESS_EXCEPTION means your MessageProcessor threw, which in a long-lived connection service is the case where a single bad message can take down a connection that has been serving traffic for hours. INPUT_EXCEPTION and OUTPUT_EXCEPTION are the I/O side, and those are the ones that tell you the peer went away mid-write.
The readme also advises extending AbstractMessageProcessor rather than implementing MessageProcessor directly when you need plugins, because that base class wires the plugin chain for you. That is a small instruction with a large consequence, since a processor that implements the interface directly and then needs SSL or metrics will have to be rewritten.
Plugins, buffer pooling and the TCP plus UDP split
The key features table lists the pieces that are not core, and the plugin system is the largest of them: SSL/TLS, idle detection, metrics, rate limiting, traffic monitoring and more.
That list is where a project like this earns its keep, because the alternative to writing those yourself is writing them yourself, and the difference between an in-house rate limiter and one that is integrated with the connection state machine is usually the difference between a service that handles a flood and one that does not.
The idle detection and traffic monitoring entries are aimed at the same regime the framework targets, long-lived connections, where you need to know who is connected and what they are doing. The rate limiting entry is the one most teams would reach for first.
Memory is handled by buffer pooling designed for high connection counts and long-running services, which is the second of the three problems the readme names against native NIO.2. If each connection holds its own read and write buffers, the footprint of a hundred thousand idle connections is a hundred thousand allocations, and that is where the memory cost that scales poorly comes from. A pool turns per-connection state into a shared resource, and the trade is the usual one, a pooled buffer is not safe to hold onto across an await.
The transport story is TCP with UDP available via extension modules. The readme is explicit that full TCP support is in the framework and UDP comes through extensions, which matters because UDP has no connection lifecycle to hang the state machine on, and the framework's design leans on that lifecycle heavily.
Production orientation is listed as explicit connection lifecycle events and a clear state machine, and the combination of the two interfaces with the event list is what makes that claim checkable.
The repository, the version process and what is not there
The repository is a Maven multi-module project, and a few things about it are informative.
The top level has aio-core and aio-pro, which are the two artifacts you choose between, plus a benchmark directory, an example directory, a smart-socket-parent aggregator, and a pom.xml at the root. There is a Makefile whose update_version target is the interesting part. It takes a version, uses sed to rewrite the version string inside IoServerConfig.java, and then runs a sequence of Maven versions commands to set the new version in the parent, commit it, build and install it, and update the dependency version across the benchmark and example modules.
So the version is held in one place in the Java source and then propagated by the build. That is a small detail, and it is the kind of small detail that means a release cannot end up with the jar and the reported version disagreeing.
The readme notes the requirements as JDK 8 or newer, and Maven or Gradle, and the quick start dependency shows version 2.1.3, which is behind the current 2.2.0 in the Makefile.
What is not in the repository is worth noticing too. There is no GitHub release published, so the version history lives in Maven Central and in the changelog rather than on the release page. There is no benchmark result in the readme, even though a benchmark directory exists, so the performance claim that the whole project rests on is not substantiated in the documentation. There is also no comparison with Netty beyond the statement that it is not a drop-in replacement.
For an evaluation, the useful next step is the benchmark module and the example, since those are the two places where you can see the claims being exercised rather than described.
Editorial conclusion
Adopt smart-socket if you are writing a Java service that holds a large number of long-lived TCP connections and you want an engine you can read and audit rather than a framework with a large configuration surface. Do not adopt it expecting Netty compatibility or the Netty ecosystem, because the readme states plainly that it is not a drop-in replacement, and check the plugin list before you commit, since SSL, rate limiting and metrics are extension modules rather than core. Before you commit, start from the aio-core artifact rather than aio-pro if you do not need the plugins, and read the connection state events so your processor handles closure and decode failures rather than only successful messages.
Frequently asked questions
What is smart-socket?
It is a lightweight Java AIO framework for high-performance TCP applications, built for long-lived connections and custom protocols, with a simple API, a low memory footprint and no third-party dependencies in the core. It keeps the AIO style of Java's native NIO.2 while reimplementing the underlying engine for efficiency and reliability.
Is smart-socket a replacement for Netty?
No. The readme states explicitly that smart-socket is not a drop-in replacement for Netty, and describes it as a focused, lightweight alternative when you want less complexity and tighter control over the networking layer. There is also no Netty dependency in the core.
What do I have to implement to use smart-socket?
Two interfaces. Protocol decodes bytes from the read buffer into a message of type T and returns null when the frame is incomplete, and MessageProcessor handles the decoded messages and the connection lifecycle events through stateEvent. The readme suggests extending AbstractMessageProcessor instead when you need plugins, because that wires the plugin chain for you.
Which connection lifecycle events does smart-socket report?
NEW_SESSION when the connection is established, INPUT_SHUTDOWN when the read side closes, SESSION_CLOSING and SESSION_CLOSED for teardown, DECODE_EXCEPTION and PROCESS_EXCEPTION for failures in your own code, and INPUT_EXCEPTION and OUTPUT_EXCEPTION for I/O side failures.
What plugins does smart-socket provide?
SSL/TLS, idle detection, metrics, rate limiting and traffic monitoring, with UDP available through extension modules. The Maven dependency example uses the aio-pro artifact, while aio-core is the smaller option to use when you do not need the extension plugins.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/smartboot-smart-socket)Was this useful?