基于Netty5.0高级案例一之NettyWebsocket

基于Netty5.0高级案例一之NettyWebsocket

2015-1-1 付政委 Netty5.0 高级篇

前言介绍:

    本案例主要介绍如何使用Netty开发websocket。

环境需求:

    1、jdk1.7

    2、Eclipse

    3、Netty5.0

    4、支持websocket的浏览器[火狐]
工程截图:


代码部分:


服务端:

Global.java

  1. package com.itstack.netty.common;
  2. import io.netty.channel.group.ChannelGroup;
  3. import io.netty.channel.group.DefaultChannelGroup;
  4. import io.netty.util.concurrent.GlobalEventExecutor;
  5. public class Global {
  6. public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
  7. }



ChildChannelHandler.java

  1. package com.itstack.netty.websocket;
  2. import io.netty.channel.ChannelInitializer;
  3. import io.netty.channel.socket.SocketChannel;
  4. import io.netty.handler.codec.http.HttpObjectAggregator;
  5. import io.netty.handler.codec.http.HttpServerCodec;
  6. import io.netty.handler.stream.ChunkedWriteHandler;
  7. public class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
  8. @Override
  9. protected void initChannel(SocketChannel e) throws Exception {
  10. e.pipeline().addLast("http-codec",new HttpServerCodec());
  11. e.pipeline().addLast("aggregator",new HttpObjectAggregator(65536));
  12. e.pipeline().addLast("http-chunked",new ChunkedWriteHandler());
  13. e.pipeline().addLast("handler",new MyWebSocketServerHandler());
  14. }
  15. }



MyWebSocketServerHandler.java


  1. package com.itstack.netty.websocket;
  2. import java.util.Date;
  3. import java.util.logging.Level;
  4. import java.util.logging.Logger;
  5. import com.itstack.netty.common.Global;
  6. import io.netty.buffer.ByteBuf;
  7. import io.netty.buffer.Unpooled;
  8. import io.netty.channel.ChannelFuture;
  9. import io.netty.channel.ChannelFutureListener;
  10. import io.netty.channel.ChannelHandlerContext;
  11. import io.netty.channel.SimpleChannelInboundHandler;
  12. import io.netty.handler.codec.http.DefaultFullHttpRequest;
  13. import io.netty.handler.codec.http.DefaultFullHttpResponse;
  14. import io.netty.handler.codec.http.FullHttpRequest;
  15. import io.netty.handler.codec.http.HttpResponseStatus;
  16. import io.netty.handler.codec.http.HttpVersion;
  17. import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
  18. import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
  19. import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;
  20. import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
  21. import io.netty.handler.codec.http.websocketx.WebSocketFrame;
  22. import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
  23. import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
  24. import io.netty.util.CharsetUtil;
  25. public class MyWebSocketServerHandler extends
  26. SimpleChannelInboundHandler<Object> {
  27. private static final Logger logger = Logger
  28. .getLogger(WebSocketServerHandshaker.class.getName());
  29. private WebSocketServerHandshaker handshaker;
  30. @Override
  31. public void channelActive(ChannelHandlerContext ctx) throws Exception {
  32. // 添加
  33. Global.group.add(ctx.channel());
  34. System.out.println("客户端与服务端连接开启");
  35. }
  36. @Override
  37. public void channelInactive(ChannelHandlerContext ctx) throws Exception {
  38. // 移除
  39. Global.group.remove(ctx.channel());
  40. System.out.println("客户端与服务端连接关闭");
  41. }
  42. @Override
  43. protected void messageReceived(ChannelHandlerContext ctx, Object msg)
  44. throws Exception {
  45. if (msg instanceof FullHttpRequest) {
  46. handleHttpRequest(ctx, ((FullHttpRequest) msg));
  47. } else if (msg instanceof WebSocketFrame) {
  48. handlerWebSocketFrame(ctx, (WebSocketFrame) msg);
  49. }
  50. }
  51. @Override
  52. public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
  53. ctx.flush();
  54. }
  55. private void handlerWebSocketFrame(ChannelHandlerContext ctx,
  56. WebSocketFrame frame) {
  57. // 判断是否关闭链路的指令
  58. if (frame instanceof CloseWebSocketFrame) {
  59. handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame
  60. .retain());
  61. }
  62. // 判断是否ping消息
  63. if (frame instanceof PingWebSocketFrame) {
  64. ctx.channel().write(
  65. new PongWebSocketFrame(frame.content().retain()));
  66. return;
  67. }
  68. // 本例程仅支持文本消息,不支持二进制消息
  69. if (!(frame instanceof TextWebSocketFrame)) {
  70. System.out.println("本例程仅支持文本消息,不支持二进制消息");
  71. throw new UnsupportedOperationException(String.format(
  72. "%s frame types not supported", frame.getClass().getName()));
  73. }
  74. // 返回应答消息
  75. String request = ((TextWebSocketFrame) frame).text();
  76. System.out.println("服务端收到:" + request);
  77. if (logger.isLoggable(Level.FINE)) {
  78. logger
  79. .fine(String.format("%s received %s", ctx.channel(),
  80. request));
  81. }
  82. TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString()
  83. + ctx.channel().id() + ":" + request);
  84. // 群发
  85. Global.group.writeAndFlush(tws);
  86. // 返回【谁发的发给谁】
  87. // ctx.channel().writeAndFlush(tws);
  88. }
  89. private void handleHttpRequest(ChannelHandlerContext ctx,
  90. FullHttpRequest req) {
  91. if (!req.getDecoderResult().isSuccess()
  92. || (!"websocket".equals(req.headers().get("Upgrade")))) {
  93. sendHttpResponse(ctx, req, new DefaultFullHttpResponse(
  94. HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
  95. return;
  96. }
  97. WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
  98. "ws://localhost:7397/websocket", null, false);
  99. handshaker = wsFactory.newHandshaker(req);
  100. if (handshaker == null) {
  101. WebSocketServerHandshakerFactory
  102. .sendUnsupportedWebSocketVersionResponse(ctx.channel());
  103. } else {
  104. handshaker.handshake(ctx.channel(), req);
  105. }
  106. }
  107. private static void sendHttpResponse(ChannelHandlerContext ctx,
  108. FullHttpRequest req, DefaultFullHttpResponse res) {
  109. // 返回应答给客户端
  110. if (res.getStatus().code() != 200) {
  111. ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(),
  112. CharsetUtil.UTF_8);
  113. res.content().writeBytes(buf);
  114. buf.release();
  115. }
  116. // 如果是非Keep-Alive,关闭连接
  117. ChannelFuture f = ctx.channel().writeAndFlush(res);
  118. if (!isKeepAlive(req) || res.getStatus().code() != 200) {
  119. f.addListener(ChannelFutureListener.CLOSE);
  120. }
  121. }
  122. private static boolean isKeepAlive(FullHttpRequest req) {
  123. return false;
  124. }
  125. @Override
  126. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
  127. throws Exception {
  128. cause.printStackTrace();
  129. ctx.close();
  130. }
  131. }



