基于Netty5.0高级案例一之NettyWebsocket
2015-1-1 付政委 Netty5.0 高级篇
前言介绍:
本案例主要介绍如何使用Netty开发websocket。
环境需求:
1、jdk1.7
2、Eclipse
3、Netty5.0
4、支持websocket的浏览器[火狐]
工程截图:
代码部分:
服务端:
Global.java
- package com.itstack.netty.common;
- import io.netty.channel.group.ChannelGroup;
- import io.netty.channel.group.DefaultChannelGroup;
- import io.netty.util.concurrent.GlobalEventExecutor;
- public class Global {
- public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
-
- }
ChildChannelHandler.java
- package com.itstack.netty.websocket;
- import io.netty.channel.ChannelInitializer;
- import io.netty.channel.socket.SocketChannel;
- import io.netty.handler.codec.http.HttpObjectAggregator;
- import io.netty.handler.codec.http.HttpServerCodec;
- import io.netty.handler.stream.ChunkedWriteHandler;
- public class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
- @Override
- protected void initChannel(SocketChannel e) throws Exception {
-
- e.pipeline().addLast("http-codec",new HttpServerCodec());
- e.pipeline().addLast("aggregator",new HttpObjectAggregator(65536));
- e.pipeline().addLast("http-chunked",new ChunkedWriteHandler());
- e.pipeline().addLast("handler",new MyWebSocketServerHandler());
- }
- }
MyWebSocketServerHandler.java
- package com.itstack.netty.websocket;
- import java.util.Date;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import com.itstack.netty.common.Global;
- import io.netty.buffer.ByteBuf;
- import io.netty.buffer.Unpooled;
- import io.netty.channel.ChannelFuture;
- import io.netty.channel.ChannelFutureListener;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.channel.SimpleChannelInboundHandler;
- import io.netty.handler.codec.http.DefaultFullHttpRequest;
- import io.netty.handler.codec.http.DefaultFullHttpResponse;
- import io.netty.handler.codec.http.FullHttpRequest;
- import io.netty.handler.codec.http.HttpResponseStatus;
- import io.netty.handler.codec.http.HttpVersion;
- import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
- import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
- import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
- import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
- import io.netty.handler.codec.http.websocketx.WebSocketFrame;
- import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
- import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
- import io.netty.util.CharsetUtil;
- public class MyWebSocketServerHandler extends
- SimpleChannelInboundHandler<Object> {
- private static final Logger logger = Logger
- .getLogger(WebSocketServerHandshaker.class.getName());
- private WebSocketServerHandshaker handshaker;
- @Override
- public void channelActive(ChannelHandlerContext ctx) throws Exception {
- // 添加
- Global.group.add(ctx.channel());
- System.out.println("客户端与服务端连接开启");
- }
- @Override
- public void channelInactive(ChannelHandlerContext ctx) throws Exception {
- // 移除
- Global.group.remove(ctx.channel());
- System.out.println("客户端与服务端连接关闭");
- }
- @Override
- protected void messageReceived(ChannelHandlerContext ctx, Object msg)
- throws Exception {
- if (msg instanceof FullHttpRequest) {
- handleHttpRequest(ctx, ((FullHttpRequest) msg));
- } else if (msg instanceof WebSocketFrame) {
- handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
- }
- }
- @Override
- public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
- ctx.flush();
- }
- private void handlerWebSocketFrame(ChannelHandlerContext ctx,
- WebSocketFrame frame) {
- // 判断是否关闭链路的指令
- if (frame instanceof CloseWebSocketFrame) {
- handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame
- .retain());
- }
- // 判断是否ping消息
- if (frame instanceof PingWebSocketFrame) {
- ctx.channel().write(
- new PongWebSocketFrame(frame.content().retain()));
- return;
- }
- // 本例程仅支持文本消息,不支持二进制消息
- if (!(frame instanceof TextWebSocketFrame)) {
- System.out.println("本例程仅支持文本消息,不支持二进制消息");
- throw new UnsupportedOperationException(String.format(
- "%s frame types not supported", frame.getClass().getName()));
- }
- // 返回应答消息
- String request = ((TextWebSocketFrame) frame).text();
- System.out.println("服务端收到:" + request);
- if (logger.isLoggable(Level.FINE)) {
- logger
- .fine(String.format("%s received %s", ctx.channel(),
- request));
- }
- TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString()
- + ctx.channel().id() + ":" + request);
- // 群发
- Global.group.writeAndFlush(tws);
- // 返回【谁发的发给谁】
- // ctx.channel().writeAndFlush(tws);
- }
- private void handleHttpRequest(ChannelHandlerContext ctx,
- FullHttpRequest req) {
- if (!req.getDecoderResult().isSuccess()
- || (!"websocket".equals(req.headers().get("Upgrade")))) {
- sendHttpResponse(ctx, req, new DefaultFullHttpResponse(
- HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
- return;
- }
- WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
- "ws://localhost:7397/websocket", null, false);
- handshaker = wsFactory.newHandshaker(req);
- if (handshaker == null) {
- WebSocketServerHandshakerFactory
- .sendUnsupportedWebSocketVersionResponse(ctx.channel());
- } else {
- handshaker.handshake(ctx.channel(), req);
- }
- }
- private static void sendHttpResponse(ChannelHandlerContext ctx,
- FullHttpRequest req, DefaultFullHttpResponse res) {
- // 返回应答给客户端
- if (res.getStatus().code() != 200) {
- ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(),
- CharsetUtil.UTF_8);
- res.content().writeBytes(buf);
- buf.release();
- }
- // 如果是非Keep-Alive,关闭连接
- ChannelFuture f = ctx.channel().writeAndFlush(res);
- if (!isKeepAlive(req) || res.getStatus().code() != 200) {
- f.addListener(ChannelFutureListener.CLOSE);
- }
- }
- private static boolean isKeepAlive(FullHttpRequest req) {
- return false;
- }
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
- throws Exception {
- cause.printStackTrace();
- ctx.close();
- }
- }
NettyServer.java
- package com.itstack.netty.websocket;
- import io.netty.bootstrap.ServerBootstrap;
- import io.netty.channel.Channel;
- import io.netty.channel.ChannelOption;
- import io.netty.channel.EventLoopGroup;
- import io.netty.channel.nio.NioEventLoopGroup;
- import io.netty.channel.socket.nio.NioServerSocketChannel;
- public class NettyServer {
- public static void main(String[] args) {
-
- new NettyServer().run();
- }
-
- public void run(){
-
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- EventLoopGroup workGroup = new NioEventLoopGroup();
-
- try {
-
- ServerBootstrap b = new ServerBootstrap();
- b.group(bossGroup, workGroup);
- b.channel(NioServerSocketChannel.class);
- b.childHandler(new ChildChannelHandler());
-
- System.out.println("服务端开启等待客户端连接 ... ...");
-
- Channel ch = b.bind(7397).sync().channel();
-
- ch.closeFuture().sync();
-
- } catch (Exception e) {
- e.printStackTrace();
- }finally{
- bossGroup.shutdownGracefully();
- workGroup.shutdownGracefully();
- }
-
- }
-
- }
客户端:
- <!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>无标题文档</title>
- </head>
- </head>
- <script type="text/javascript">
- var socket;
-
-
- if(!window.WebSocket){
-
- window.WebSocket = window.MozWebSocket;
- }
-
- if(window.WebSocket){
- socket = new WebSocket("ws://localhost:7397/websocket");
-
- socket.onmessage = function(event){
-
- var ta = document.getElementById('responseText');
- ta.value += event.data+"\r\n";
- };
-
- socket.onopen = function(event){
-
- var ta = document.getElementById('responseText');
- ta.value = "打开WebSoket 服务正常,浏览器支持WebSoket!"+"\r\n";
-
- };
-
- socket.onclose = function(event){
-
- var ta = document.getElementById('responseText');
- ta.value = "";
- ta.value = "WebSocket 关闭"+"\r\n";
- };
- }else{
- alert("您的浏览器不支持WebSocket协议!");
- }
-
- function send(message){
- if(!window.WebSocket){return;}
- if(socket.readyState == WebSocket.OPEN){
- socket.send(message);
- }else{
- alert("WebSocket 连接没有建立成功!");
- }
-
- }
-
- </script>
- <body>
- <form onSubmit="return false;">
- <input type = "text" name="message" value="Netty The Sinper"/>
- <br/><br/>
- <input type="button" value="发送 WebSocket 请求消息" onClick="send(this.form.message.value)"/>
- <hr color="blue"/>
- <h3>服务端返回的应答消息</h3>
- <textarea id="responseText" style="width: 1024px;height: 300px;"></textarea>
- </form>
- </body>
- </html>
测试运行:
1、启动NettyServer
2、启动支持websocket的浏览器,打开index.html【开启2个以上】
3、服务端输出结果:
服务端开启等待客户端连接 ... ...
客户端与服务端连接开启
客户端与服务端连接开启
客户端与服务端连接关闭
客户端与服务端连接开启
服务端收到:Netty The Sinper
服务端收到:Netty The Sinper
服务端收到:www.itstack.org
服务端收到:www.itstack.org
服务端收到:您好,您也在学习Netty吗?
服务端收到:是的!一起学习吧!
服务端收到:嗯,到群里来吧5360392
服务端收到:好的,么么大
4、客户端输出截图: