Netty-WebSocket长连接推送服务

转自:http://blog.csdn.net/sinosoft_fesco_12138/article/details/50380256


November 25, 2015


推送服务 推送服务

几种消息推送技术比较
AJAX轮询 轮询:缺点,糟糕的用户体验;对服务器压力很大,并造成带宽的极大浪费。
Comet:长连接机制,同样由浏览器端主动发起,但Server端以一种很慢的方式给出回应,优点是实时性好,缺点,长时间占用链接,丧失了无状态高并发的缺 点
Flash XML Socket,需客户端安装java虚拟机,Chrome浏览器新版本已经默认屏蔽Flash
DWR开源框架,将java代码转为js文件,引入js文件路径不能更改,原理也是轮询,
WebSocket,HTML5定义,不支持html5的可以使用socket.io,高效节能,简单易用,配合netty可以达到百万级的链接推送。
NIO,java1.4版本,非阻塞IO

netty,基于原生NIO实现的高并发框架,配合websocket实现消息推送,netty会单独开一个websocket端口处理请求,并不会占用中间件的连接数,而且一 个线程可以处理几万个链接 


NIO是什么 


NIO 是New IO 的简称,在jdk1.4 里提供的新api 。Sun 官方标榜的特性如下:
为所有的原始类型提供 为所有的原始类型提供(Buffer)缓存支持 缓存支持。字符集编码解码解决方案。
Channel :一个新的原始I/O 抽象。 支持锁和内存映射文件的文件访问接口。 提供多路(non-bloking) 非阻塞式的高伸缩性网络I/O 。
Channel 通道、Buffer 缓冲区、Selector 选择器
其中Channel对应以前的流 对应以前的流,Buffer不是什么新东西,Selector是因为nio可以使用异步的非堵塞模式才加入的东西。以前的流总是堵塞的,一个线程只要 对它进行操作,其它操作就会被堵塞,也就相当于水管没有阀门,你伸手接水的时候,不管水到了没有,你就都只能耗在接水(流)上。nio的Channel的加 入,相当于增加了水龙头(有阀门),虽然一个时刻也只能接一个水管的水,但依赖轮换策略,在水量不大的时候,各个水管里流出来的水,都可以得到妥善接 纳,这个关键之处就是增加了一个接水工,也就是Selector,他负责协调,也就是看哪根水管有水了的话,在当前水管的水接到一定程度的时候,就切换一 下:临时关上当前水龙头,试着打开另一个水龙头(看看有没有水)。当其他人需要用水的时候,不是直接去接水,而是事前提了一个水桶给接水工,这个水桶 就是Buffer。也就是,其他人虽然也可能要等,但不会在现场等,而是回家等,可以做其它事去,水接满了,接水工会通知他们 
Java NIO 实现百万连接( 这 段 代 码 只 会 接 受 连 过 来 的 连 接 , 不 做 任 何 操 作 ) 


[java]  view plain  copy
  1. ServerSocketChannel ssc = ServerSocketChannel.open();  
  2. Selector sel = Selector.open();   
  3. ssc.configureBlocking(false);  
  4. ssc.socket().bind(new InetSocketAddress(8080));  
  5. SelectionKey key = ssc.register(sel, SelectionKey.OP_ACCEPT);   
  6. while(true) {  
  7.     sel.select();  
  8.     Iterator it = sel.selectedKeys().iterator();  
  9.     while(it.hasNext()) {  
  10.         SelectionKey skey = (SelectionKey)it.next();  
  11.         it.remove();  
  12.         if(skey.isAcceptable()) {  
  13.             ch = ssc.accept();  
  14.         }  
  15.     }  
  16. }  

 Netty是什么


Netty: http://netty.io/ Netty is an asynchronous event-driven network application framework for rapid development of maintainable high performance protocol servers & clients 


官方解释已经够清楚的了,其中最吸引人的就是高性能 高性能了。
 1,普通的服务器10000个连接需要10000个线程,服务器可能就直接卡住了,但对于netty服务器可能几个线程就够了
