Implementing a Netty WebSocket Server with URL Parameter Support in Spring Boot

This article demonstrates how to build a Netty‑based WebSocket server integrated with Spring Boot, covering Maven dependencies, server initialization, channel handlers, a simple HTML client, and extending the handler to parse URL query parameters for custom communication.

Top Architect
Top Architect
Top Architect
Implementing a Netty WebSocket Server with URL Parameter Support in Spring Boot

Netty is a Java client/server framework that abstracts low‑level network operations behind an easy‑to‑use API. The article starts by adding the io.netty:netty-all:4.1.36.Final dependency to a Maven project.

SpringBootApplication

The Spring Boot entry point creates a NettyServer instance on port 12345 and starts it, printing the server URL for testing.

@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

The server configures boss and worker EventLoopGroup s, sets up a ServerBootstrap with TCP backlog, binds to the specified port, and installs a pipeline consisting of HTTP codec, chunked writer, HTTP aggregator, WebSocket protocol handler, and a custom MyWebSocketHandler. It logs the listening address and shuts down gracefully.

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();
        }
    }
}

Channel Management

A static MyChannelHandlerPool holds a ChannelGroup that tracks all active WebSocket connections, allowing broadcast of messages.

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

MyWebSocketHandler

The handler overrides channelActive, channelInactive, and channelRead. On the first HTTP handshake it extracts the request URI, parses query parameters, and removes them from the URI before delegating to the WebSocket protocol handler. Subsequent TextWebSocketFrame messages are logged and broadcast to all channels.

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<String, String> 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<String, String> getUrlParams(String url) {
        Map<String, String> map = new HashMap<>();
        url = url.replace("?", ";");
        if (!url.contains(";")) return map;
        String[] parts = url.split(";");
        if (parts.length > 1) {
            String[] pairs = parts[1].split("&");
            for (String s : pairs) {
                String[] kv = s.split("=");
                if (kv.length == 2) map.put(kv[0], kv[1]);
            }
        }
        return map;
    }
}

HTML Client (socket.html)

A simple HTML page creates a WebSocket connection to ws://127.0.0.1:12345/ws, displays received messages in a textarea, and provides a form to send messages with a user‑defined UID.

<!DOCTYPE html>
<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>

Spring MVC Controller

The controller maps /index to the HTML page, injecting a random six‑digit UID for the client.

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

Adding URL Parameter Support

To handle query strings, the article reorders the pipeline so MyWebSocketHandler runs before WebSocketServerProtocolHandler. The handler extracts parameters from the initial FullHttpRequest, logs them, and strips them from the URI so the WebSocket handshake proceeds normally. The client can now connect with a URL such as ws://127.0.0.1:12345/ws?uid=666&gid=777.

Console output after the change shows the connection establishment, parameter parsing, and broadcast of messages.

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.

JavaNettyWebSocketSpringBootNetworking
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

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.