11.netty开发webSock协议

来源:互联网 发布:yum 不支持python 2.7 编辑:程序博客网 时间:2024/06/05 19:25

1.介绍

一般情况下网络都是请求/响应模式,在下一次点击发送请求之前什么都不会发生。后来出现的ajax是浏览器定期轮询捕捉用户在页面的操作并发送请求,这也是基于客户端浏览器实现的。

另一种情况,当服务器发现有新数据可用时通过推送的方式主动发送给客户端,而不是客户端主动发送请求。基于这种情况可以使用长轮询(客户端打开指定http连接,知道收到服务器的响应)

上面两种情况都有一个缺点:由于http协议的开销,导致他们都不能应用于低延迟的应用。所以h5定义的webSocket将socket引入server和client两端,通过socket建立持久的连接并且双方可以主动发送数据,而不是请求响应模式。


http协议弊端:半双工协议(同一时刻只能由一方发送向另一方,不能双方同时发送)、http消息冗长繁琐(消息头、消息体、换行符等等)、针对服务器推送的黑客攻击(长轮询)

webSocket特点:全双工、对代理和防火墙和路由器透明、无头部信息无cookie无身份验证、无安全开销、pingpong保持链路激活、双方可以主动发送请求


发起webSocket请求的过程:客户端必须先发送一个http请求,这个请求和普通的http请求不同,主要是请求头中附加了信息:"Upgrade:WebSocke"表明是一个升级的http请求。server端生成应答信息发回给client这样就建立起一个WebSocket连接。之后双方可以互相发送数据


2.netty实现

handler

接收到客户端请求后第一次通过http响应,响应成功双方建立起webSocket链路

client通过这个链路生成webSocket对象来发送请求

server将信息加上系统时间返回