2,netty是一套在 是一套在java NIO的基础上封装的便于用户开发网络应用程序的 的基础上封装的便于用户开发网络应用程序的api. 应用场景很多,诸如阿里的消息队列(RocketMQ),分布式rpc(Dubbo)通 信层都使用到了netty(dubbo可以用服务发现自由选择通信层)
3,netty是非阻塞事件驱动框架 是非阻塞事件驱动框架, 并结合线程组(group)的概念,可以很好的支持高并发,慢连接的场景
4,编程接口非常容易,并且也较好的解决了TCP粘包/拆包的问题.netty提供了自己的ByteBuf和channel,相比于jdk的ByteBuffer和channel来说更简便灵 活操作, 并提供了pipeline的概念,并针对每个contextHandler都可以由用户定义, 方便直接.
5,有一些web框架已经开始直接使用netty做为底层通信服务,诸如play. 这样play就不用依赖于容器去进行部署,在没有nginx做反向代理的情况下也能支持 高并发.编解码器可以随意扩展,今天是个web,明天就可以是一个ftp或email服务器,个人觉得比较灵活。
6,相对于Tomcat这种Web Server(顾名思义主要是提供Web协议相关的服务的),Netty是一个 是一个Network Server,是处于Web Server更下层的网络框 架,也就是说你可以使用Netty模仿Tomcat做一个提供HTTP服务的Web容器。简而言之,Netty通过使用NIO的很多新特性,对TCP/UDP编程进行了简化和封 装,提供了更容易使用的网络编程接口,让你可以根据自己的需要封装独特的HTTP Server活着FTP Server等.
Netty更多的链接 更多的链接
非阻塞IO
其实无论是用Java NIO 还是用Netty,达到百万链接都没有任何难度,因为他们都是非阻塞的IO,不需要为每个链接创建一个线程了。
预知详情,可以搜索一下BIO,NIO,AIO的相关知识。

Netty 实现百万连接( 这 段 代 码 只 会 接 受 连 过 来 的 连 接 ) 


[java]  view plain  copy
  1. NioEventLoopGroup bossGroup =  new NioEventLoopGroup();  
  2. NioEventLoopGroup workerGroup= new NioEventLoopGroup();  
  3. ServerBootstrap bootstrap = new ServerBootstrap();  
  4. bootstrap.group(bossGroup, workerGroup);   
  5. bootstrap.channel( NioServerSocketChannel.class);   
  6. bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {  
  7.     @Override protected void initChannel(SocketChannel ch) throws Exception {  
  8.         ChannelPipeline pipeline = ch.pipeline();  
  9.         //todo: add handler  
  10.     }});  
  11. bootstrap.bind(8080).sync();   


WebSocket是什么


WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)
首先,Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。简单的举个例子吧,用目前应用比较广泛的PHP生命周期来解释。
1) HTTP的生命周期通过Request来界定,也就是一个Request 一个Response,那么在HTTP1.0中,这次HTTP请求就结束了。在HTTP1.1中进行了改进,使 得有一个keep-alive,也就是说,在一个HTTP连接中,可以发送多个Request,接收多个Response。但是请记住 Request = Response , 在HTTP中永 远是这样,也就是说一个request只能有一个response。而且这个response也是被动的,不能主动发起。

Websocket的作用 在讲Websocket之前,我就顺带着讲下 long poll 和 ajax轮询 的原理。 

首先是 ajax轮询 ,ajax轮询 的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。 


场景再现: 客户端:啦啦啦,有没有新信息(Request) 

服务端:没有(Response) 

客户端:啦啦啦,有没有新信息(Request) 

服务端:没有。。(Response) 

客户端:啦啦啦,有没有新信息(Request) 

服务端:你好烦啊,没有啊。。(Response)

 客户端:啦啦啦,有没有新消息(Request) 

服务端:好啦好啦,有啦给你。(Response) 

客户端:啦啦啦,有没有新消息(Request) 