NettyServer.java


  1. package com.itstack.netty.websocket;
  2. import io.netty.bootstrap.ServerBootstrap;
  3. import io.netty.channel.Channel;
  4. import io.netty.channel.ChannelOption;
  5. import io.netty.channel.EventLoopGroup;
  6. import io.netty.channel.nio.NioEventLoopGroup;
  7. import io.netty.channel.socket.nio.NioServerSocketChannel;
  8. public class NettyServer {
  9. public static void main(String[] args) {
  10. new NettyServer().run();
  11. }
  12. public void run(){
  13. EventLoopGroup bossGroup = new NioEventLoopGroup();
  14. EventLoopGroup workGroup = new NioEventLoopGroup();
  15. try {
  16. ServerBootstrap b = new ServerBootstrap();
  17. b.group(bossGroup, workGroup);
  18. b.channel(NioServerSocketChannel.class);
  19. b.childHandler(new ChildChannelHandler());
  20. System.out.println("服务端开启等待客户端连接 ... ...");
  21. Channel ch = b.bind(7397).sync().channel();
  22. ch.closeFuture().sync();
  23. } catch (Exception e) {
  24. e.printStackTrace();
  25. }finally{
  26. bossGroup.shutdownGracefully();
  27. workGroup.shutdownGracefully();
  28. }
  29. }
  30. }



客户端:




  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  2. <html xmlns="http://www.w3.org/1999/xhtml">
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>无标题文档</title>
  6. </head>
  7. </head>
  8. <script type="text/javascript">
  9. var socket;
  10. if(!window.WebSocket){
  11.  
  12. window.WebSocket = window.MozWebSocket;
  13. }
  14.  
  15. if(window.WebSocket){
  16. socket = new WebSocket("ws://localhost:7397/websocket");
  17. socket.onmessage = function(event){
  18.  
  19. var ta = document.getElementById('responseText');
  20. ta.value += event.data+"\r\n";
  21. };
  22.  
  23. socket.onopen = function(event){
  24.  
  25. var ta = document.getElementById('responseText');
  26. ta.value = "打开WebSoket 服务正常,浏览器支持WebSoket!"+"\r\n";
  27. };
  28.  
  29. socket.onclose = function(event){
  30.  
  31. var ta = document.getElementById('responseText');
  32. ta.value = "";
  33. ta.value = "WebSocket 关闭"+"\r\n";
  34. };
  35. }else{
  36. alert("您的浏览器不支持WebSocket协议!");
  37. }
  38.  
  39. function send(message){
  40. if(!window.WebSocket){return;}
  41. if(socket.readyState == WebSocket.OPEN){
  42. socket.send(message);
  43. }else{
  44. alert("WebSocket 连接没有建立成功!");
  45. }
  46. }
  47. </script>
  48. <body>
  49. <form onSubmit="return false;">
  50. <input type = "text" name="message" value="Netty The Sinper"/>
  51. <br/><br/>
  52. <input type="button" value="发送 WebSocket 请求消息" onClick="send(this.form.message.value)"/>
  53. <hr color="blue"/>
  54. <h3>服务端返回的应答消息</h3>
  55. <textarea id="responseText" style="width: 1024px;height: 300px;"></textarea>
  56. </form>
  57. </body>
  58. </html>



测试运行:

1、启动NettyServer

2、启动支持websocket的浏览器,打开index.html【开启2个以上】

3、服务端输出结果:

服务端开启等待客户端连接 ... ...
客户端与服务端连接开启
客户端与服务端连接开启
客户端与服务端连接关闭
客户端与服务端连接开启
服务端收到:Netty The Sinper
服务端收到:Netty The Sinper
服务端收到:www.itstack.org
服务端收到:www.itstack.org
服务端收到:您好,您也在学习Netty吗?
服务端收到:是的!一起学习吧!
服务端收到:嗯,到群里来吧5360392
服务端收到:好的,么么大

4、客户端输出截图:

阅读更多
文章标签: Netty Websocket
个人分类: Mina/netty
上一篇MyBatis传入参数为集合 list 数组 map写法
下一篇netty的一些杂谈
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