websocket协议开发
首先是服务端启动类:
@Slf4j public class WebSocketServer { private static final int port = 8888; public void run() { NioEventLoopGroup boss = new NioEventLoopGroup(); NioEventLoopGroup worker = new NioEventLoopGroup(); try { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(boss, worker) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); /** *将请求和应答消息编码或者解码为Http消息 */ pipeline.addLast("http-codec", new HttpServerCodec()); pipeline.addLast("aggregator", new HttpObjectAggregator(65536)); /** *向客户端发送HTML5文件,主要用于支持浏览器和服务端进行WebSocket通信 */ pipeline.addLast("http-chunked", new ChunkedWriteHandler()); pipeline.addLast("handler", new WebSocketServerHandler()); } }); Channel channel = bootstrap.bind(port).sync().channel(); log.info("web socket server started at port:{}", port); channel.closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } finally { boss.shutdownGracefully(); worker.shutdownGracefully(); } } public static void main(String[] args) { new WebSocketServer().run(); } }
处理器
@Slf4j public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> { private WebSocketServerHandshaker handshaker; private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss"); @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { /** * 处理webSocket握手请求 */ handleHttpRequest(ctx, (FullHttpRequest) msg); } else if (msg instanceof WebSocketFrame) { /** * 处理webSocket请求 */ handleWebSocketFrame(ctx, (WebSocketFrame) msg); } } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) { /** * 先判断解码是或否成功,然后判断是不是请求建立WebSocket连接 */ if (!request.decoderResult().isSuccess() || (!"websocket".equals(request.headers().get("Upgrade")))) { sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); } /** * 创建握手处理类WebSocketServerHandshaker来构造握手响应返回客户端 */ WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://localhost:8888/websocket", null, false); handshaker = wsFactory.newHandshaker(request); if (null == handshaker) { WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); } else { handshaker.handshake(ctx.channel(), request); } } /** * 接收的消息是已经解码的WebSocketFrame消息 * * @param ctx * @param frame */ private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { /** *先对控制帧判断 *判断是否为关闭链路指令 */ if (frame instanceof CloseWebSocketFrame) { handshaker.close(ctx.channel(), ((CloseWebSocketFrame) frame).retain()); return; } if (frame instanceof PingWebSocketFrame) { ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); return; } if (!(frame instanceof TextWebSocketFrame)) { throw new UnsupportedOperationException(String.format("%s frame type not supported", frame.getClass().getName())); } String request = ((TextWebSocketFrame) frame).text(); log.info("receive msg:{}", request); ctx.channel().write(new TextWebSocketFrame(request + ",欢迎使用Netty WebSocket服务" + dateTimeFormatter.format(LocalDateTime.now()))); } private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) { if (response.status().code() != HttpStatus.SC_OK) { ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8); response.content().writeBytes(buf); buf.release(); setContentLength(response, response.content().readByte()); } ChannelFuture future = ctx.channel().writeAndFlush(response); if (!isKeepAlive(response) || response.status().code() != HttpStatus.SC_OK) { future.addListener(ChannelFutureListener.CLOSE); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
客户端页面需要通过js发起建立WebSocket的连接,然后进行通信,页面代码如下:
<!DOCTYPE html> <html> <head> <title>Netty WebSocket时间服务器</title> <meta name="content-type" content="text/html; charset=UTF-8"> </head> <br> <body> <br> <script type="text/javascript"> var socket; if(!window.WebSocket){ window.WebSocket=window.MozWebSocket; } if(window.WebSocket){ socket=new WebSocket("ws://localhost:8888/webSocket"); socket.onmessage=function(event){ var ta=document.getElementById('responseText'); ta.value=""; ta.value=event.data; }; socket.onopen=function(event){ var ta=document.getElementById('responseText'); ta.value='打开WebSocket服务器正常,浏览器支持WebSocket!'; }; socket.onclose=function(event){ var ta=document.getElementById('responseText'); ta.value=''; ta.value="WebSocket 关闭!"; }; }else{ alert("抱歉,您的浏览器不支持WebSocket协议!"); } function send(message){ if(!window.WebSocket){ return; } if(socket!=null){ socket.send(message); }else{ alert("WebSocket连接没有建立成功,请刷新页面!"); } } </script> <form onsubmit="return false;"> <input type="text" name="message" value="Netty最佳实践"/> <br><br> <input type="button" value="发送WebSocket请求消息" onclick="send(this.form.message.value)"/> <hr color="blue"/> <h3>服务端返回的应答消息</h3> <textarea id="responseText" style="width:500px;height:300px;"></textarea> </form> </body> </html>
立志如山 静心求实
浙公网安备 33010602011771号