服务端:。。。。。没。。。。没。。。没有(Response) ---- loop


long poll long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接 后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。 

场景再现 客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) 

服务端:额。。 等待到有消息的时候。。来 给你(Response) 

客户端:啦啦啦,有没有新信息,没有的话就等有了才返回给我吧(Request) -loop


从上面可以看出其实这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性。 何为被动性呢,其实就是,服务端不能主动联系客户端,只能有客户端发起。 简单地说就是,服务器是一个很懒的冰箱(这是个梗)(不会、不能主动发起连接),但是上司有命令,如果有客户来,不管多么累都要好好接待。


说完这个,我们再来说一说上面的缺陷 从上面很容易看出来,不管怎么样,上面这两种都是非常消耗资源的。 

ajax轮询 需要服务器有很快的处理速度和资源。(速度) long poll 需要有很高的并发,也就是说同时接待客户的能力。(场地大小) 所以ajax轮询 和long poll 都有可能发生这种情况。

客户端:啦啦啦啦,有新信息么? 

客户端:啦啦啦啦,有新信息么? 

服务端:月线正忙,请稍后再试( 服务端:月线正忙,请稍后再试(503 Server Unavailable) ) 

客户端:。。。。好吧,啦啦啦,有新信息么? 

客户端:。。。。好吧,啦啦啦,有新信息么? 

服务端:月线正忙,请稍后再试( 服务端:月线正忙,请稍后再试(503 Server Unavailable) )


通过上面这个例子,我们可以看出,这两种方式都不是最好的方式,需要很多资源。 一种需要更快的速度,一种需要更多的'电话'。这两种都会导致'电话'的需求越来越高。 哦对了,忘记说了HTTP还是一个状态协议。 通俗的说就是,服务器因为每天要接待太多客户了,是个健忘鬼 健忘鬼,你一挂电话,他就把你的东西全忘光了,把你的东西全丢掉了。你第二次还得再告诉服务 器一遍。


所以在这种情况下出现了,Websocket出现了。 他解决了HTTP的这几个难题。 首先,被动性 被动性,当服务器完成协议升级后(HTTP->Websocket),服务端就可以主动推送信息给客户端啦。 所以上面的情景可以做如下修改。 

客户端:啦啦啦,我要建立Websocket协议,需要的服务:chat,Websocket协议版本:17(HTTP Request) 

服务端:ok,确认,已升级为Websocket协议(HTTP Protocols Switched) 

客户端:麻烦你有信息的时候推送给我噢。。 

服务端:ok,有的时候会告诉你的。 

服务端:balabalabalabala 

服务端:balabalabalabala 

服务端:哈哈哈哈哈啊哈哈哈哈 

服务端:笑死我了哈哈哈哈哈哈哈 

浏览器打开 浏览器打开websocket代码 代码


[javascript]  view plain  copy
  1. var socket;  
  2. if(!window.WebSocket){  
  3.   window.WebSocket = window.MozWebSocket;  
  4. }   
  5. if(window.WebSocket){  
  6.   socket = new WebSocket("ws://10.0.53.219:7397/websocket");  
  7.   socket.onmessage = function(event){  
  8.                    var ta = document.getElementById('responseText');  
  9.                    ta.value += event.data+"\r\n";  
  10.   };  
  11.   socket.onopen = function(event){  
  12.                    var ta = document.getElementById('responseText');  
  13.                    ta.value = "这里显示服务器推送信息"+"\r\n";  
  14.                     
  15.   };  
  16. socket.onclose = function(event){  
  17.                    var ta = document.getElementById('responseText');  
  18.                    ta.value = "";  
  19.                    ta.value = "WebSocket 关闭"+"\r\n";  
  20.   };  
  21. }else{  
  22.          alert("您的浏览器不支持WebSocket协议!");  
  23. }   


不支持websocket的浏览器处理方法

点击打开链接 https://github.com/gimite/web-socket-js/

复制 swfobject.js, web_socket.js, WebSocketMain.swf到项目中

