How to Build a Netty WebSocket Server with Spring Boot and Java

This tutorial walks through creating a Netty‑based WebSocket server in Java, adding Maven dependencies, integrating it with a SpringBootApplication, implementing server and channel handlers, crafting a simple HTML client, and extending the setup to support URL parameters, complete with code examples and screenshots.

Programmer DD
Programmer DD
Programmer DD
How to Build a Netty WebSocket Server with Spring Boot and Java

About Netty

Netty is a client/server framework that leverages Java's advanced networking capabilities, hides the underlying complexity, and provides an easy‑to‑use API.

Maven Dependency

<dependencies>
    <!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.36.Final</version>
    </dependency>
</dependencies>

SpringBootApplication

@SpringBootApplication
public class SpringCloudStudyDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudStudyDemoApplication.class, args);
        try {
            new NettyServer(12345).start();
            System.out.println("https://blog.csdn.net/moshowgame");
            System.out.println("http://127.0.0.1:6688/netty-websocket/index");
        } catch (Exception e) {
            System.out.println("NettyServerError:" + e.getMessage());
        }
    }
}

NettyServer

/**
 * NettyServer Netty server configuration
 */
public class NettyServer {
    private final int port;
    public NettyServer(int port) { this.port = port; }
    public void start() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap sb = new ServerBootstrap();
            sb.option(ChannelOption.SO_BACKLOG, 1024);
            sb.group(group, bossGroup)
              .channel(NioServerSocketChannel.class)
              .localAddress(this.port)
              .childHandler(new ChannelInitializer<SocketChannel>() {
                  @Override
                  protected void initChannel(SocketChannel ch) throws Exception {
                      System.out.println("收到新连接");
                      ch.pipeline().addLast(new HttpServerCodec());
                      ch.pipeline().addLast(new ChunkedWriteHandler());
                      ch.pipeline().addLast(new HttpObjectAggregator(8192));
                      ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws", null, true, 65536 * 10));
                      ch.pipeline().addLast(new MyWebSocketHandler());
                  }
              });
            ChannelFuture cf = sb.bind().sync();
            System.out.println(NettyServer.class + " 启动正在监听: " + cf.channel().localAddress());
            cf.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
            bossGroup.shutdownGracefully().sync();
        }
    }
}

MyChannelHandlerPool

public class MyChannelHandlerPool {
    public MyChannelHandlerPool() {}
    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}

MyWebSocketHandler

Handles WebSocket events such as connection establishment, disconnection, and message processing.

public class MyWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("与客户端建立连接,通道开启!");
        MyChannelHandlerPool.channelGroup.add(ctx.channel());
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("与客户端断开连接,通道关闭!");
        MyChannelHandlerPool.channelGroup.remove(ctx.channel());
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            FullHttpRequest request = (FullHttpRequest) msg;
            String uri = request.uri();
            Map paramMap = getUrlParams(uri);
            System.out.println("接收到的参数是:" + JSON.toJSONString(paramMap));
            if (uri.contains("?")) {
                String newUri = uri.substring(0, uri.indexOf("?"));
                request.setUri(newUri);
            }
        } else if (msg instanceof TextWebSocketFrame) {
            TextWebSocketFrame frame = (TextWebSocketFrame) msg;
            System.out.println("客户端收到服务器数据:" + frame.text());
            sendAllMessage(frame.text());
        }
        super.channelRead(ctx, msg);
    }
    private void sendAllMessage(String message) {
        MyChannelHandlerPool.channelGroup.writeAndFlush(new TextWebSocketFrame(message));
    }
    private static Map getUrlParams(String url) {
        Map<String, String> map = new HashMap<>();
        url = url.replace("?", ";");
        if (!url.contains(";")) return map;
        if (url.split(";").length > 0) {
            String[] arr = url.split(";")[1].split("&");
            for (String s : arr) {
                String key = s.split("=")[0];
                String value = s.split("=")[1];
                map.put(key, value);
            }
            return map;
        } else {
            return map;
        }
    }
}

socket.html (WebSocket client)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Netty-Websocket</title>
    <script type="text/javascript">
        var socket;
        if (!window.WebSocket) { window.WebSocket = window.MozWebSocket; }
        if (window.WebSocket) {
            socket = new WebSocket("ws://127.0.0.1:12345/ws");
            socket.onmessage = function(event) {
                var ta = document.getElementById('responseText');
                ta.value += event.data + "
";
            };
            socket.onopen = function(event) {
                var ta = document.getElementById('responseText');
                ta.value = "Netty-WebSocket服务器。。。。。。连接  
";
            };
            socket.onclose = function(event) {
                var ta = document.getElementById('responseText');
                ta.value = "Netty-WebSocket服务器。。。。。。关闭 
";
            };
        } else {
            alert("您的浏览器不支持WebSocket协议!");
        }
        function send(message) {
            if (!window.WebSocket) { return; }
            if (socket.readyState == WebSocket.OPEN) {
                socket.send(message);
            } else {
                alert("WebSocket 连接没有建立成功!");
            }
        }
    </script>
</head>
<body>
    <form onSubmit="return false;">
        <label>ID</label><input type="text" name="uid" value="${uid!!}" /><br/>
        <label>TEXT</label><input type="text" name="message" value="这里输入消息" /><br/>
        <input type="button" value="发送ws消息" onClick="send(this.form.uid.value+':'+this.form.message.value)" />
        <hr color="black"/>
        <h3>服务端返回的应答消息</h3>
        <textarea id="responseText" style="width:1024px;height:300px;"></textarea>
    </form>
</body>
</html>

Controller

@RestController
public class IndexController {
    @GetMapping("/index")
    public ModelAndView index() {
        ModelAndView mav = new ModelAndView("socket");
        mav.addObject("uid", RandomUtil.randomNumbers(6));
        return mav;
    }
}

Effect Demonstration

Modifying Netty to Support URL Parameters

1. Adjust the handler order so that MyWebSocketHandler is added before WebSocketServerProtocolHandler:

ch.pipeline().addLast(new MyWebSocketHandler());
ch.pipeline().addLast(new WebSocketServerProtocolHandler("/ws", null, true, 65536 * 10));

2. In MyWebSocketHandler.channelRead, detect a FullHttpRequest on the first connection, extract the full URL via uri(), parse query parameters, and reset the request URI to the path part.

3. Update the HTML client to include query parameters in the WebSocket URL, e.g. ws://127.0.0.1:12345/ws?uid=666&gid=777.

4. After the changes, console output shows connection establishment, parsed parameters, and message broadcasting.

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.

JavamavenNettyWebSocketSpringBoot
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

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.