Mastering Java WebSocket: Four Practical Implementation Strategies

This article explains four ways to use WebSocket in Java—J2EE, Spring, SocketIO, and Netty—covering protocol basics, required dependencies, configuration code, and practical examples so developers can choose the most suitable approach for real‑time communication.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Mastering Java WebSocket: Four Practical Implementation Strategies

Preface

When I built an IM messaging system at my previous company, I used WebSocket as the core transport. This article introduces four common ways to use WebSocket in Java, providing reference code and tips for future projects.
WebSocket architecture diagram
WebSocket architecture diagram

The diagram above lists three WebSocket usage patterns; the fourth one is hinted at and revealed later.

WebSocket Overview

WebSocket is a TCP‑based protocol that enables full‑duplex communication, unlike HTTP’s request‑response model which requires polling or long‑polling for server push.

Compared with HTTP, WebSocket offers several advantages:

Lower resource consumption because the header is smaller.

Stronger real‑time capability: the server can push messages proactively.

Stateful connection: after the handshake, no need to resend state information on each request.

Understanding the handshake process and frame structure is essential, similar to studying TCP headers.

WebSocket defines six operation codes: Text, Binary, Fragmented, Close, Ping, and Pong.

J2EE Approach

In the J2EE style we rely on the javax.websocket package provided by Tomcat. The example uses Spring Boot with an embedded Tomcat container.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

Define a WebSocket server class and annotate it with @ServerEndpoint. The most common parameters are the URL, decoder, and encoder.

@Component
@ServerEndpoint("/j2ee-ws/{msg}")
public class WebSocketServer {
    @OnOpen
    public void onOpen(Session session, @PathParam("msg") String msg) {
        System.out.println("WebSocketServer received connection: " + session.getId() + ", msg: " + msg);
    }
    @OnMessage
    public void onMessage(Session session, String message) throws IOException {
        String reply = "WebSocketServer received: " + session.getId() + ", message: " + message;
        System.out.println(reply);
        session.getBasicRemote().sendText(reply);
    }
    @OnClose
    public void onClose(Session session) {
        System.out.println("Connection closed");
    }
}

The four key annotations are: @ServerEndpoint: maps the URL to the server class. @OnOpen: executed when a connection is established. @OnClose: executed when the connection is closed. @OnMessage: handles incoming messages (text or binary).

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

Tip: The bean is unnecessary when not using an embedded container.

Spring Approach

Spring Boot provides a higher‑level abstraction for WebSocket development.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

Implement WebSocketHandler to define custom logic.

@Component
public class SpringSocketHandle implements WebSocketHandler {
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("SpringSocketHandle, new connection: " + session.getId());
    }
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        String msg = "SpringSocketHandle, connection: " + session.getId() + ", received message.";
        System.out.println(msg);
        session.sendMessage(new TextMessage(msg));
    }
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println("WS connection error");
    }
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        System.out.println("WS closed connection");
    }
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

The interface defines five core methods: afterConnectionEstablished: called when the connection is opened. handleMessage: processes incoming messages. handleTransportError: invoked on errors. afterConnectionClosed: called after the connection is closed. supportsPartialMessages: indicates whether fragmented messages are supported.

@Configuration
@EnableWebSocket
public class SpringSocketConfig implements WebSocketConfigurer {
    @Autowired
    private SpringSocketHandle springSocketHandle;
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(springSocketHandle, "/spring-ws").setAllowedOrigins("*");
    }
}

Spring’s AbstractWebSocketHandler can be extended to avoid boilerplate when only specific message types need handling.

SocketIO Approach

Socket.IO is a JavaScript library that falls back to other transports when WebSocket is unavailable. A Java server implementation based on Netty is available via netty-socketio, but it only supports older client versions.

<dependency>
    <groupId>com.corundumstudio.socketio</groupId>
    <artifactId>netty-socketio</artifactId>
    <version>1.7.19</version>
</dependency>
@Configuration
public class SocketIoConfig {
    @Bean
    public SocketIOServer socketIOServer() {
        com.corundumstudio.socketio.Configuration config = new com.corundumstudio.socketio.Configuration();
        config.setHostname("127.0.0.1");
        config.setPort(8001);
        config.setContext("/socketio-ws");
        SocketIOServer server = new SocketIOServer(config);
        server.start();
        return server;
    }
    @Bean
    public SpringAnnotationScanner springAnnotationScanner() {
        return new SpringAnnotationScanner(socketIOServer());
    }
}
@Component
public class SocketIoHandle {
    @OnConnect
    public void onConnect(SocketIOClient client) {
        System.out.println("SocketIoHandle received connection: " + client.getSessionId());
    }
    @OnDisconnect
    public void onDisconnect(SocketIOClient client) {
        System.out.println("Connection closed: " + client.getSessionId());
    }
    @OnEvent(value = "onMsg")
    public void onMessage(SocketIOClient client, AckRequest request, Object data) {
        System.out.println("SocketIoHandle received message: " + data);
        client.sendEvent("chatMsg", "Server received connection: " + client.getSessionId());
    }
}

Netty Approach

Netty provides low‑level building blocks for WebSocket servers.

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.75.Final</version>
</dependency>
public class WebSocketNettServer {
    public static void main(String[] args) throws Exception {
        NioEventLoopGroup boss = new NioEventLoopGroup(1);
        NioEventLoopGroup work = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, work)
                     .channel(NioServerSocketChannel.class)
                     .childOption(ChannelOption.SO_KEEPALIVE, true)
                     .localAddress(8080)
                     .handler(new LoggingHandler(LogLevel.DEBUG))
                     .childHandler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         protected void initChannel(SocketChannel ch) throws Exception {
                             ch.pipeline()
                               .addLast(new HttpServerCodec())
                               .addLast(new HttpContentCompressor())
                               .addLast(new HttpObjectAggregator(65536))
                               .addLast(new WebSocketServerProtocolHandler("/ws"))
                               .addLast(WsTextInBoundHandle.INSTANCE);
                         }
                     });
            ChannelFuture cf = bootstrap.bind().sync();
            System.out.println("WebSocketNettServer started");
            cf.channel().closeFuture().sync();
        } finally {
            boss.shutdownGracefully().syncUninterruptibly();
            work.shutdownGracefully().syncUninterruptibly();
        }
    }
}
@ChannelHandler.Sharable
public class WsTextInBoundHandle extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    public static final WsTextInBoundHandle INSTANCE = new WsTextInBoundHandle();
    private WsTextInBoundHandle() { System.out.println("WsTextInBoundHandle initialized"); }
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("WsTextInBoundHandle received connection");
    }
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        String received = "WsTextInBoundHandle received: " + msg.text();
        System.out.println(received);
        String response = "{\"status\":200, \"content\":\"received\"}";
        ctx.channel().writeAndFlush(new TextWebSocketFrame(response));
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("WsTextInBoundHandle read complete");
        ctx.flush();
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("WsTextInBoundHandle exception");
        cause.printStackTrace();
        ctx.close();
    }
}

Conclusion

Among the four Java WebSocket implementations, the author prefers the Spring approach, followed by Netty, J2EE, and finally SocketIO for compatibility scenarios.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

javaNettyWebSocketSpringBootSocketIOJ2EE
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.