[javascript]  view plain  copy
  1. <span style="font-size:10px;"><script type="text/javascript" src="swfobject.js"></script>  
  2. <script type="text/javascript" src="web_socket.js"></script>  
  3.   
  4. <script type="text/javascript">  
  5.   
  6.   // Let the library know where WebSocketMain.swf is:  
  7.   WEB_SOCKET_SWF_LOCATION = "WebSocketMain.swf";  
  8.   
  9.   // Write your code in the same way as for native WebSocket:  
  10.   var ws = new WebSocket("ws://example.com:10081/");  
  11.   ws.onopen = function() {  
  12.     ws.send("Hello");  // Sends a message.  
  13.   };  
  14.   ws.onmessage = function(e) {  
  15.     // Receives a message.  
  16.     alert(e.data);  
  17.   };  
  18.   ws.onclose = function() {  
  19.     alert("closed");  
  20.   };  
  21.   
  22. </script></span>  




附录: 附录:netty+websocket即时聊天代码 即时聊天代码
Global.java
[java]  view plain  copy
  1. package com.fesco.netty.websocket.common;   
  2. import io.netty.channel.group.ChannelGroup;  
  3. import io.netty.channel.group.DefaultChannelGroup;  
  4. import io.netty.util.concurrent.GlobalEventExecutor;  
  5. public final class Global {  
  6.           
  7.          public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);  
  8.           
  9. }   
  10. ChildChannelHandler.java   
  11. package com.fesco.netty.websocket;   
  12. import io.netty.channel.ChannelInitializer;  
  13. import io.netty.channel.socket.SocketChannel;  
  14. import io.netty.handler.codec.http.HttpObjectAggregator;  
  15. import io.netty.handler.codec.http.HttpServerCodec;  
  16. import io.netty.handler.stream.ChunkedWriteHandler;   
  17. public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {   
  18.          @Override  
  19.          protected void initChannel(SocketChannel ch) throws Exception {  
  20.                    ch.pipeline().addLast("http-codec"new HttpServerCodec());  
  21.                    ch.pipeline().addLast("aggregator",new HttpObjectAggregator(65535));  
  22.                    ch.pipeline().addLast("http-chunked",new ChunkedWriteHandler());  
  23.                    ch.pipeline().addLast("handler",new MyWebSocketServerHandler());  
  24.          }   
  25. }     
  26. MyWebSocketServerHandler.java   
  27. package com.fesco.netty.websocket;   
  28. import io.netty.buffer.ByteBuf;  
  29. import io.netty.buffer.Unpooled;  
  30. import io.netty.channel.ChannelFuture;  
  31. import io.netty.channel.ChannelFutureListener;  
  32. import io.netty.channel.ChannelHandlerContext;  
  33. import io.netty.channel.SimpleChannelInboundHandler;  
  34. import io.netty.handler.codec.http.DefaultFullHttpResponse;  
  35. import io.netty.handler.codec.http.FullHttpRequest;  
  36. import io.netty.handler.codec.http.HttpResponseStatus;  
  37. import io.netty.handler.codec.http.HttpVersion;  
  38. import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;  
  39. import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;  
  40. import io.netty.handler.codec.http.websocketx.PongWebSocketFrame;  
  41. import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;  
  42. import io.netty.handler.codec.http.websocketx.WebSocketFrame;  
  43. import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;  
  44. import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;  
  45. import io.netty.util.CharsetUtil;   
  46. import java.util.Calendar;  
  47. import java.util.GregorianCalendar;  
  48. import java.util.logging.Level;  
  49. import java.util.logging.Logger;   
  50. import com.fesco.netty.websocket.common.Global;   
  51. public class MyWebSocketServerHandler extends SimpleChannelInboundHandler<Object>{   
  52.          private static final Logger logger = Logger.getLogger(WebSocketServerHandshaker.class.getName());  
  53.          private WebSocketServerHandshaker handshaker;  
  54.           
  55.           
  56.          @Override  
  57.          public void channelActive(ChannelHandlerContext ctx) throws Exception {  
  58.                    Global.group.add(ctx.channel());  
  59.                    System.out.println("客户端与服务器段开启");  
  60.          }   
  61.          @Override  
  62.          public void channelInactive(ChannelHandlerContext ctx) throws Exception {  
  63.                    Global.group.remove(ctx.channel());  
  64.                    System.out.println("客户端与服务器链接关闭!");  
  65.          }   
  66.          @Override  
  67.          protected void messageReceived(ChannelHandlerContext ctx, Object msg)  
  68.                             throws Exception {  
  69.                    if(msg instanceof FullHttpRequest){  
  70.                             handleHttpRequest(ctx, ((FullHttpRequest) msg));  
  71.                    }else if(msg instanceof WebSocketFrame){  
  72.                             handlerWebSocketFrame(ctx, (WebSocketFrame) msg);  
  73.                    }  
  74.          }   
  75.          @Override  
  76.          public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {  
  77.                    ctx.flush();  
  78.          }   
  79.          private void handlerWebSocketFrame(ChannelHandlerContext ctx,  
  80.                             WebSocketFrame frame) {  
  81.                     
  82.                    if(frame instanceof CloseWebSocketFrame){  
  83.                             handshaker.close(ctx.channel(), (CloseWebSocketFrame)frame.retain());  
  84.                    }  
  85.                    if(frame instanceof PingWebSocketFrame){  
  86.                             ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));  
  87.                    }  
  88.                     
  89.                    if(!(frame instanceof TextWebSocketFrame)){  
  90.                             System.err.println("本例仅支持文本!");  
  91.                             throw new UnsupportedOperationException(String.format(  
  92.                                                "%s frame types not supported", frame.getClass().getName()));  
  93.                    }  
  94.                     
  95.                    String request = ((TextWebSocketFrame)frame).text();  
  96.                    System.out.println("服务器收到:" + request);  
  97.                    if (logger.isLoggable(Level.FINE)) {  
  98.                             logger.fine(String.format("%s received %s", ctx.channel(),request));  
  99.                    }  
  100.                     
  101.                     
  102.                    TextWebSocketFrame tws = new TextWebSocketFrame(getDateTime()  
  103.                                      + "(" +ctx.channel().remoteAddress() + ") :" + request);  
  104.                    // 群发  
  105.                    Global.group.writeAndFlush(tws);  
  106.                    // 返回【谁发的发给谁】  
  107.                    // ctx.channel().writeAndFlush(tws);  
  108.          }   
  109.          private void handleHttpRequest(ChannelHandlerContext ctx,FullHttpRequest req) {  
  110.                     
  111.                    if(!req.getDecoderResult().isSuccess() ||  
  112.                                      !("websocket".equals(req.headers().get("Upgrade")))){  
  113.                             sendHttpResponse(ctx, req, new DefaultFullHttpResponse(  
  114.                                                HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));  
  115.                             return;  
  116.                    }  
  117.                     
  118.                    WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://localhost:7397/websocket"nullfalse);  
  119.                    handshaker = wsFactory.newHandshaker(req);  
  120.                    if(null == handshaker){  
  121.                             WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());  
  122.                    }else{  
  123.                             handshaker.handshake(ctx.channel(), req);  
  124.                    }  
  125.                     
  126.          }   
  127.          private void sendHttpResponse(ChannelHandlerContext ctx,  
  128.                             FullHttpRequest req, DefaultFullHttpResponse res) {  
  129.                    if(res.getStatus().code() != 200){  
  130.                             ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(),  
  131.                                                CharsetUtil.UTF_8);  
  132.                             res.content().writeBytes(buf);  
  133.                             buf.release();  
  134.                    }  
  135.                     
  136.                    ChannelFuture future = ctx.channel().writeAndFlush(res);  
  137.                    if (!isKeepAlive(req) || res.getStatus().code() != 200) {  
  138.                             future.addListener(ChannelFutureListener.CLOSE);  
  139.                    }  
  140.                     
  141.          }  
  142.           
  143.          private static boolean isKeepAlive(FullHttpRequest req){  
  144.                    return false;  
  145.          }  
  146.           
  147.          @Override  
  148.          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)  
  149.                             throws Exception {  
  150.                    cause.printStackTrace();  
  151.                    ctx.close();  
  152.          }    
  153.          private String getDateTime() {  
  154.                    // Calendar calendar = Calendar.getInstance();  
  155.                    Calendar calendar = new GregorianCalendar();  
  156.                    java.util.Date date = new java.util.Date();  
  157.                    calendar.setTime(date);  
  158.                    String sHour = null;  
  159.                    String sMinute = null;  
  160.                    String sSecond = null;  
  161.                    String sYear = null;  
  162.                    String sMonth = null;  
  163.                    String sDay = null;  
  164.                    int year = calendar.get(Calendar.YEAR);  
  165.                    int month = calendar.get(Calendar.MONTH) + 1;  
  166.                    int day = calendar.get(Calendar.DATE);  
  167.                    int hour = calendar.get(Calendar.HOUR_OF_DAY);  
  168.                    int minute = calendar.get(Calendar.MINUTE);  
  169.                    int second = calendar.get(Calendar.SECOND);  
  170.                    int milliSecond = calendar.get(Calendar.MILLISECOND);   
  171.                    sYear = String.valueOf(year);  
  172.                    if (month < 10) {  
  173.                             sMonth = "0" + month;  
  174.                    } else  
  175.                             sMonth = String.valueOf(month);  
  176.                    if (day < 10) {  
  177.                             sDay = "0" + day;  
  178.                    } else  
  179.                             sDay = String.valueOf(day);   
  180.                    if (hour < 10) {  
  181.                             sHour = "0" + hour;  
  182.                    } else {  
  183.                             sHour = String.valueOf(hour);  
  184.                    }   
  185.                    if (minute < 10) {  
  186.                             sMinute = "0" + minute;  
  187.                    } else {  
  188.                             sMinute = String.valueOf(minute);  
  189.                    }   
  190.                    if (second < 10) {  
  191.                             sSecond = "0" + second;  
  192.                    } else {  
  193.                             sSecond = String.valueOf(second);  
  194.                    }   
  195.                    return sYear + "-" + sMonth + "-" + sDay + " " + sHour + ":" + sMinute + ":" + sSecond;  
  196.          }    
  197. }   