package com.tyf.netty;import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;import static io.netty.handler.codec.http.HttpHeaders.setContentLength;import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;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.DefaultFullHttpResponse;import io.netty.handler.codec.http.FullHttpRequest;import io.netty.handler.codec.http.FullHttpResponse;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;import java.util.logging.Level;import java.util.logging.Logger;public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {    private static final Logger logger = Logger.getLogger(WebSocketServerHandler.class.getName());    private WebSocketServerHandshaker handshaker;    @Override    public void messageReceived(ChannelHandlerContext ctx, Object msg)throws Exception {    // 第一次握手有http请求处理if (msg instanceof FullHttpRequest) {    handleHttpRequest(ctx, (FullHttpRequest) msg);}// WebSocket链路建立成功之后处理WebSocket请求else if (msg instanceof WebSocketFrame) {    handleWebSocketFrame(ctx, (WebSocketFrame) msg);}    }    @Override    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {ctx.flush();    }    //http协议处理第一次握手    private void handleHttpRequest(ChannelHandlerContext ctx,FullHttpRequest req) throws Exception {// 如果HTTP解码失败,返回HHTP异常if (!req.getDecoderResult().isSuccess()|| (!"websocket".equals(req.headers().get("Upgrade")))) {    sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1,BAD_REQUEST));    return;}// 构造握手响应返回,本机测试。返回成功之后建立webSocket链路// 客户端通过ws://localhost:8080/websocket这个地址来生成websocket对象WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://localhost:8080/websocket", null, false);handshaker = wsFactory.newHandshaker(req);if (handshaker == null) {    WebSocketServerHandshakerFactory    .sendUnsupportedWebSocketVersionResponse(ctx.channel());} else {    handshaker.handshake(ctx.channel(), req);}    }    //处理双方的webSocket请求    private void handleWebSocketFrame(ChannelHandlerContext ctx,WebSocketFrame frame) {// 判断是否是关闭链路的指令if (frame instanceof CloseWebSocketFrame) {    handshaker.close(ctx.channel(),    (CloseWebSocketFrame) frame.retain());    return;}// 判断是否是Ping消息if (frame instanceof PingWebSocketFrame) {    ctx.channel().write(    new PongWebSocketFrame(frame.content().retain()));    return;}// 本例程仅支持文本消息,不支持二进制消息if (!(frame instanceof TextWebSocketFrame)) {    throw new UnsupportedOperationException(String.format(    "%s frame types not supported", frame.getClass().getName()));}// 返回应答消息String request = ((TextWebSocketFrame) frame).text();if (logger.isLoggable(Level.FINE)) {    logger.fine(String.format("%s received %s", ctx.channel(), request));}ctx.channel().write(new TextWebSocketFrame(request+ " , 欢迎使用Netty WebSocket服务,现在时刻:"+ new java.util.Date().toString()));    }    private static void sendHttpResponse(ChannelHandlerContext ctx,    FullHttpRequest req, FullHttpResponse res) {// 返回应答给客户端if (res.getStatus().code() != 200) {    ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(),    CharsetUtil.UTF_8);    res.content().writeBytes(buf);    buf.release();    setContentLength(res, res.content().readableBytes());}// 如果是非Keep-Alive,关闭连接ChannelFuture f = ctx.channel().writeAndFlush(res);if (!isKeepAlive(req) || res.getStatus().code() != 200) {    f.addListener(ChannelFutureListener.CLOSE);}    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();    }}


server

package com.tyf.netty;import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.Channel;import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.SocketChannel;import io.netty.channel.socket.nio.NioServerSocketChannel;import io.netty.handler.codec.http.HttpObjectAggregator;import io.netty.handler.codec.http.HttpServerCodec;import io.netty.handler.stream.ChunkedWriteHandler;public class WebSocketServer {    public void run(int port) throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup();EventLoopGroup workerGroup = new NioEventLoopGroup();try {    ServerBootstrap b = new ServerBootstrap();    b.group(bossGroup, workerGroup)    .channel(NioServerSocketChannel.class)    .childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch)throws Exception {    ChannelPipeline pipeline = ch.pipeline();    pipeline.addLast("http-codec",new HttpServerCodec());    pipeline.addLast("aggregator",new HttpObjectAggregator(65536));    ch.pipeline().addLast("http-chunked",new ChunkedWriteHandler());    pipeline.addLast("handler",new WebSocketServerHandler());}    });    //绑定本地8080端口    Channel ch = b.bind(port).sync().channel();    //future开启同步    ch.closeFuture().sync();} finally {    bossGroup.shutdownGracefully();    workerGroup.shutdownGracefully();}    }    public static void main(String[] args) throws Exception {int port = 8080;if (args.length > 0) {    try {port = Integer.parseInt(args[0]);    } catch (NumberFormatException e) {e.printStackTrace();    }}new WebSocketServer().run(port);    }}


client(客户端使用html和js来发送请求)

<!DOCTYPE html><html><head><meta charset="UTF-8">Netty WebSocket 时间服务器</head><br><body><br><script type="text/javascript">var socket;if (!window.WebSocket) {window.WebSocket = window.MozWebSocket;}if (window.WebSocket) {socket = new WebSocket("ws://localhost:8080/websocket");socket.onmessage = function(event) {var ta = document.getElementById('responseText');ta.value="";ta.value = event.data};socket.onopen = function(event) {var ta = document.getElementById('responseText');ta.value = "打开WebSocket服务正常,浏览器支持WebSocket!";};socket.onclose = function(event) {var ta = document.getElementById('responseText');ta.value = "";ta.value = "WebSocket 关闭!"; };}else{alert("抱歉,您的浏览器不支持WebSocket协议!");}function send(message) {if (!window.WebSocket) { return; }if (socket.readyState == WebSocket.OPEN) {socket.send(message);}else{  alert("WebSocket连接没有建立成功!");}}</script><form onsubmit="return false;"><input type="text" name="message" value="Netty最佳实践"/><br><br><input type="button" value="发送WebSocket请求消息" onclick="send(this.form.message.value)"/><hr color="blue"/><h3>服务端返回的应答消息</h3><textarea id="responseText" style="width:500px;height:300px;"></textarea></form></body></html>

3.测试

直接运行server和client,使用客户端发送消息给server:



阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 如何淡化结痂后的红印 尸鬼封尽的结印 多重影分身之术结印 点面结合 结合律 劳逸结合 结合 结合胆红素 点面结合作文 两个人结合处泛着白沫 点面结合什么意思 点面结合好处 劳逸结合意思 劳逸结合什么意思 点面结合作文500字 结合处一进一出 托着腰结合鲤鱼乡h 结合雌激素片 早上醒来结合处在一起 点面结合句子 用点面结合写一段话 运动会作文点面结合 结城明日奈 结城乃乃 结城美纱 结城花梨 结城 结城美沙 结城乃乃ipx154在线播放 结城夏野 结城友奈 结城恋 结城友奈是勇者 韩雅凛结婚 结婚纪念日 结婚后 结婚晚上 的结婚 结婚条件 什么是结婚 想结婚