一:常用实现方法
实时通信主要的实现方法
- Ajax 轮训
- long pull
- WebSocket
二:服务端实现
1.WebSocketServer
package com.south.websocket; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * 描述: 启动类 **/ public class WebSocketServer { public static void main(String[] args) throws InterruptedException { //创建主从线程池 EventLoopGroup mainGroup=new NioEventLoopGroup(); EventLoopGroup subGroup=new NioEventLoopGroup(); try{ //创建服务器类 ServerBootstrap server=new ServerBootstrap(); server.group(mainGroup,subGroup) .channel(NioServerSocketChannel.class) .childHandler(new WSServerInitialzer()); ChannelFuture future = server.bind(8888).sync(); future.channel().closeFuture().sync(); }finally { mainGroup.shutdownGracefully(); subGroup.shutdownGracefully(); } } }
2.WSServerInitialzer
package com.south.websocket; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.stream.ChunkedWriteHandler; /** * 描述: 助手初始化类 **/ public class WSServerInitialzer extends ChannelInitializer<SocketChannel> { protected void initChannel(SocketChannel channel) throws Exception { //获取管道 ChannelPipeline pipeline = channel.pipeline(); //websocket 基于http协议,所需要的http 编解码器 pipeline.addLast(new HttpServerCodec()); //在http上有一些数据流产生提供支持 pipeline.addLast(new ChunkedWriteHandler()); //对httpMessage 进行聚合处理,聚合成request或response pipeline.addLast(new HttpObjectAggregator(1024*64)); /** * 处理一些繁重复杂的事情 * 处理握手动作:handshaking(close,ping,pong) ping+pong=心跳 * 对于websocket来讲,都是以frams进行传输的,不同的数据类型对应的frams也不同 */ pipeline.addLast(new WebSocketServerProtocolHandler("/websocket")); //自定义handler pipeline.addLast(new ChatHandler()); } }
3.ChatHandler
package com.south.websocket; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.util.concurrent.GlobalEventExecutor; import java.time.LocalDateTime; /** * 描述: 用于处理消息的handler * 用于它的传输数据的载体是frame,这个frame 在netty中,是用于为websocket专门处理文本对象的,frames是消息载体,(TextWebSocketFrame) **/ public class ChatHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { //记录和管理所有客户端的channel private static ChannelGroup clients=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { //获取客户端所传输的消息 String content = msg.text(); System.out.println("接收到的数据:"+content); //将数据刷新到客户端上 clients.writeAndFlush( new TextWebSocketFrame( "[服务器在:]"+ LocalDateTime.now() +"接收到消息,消息内容为:"+content ) ); } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { clients.add(ctx.channel()); } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { //clients.remove(ctx.channel()); System.out.println("客户端断开,channel对应的长ID为:"+ctx.channel().id().asLongText()); System.out.println("客户端断开,channel对应的短ID为:"+ctx.channel().id().asShortText()); } }
三:前端实现
1.WebSocket Api
初始化对象
var aWebSocket = new WebSocket(url [, protocols]);
生命周期
//只发出一次 aWebSocket.onopen = function(event) { console.log("WebSocket is open now."); }; //只要二者建立连接,服务端向客户端推送消息,就会发出此函数 aWebSocket.onmessage = function(event) { console.debug("WebSocket message received:", event); }; //监听器将在 WebSocket 连接的readyState 变为 CLOSED时被调用 WebSocket.onclose = function(event) { console.log("WebSocket is closed now."); }; //可以定义一个发生错误时执行的回调函数 WebSocket.onerror = function(event) { console.error("WebSocket error observed:", event); };
主动方法
//将需要通过 WebSocket 链接传输至服务器的数据排入队列 WebSocket.send("Hello server!"); //关闭 WebSocket 连接或连接尝试(如果有的话)。 如果连接已经关闭,则此方法不执行任何操作。 WebSocket.close();
2.具体实现
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Netty 实时通讯</title> </head> <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script> var socket; function openSocket() { if(typeof(WebSocket) == "undefined") { console.log("您的浏览器不支持WebSocket"); }else{ console.log("您的浏览器支持WebSocket"); //实现化WebSocket对象,指定要连接的服务器地址与端口 建立连接 //等同于socket = new WebSocket("ws://localhost:8888/websocket"); var socketUrl="http://localhost:8888/websocket"; socketUrl=socketUrl.replace("https","ws").replace("http","ws"); console.log(socketUrl) socket = new WebSocket(socketUrl); //打开事件 socket.onopen = function() { console.log("websocket已打开"); //socket.send("这是来自客户端的消息" + location.href + new Date()); }; //获得消息事件 socket.onmessage = function(msg) { console.log("接收消息"+msg.data); var receiveMsg=$("#receiveMsg"); var html=receiveMsg.html(); receiveMsg.html(html+"<br/>"+msg.data); }; //关闭事件 socket.onclose = function() { console.log("websocket已关闭"); }; //发生了错误事件 socket.onerror = function() { console.log("websocket发生了错误"); } } } function sendMessage() { if(typeof(WebSocket) == "undefined") { console.log("您的浏览器不支持WebSocket"); }else { console.log("您的浏览器支持WebSocket"); var msgContent=$("#msgContent").val(); socket.send(msgContent); } } $(function(){ openSocket(); }) </script> <body> 发送消息: <input type="text" id="msgContent"> <input type="button" value="发送消息" onclick="sendMessage()"> <hr> 接收消息: <div id="receiveMsg" ></div> </body> </html>
三:效果图展示
欢迎大家的支持和留言,代码已上传到我的开源项目,方便大家下载;