NettyServer.java 
[java]  view plain  copy
  1. package com.fesco.netty.websocket;   
  2. import java.net.InetSocketAddress;   
  3. import io.netty.bootstrap.ServerBootstrap;  
  4. import io.netty.channel.Channel;  
  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 void run(){  
  10.                    EventLoopGroup boosGroup = new NioEventLoopGroup();  
  11.                    EventLoopGroup workGroup = new NioEventLoopGroup();  
  12.                     
  13.                    try {  
  14.                             ServerBootstrap bootstrap = new ServerBootstrap();  
  15.                             bootstrap.group(boosGroup, workGroup);  
  16.                             bootstrap.channel(NioServerSocketChannel.class);  
  17.                             bootstrap.childHandler(new ChildChannelHandler());  
  18.                             System.err.println("服务器开启待客户端链接.....");  
  19.                             Channel ch = bootstrap.bind(new InetSocketAddress("10.0.53.219"7397)).sync().channel();  
  20.                             ch.closeFuture().sync();  
  21.                    } catch (Exception e) {  
  22.                             e.printStackTrace();  
  23.                    }finally{  
  24.                             boosGroup.shutdownGracefully();  
  25.                             workGroup.shutdownGracefully();  
  26.                    }  
  27.          }  
  28.           
  29.          public static void main(String[] args){  
  30.                    new NettyServer().run();  
  31.          }  
  32.           
  33. }   


