Netty学习(一)基于长连接的双攻的通信,通过websocket编程实现
效果图,客户端和服务器端建立起长连接,客户端发送请求,服务器端响应
但是目前缺少心跳,如果两个建立起来的连接,一个断网之后,另外一个是感知不到对方已经断掉的。以后使用心跳技术来进行连接检测
须知:
状态码101,代表 协议转换,从HTTP协议升级为WebSocket协议
HTTP协议,一般访问的时候:是 Http://localhost:8080/ws
WebSocket协议,访问的时候,需要是:ws://localhost:8080/ws
实现代码:
服务器端:
package com.dawa.netty.fifthexample; 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; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import java.net.InetSocketAddress; /** * @Title: MyServer * @Author: 大娃 * @Date: 2019/11/28 14:02 * @Description: */ public class MyServer { public static void main(String[] args) throws Exception{ //定义一个线程组 , 事件循环组 。 异步的NIO , 就是一个死循环。 EventLoopGroup bossGroutp = new NioEventLoopGroup(); //不断的接受连接,但是不处理 EventLoopGroup workerGroup = new NioEventLoopGroup(); // 完成后续处理,把结果返回给客户端 try { //服务端,启动类 ServerBootstrap serverBootstrap = new ServerBootstrap(); //group方法,有两个参数的,有一个参数的 serverBootstrap.group(bossGroutp, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new WebSocketChannelInitializer()); //字处理器,自己 定义的 ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(8899)).sync(); channelFuture.channel().closeFuture().sync(); } finally { //优雅关闭、 bossGroutp.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
package com.dawa.netty.fifthexample; 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.WebSocketServerProtocolHandler; import io.netty.handler.stream.ChunkedWriteHandler; /** * @Title: WebSocketChannelInitializer * @Author: 大娃 * @Date: 2019/11/28 14:06 * @Description: */ public class WebSocketChannelInitializer extends ChannelInitializer{ @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new HttpServerCodec()); pipeline.addLast(new ChunkedWriteHandler());//新的处理器 块 pipeline.addLast(new HttpObjectAggregator(8192)); //将分开的段,给聚合到一起,形成完整的HTTP响应。很重要的一个对象,在处理HTTP的时候 pipeline.addLast(new WebSocketServerProtocolHandler("/ws")); pipeline.addLast(new TextWebSocketFrameHandler()); } }
package com.dawa.netty.fifthexample; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import java.time.LocalDateTime; /** * @Title: TextWebSocketFrameHandler * @Author: 大娃 * @Date: 2019/12/2 08:33 * @Description: */ public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler{ /** * * @param ctx 上下文对象 * @param msg //文本帧对象 * @throws Exception 抛异常 */ @Override protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { System.out.println("收到消息:"+msg.text()); ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间:"+ LocalDateTime.now() )); //不能直接传入 字符串.因为不同协议的规范不同,websocket要求要传回这种的对象 } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { System.out.println("handlerAdded:" + ctx.channel().id().asLongText());//每一个channel 都有一个唯一的id值与其对应 } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { System.out.println("HandlerRemoved:" + ctx.channel().id().asLongText()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.channel().close(); } }
客户端(使用HTML,JS来代替模拟客户端)
1 DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>websocket客户端title> 6 head> 7 <body> 8 11 <script type="text/javascript"> 12 //使用浏览器,进行websocket服务器端的访问 13 var socket; 14 if (window.WebSocket) {//判断浏览器是否支持websocket 15 socket = new WebSocket("ws:localhost:8899/ws"); //建立websocket连接 16 socket.onmessage = function (ev) { //如果socket收到消息,这个onmessage方法就会被回调 17 var ta = document.getElementById('responseText'); 18 ta.value = ta.value + "\n" + ev.data; 19 }; 20 21 socket.onopen=function (ev) {//当连接被打开的时候,执行的回调 22 var ta = document.getElementById('responseText'); 23 ta.value = "连接开启!"; 24 }; 25 26 socket.onclose = function (ev) { 27 var ta = document.getElementById('responseText'); 28 ta.value = ta.value + "\n" + "连接断开!"; 29 }; 30 } else { 31 alert("浏览器不支持Websocket") 32 } 33 34 function send(message) { 35 if (!window.WebSocket) { 36 return; 37 } 38 if (socket.readyState === WebSocket.OPEN) { 39 socket.send(message); 40 } else { 41 alert("连接未开启"); 42 } 43 } 44 script> 45 46 47 <form action="" onsubmit="return false;"> 48 <textarea name="message" style="width: 400px;height: 200px;">textarea> 49 50 <input type="button" value="发送数据" onclick="send(this.form.message.value)"> 51 52 <h3>服务器端输出:h3> 53 <textarea id="responseText" style="width: 400px;height: 300px;">textarea> 54 <input type="button" onclick="javascript:document.getElementById('responseText').value='';" value="清空内容"> 55 form> 56 body> 57 html>