ClinetHtml.html 
[html]  view plain  copy
  1. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1transitional.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. <script type="text/javascript">  
  7.   var socket;  
  8.   if(!window.WebSocket){  
  9.            window.WebSocket = window.MozWebSocket;  
  10.   }   
  11.   if(window.WebSocket){  
  12.            socket = new WebSocket("ws://10.0.53.219:7397/websocket");  
  13.            socket.onmessage = function(event){  
  14.                             var ta = document.getElementById('responseText');  
  15.                             ta.value += event.data+"\r\n";  
  16.            };  
  17.            socket.onopen = function(event){  
  18.                             var ta = document.getElementById('responseText');  
  19.                             ta.value = "这里显示服务器推送信息"+"\r\n";  
  20.                              
  21.            };   
  22.            socket.onclose = function(event){  
  23.                             var ta = document.getElementById('responseText');  
  24.                             ta.value = "";  
  25.                             ta.value = "WebSocket 关闭"+"\r\n";  
  26.            };  
  27.   }else{  
  28.                    alert("您的浏览器不支持WebSocket协议!");  
  29.   }   
  30.   function send(message){  
  31.          if(!window.WebSocket){return;}  
  32.          if(socket.readyState == WebSocket.OPEN){  
  33.                    socket.send(message);  
  34.          }else{  
  35.                    alert("WebSocket 连接没有建立成功!");  
  36.          }  
  37.           
  38.   }  
  39.             
  40.   </script>  
  41. </head>   
  42. <body>  
  43.          <form onSubmit="return false;">  
  44.                    <input type="text" name="message" value="这里输入消息" /> <br />  
  45.                    <br /> <input type="button" value="发送 WebSocket 请求消息"  
  46.                             onClick="send(this.form.message.value)" />  
  47.                    <hr color="blue" />  
  48.                    <h3>服务端返回的应答消息</h3>  
  49.                    <textarea id="responseText" style="width: 1024px;height: 300px;"></textarea>  
  50.          </form>  
  51. </body>  
  52. </html>  

netty-websocket-spring-boot-starter是一个基于Netty实现的WebSocket框架,可以方便地在Spring Boot应用中集成WebSocket功能。使用该框架可以快速构建实时通信、消息推送等功能。 下面是一个使用netty-websocket-spring-boot-starter的简单案例: 1. 在pom.xml中添加依赖: ```xml <dependency> <groupId>com.github.wujiuye</groupId> <artifactId>netty-websocket-spring-boot-starter</artifactId> <version>1.0.0.RELEASE</version> </dependency> ``` 2. 编写WebSocket处理器 ```java @ServerEndpoint("/websocket/{userId}") @Component public class WebSocketHandler { private static final Logger logger = LoggerFactory.getLogger(WebSocketHandler.class); private Session session; private String userId; @OnOpen public void onOpen(Session session, @PathParam("userId") String userId) { this.session = session; this.userId = userId; logger.info("WebSocket连接建立,userId: {}", userId); } @OnMessage public void onMessage(String message) { logger.info("收到来自客户端的消息:{}", message); sendMessage("服务端已收到消息:" + message); } @OnClose public void onClose() { logger.info("WebSocket连接关闭,userId: {}", userId); } @OnError public void onError(Throwable t) { logger.error("WebSocket连接异常,userId: {}", userId, t); } public void sendMessage(String message) { try { this.session.getBasicRemote().sendText(message); } catch (IOException e) { logger.error("发送WebSocket消息失败,userId: {}", userId, e); } } } ``` 3. 配置WebSocket 在配置类中添加@EnableWebSocket注解,启用WebSocket功能,同时,也可以配置WebSocket的一些参数,例如端口号、路径等。 ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Autowired private WebSocketHandler webSocketHandler; @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(webSocketHandler, "/websocket/{userId}") .setAllowedOrigins("*"); } } ``` 4. 测试WebSocket 使用浏览器或WebSocket客户端连接WebSocket服务端,例如:ws://localhost:8080/websocket/123,其中123为userId。 以上就是一个简单的netty-websocket-spring-boot-starter使用案例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值