`
liuzhengqiu0127
  • 浏览: 7235 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Netty应用高级篇二

阅读更多

WebSocket协议开发

 

一,背景

 

一直以来,网络在很大程度上都是围绕着HTTP的请求/响应模式而构建的。所有HTTP通信仍然是客户端控制的,需要用户进行互动或定期轮询,从服务端加载新数据。

 

HTTP协议的弊端如下:

(1)HTTP协议为半双工协议。数据在客户端和服务端两个方向上传输,但是不能同时传输。这意味着在同一个时刻,只有一个方向上的数据传送。

(2)HTTP消息冗长而繁琐。HTTP消息包括消息头,消息体,换行符等,通常情况下采用文本方式传输,相比于其他的二进制通信协议,冗长而繁琐。

(3)针对服务器推送的黑客攻击。利用长时间轮询的方式。比较新的一种轮询技术是Comet,使用了Ajax。这种技术虽然可以达到双向通信,但依然需要发出请求,而且在Comet中,普遍采用了长连接,这也会大量消耗服务器带宽和资源。

 

为了解决这些问题,WebSocket将网络套接字引入到了客户端和服务端,浏览器和服务器之间可以通过套接字建立持久的连接,双方随时都可以互发数据给对方,而不是之前由客户端控制的“请求-应答模式”。

 

二,WebSocket协议简介

 

WebSocket是HTML5开始提供的一种浏览器与服务器间进行全双工通信的网络技术,WebSocket通信协议于2011年被IETF定为标准RFC6455,WebSocket API被W3C定为标准。

在WebSocket API中,浏览器和服务器只需要做一个握手的动作,两者就可以直接互相传送数据了。WebSocket基于TCP双向全双工进行消息传递,在同一时刻,既可以发送消息,也可以接收消息,相比于HTTP的半双工协议,性能得到很大的提升。

WebSocket的特点:

1,单一的TCP连接,采用全双工模型通信。

2,对代理,防火墙和路由器透明。

3,无头部信息,Cookie和身份验证。

4,无安全开销。

5,通过"ping/pong"帧保持链路激活。

6,服务器可以主动传递消息给客户端,不再需要客户端轮询。

 

WebSocket设计出来的目的就是取代轮询和Comet技术,使客户端浏览器具备像C/S架构下桌面系统一样实时通信能力。WebSocket连接本质上就是一个TCP连接,所以在数据传输的稳定性和数据传输量的大小方面,和轮询以及Comet技术相比,具有很大的性能优势。

 

WebSocket连接建立过程:

1,客户端或者浏览器发出握手请求,请求消息示例如下:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGHHUkshfKKJHJKKJ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

 这个请求和通常的HTTP请求不同,包含了一些附加头信息,其中附加头信息“Upgrade:WebSocket"表明这是一个申请协议升级的HTTP请求。

2,服务端解析这些附加的头信息,然后生成应答信息返回给客户端,客户端和服务器端的WebSocket连接就建立起来了,双方可以通过这个连接通道自由地传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动关闭连接。

服务端返回给客户端的应答消息如下:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhsfsfskfhsk==
Sec-WebSocket-Protocol: chat

 

WebSocket的生命周期

三次握手成功之后,服务端和客户端就可以通过”messages"的方式进行通信了,一个消息有一个或者多个帧组成,WebSocket的消息并不一定对应一个特定网络层的帧,它可以被分割成多个帧或者被合并。

WebSocket的握手连接关闭消息带有一个状态码和一个可选的关闭原因,它必须按照协议要求发送一个Close控制帧,当对端接收到关闭控制帧指令时,需要主动关闭WebSocket连接。

 

三,基于Netty的开发实例

 

Netty基于HTTP协议栈开发了WebSocket协议栈,利用Netty的WebSocket协议栈可以非常方便地开发出WebSocket客户端和服务端。

 

WebSocket服务端的功能如下:支持WebSocket的浏览器通过WebSocket协议发送请求消息给服务端,服务端对消息进行判断,如果是合法的WebSocket请求,则获取请求消息体,并在后面追加字符串。

 

客户端HTML通过内嵌JS脚本创建WebSocket连接。

 

WebSocket服务端代码如下:

package com.huawei.netty.websocket;

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;

/**
 * Created by liuzhengqiu on 2017/11/13.
 */
public class WebSocketServer
{
    public void run(int port) throws Exception
    {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast("http-codec", new HttpServerCodec())
                                    .addLast("aggregator", new HttpObjectAggregator(65536))
                                    .addLast("http-chunked", new ChunkedWriteHandler())
                                    .addLast("handler", new WebSocketServerHandler());
                        }
                    });
            Channel channel = serverBootstrap.bind(port).sync().channel();
            System.out.println("Web socket server started at port"+port+".");
            System.out.println("Open your browser and navigate to http://localhost:"+port+"/");
            channel.closeFuture().sync();
        }
        finally
        {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new WebSocketServer().run(8080);
    }
}

 

package com.huawei.netty.websocket;

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.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;

import static io.netty.handler.codec.http.HttpHeaders.isKeepAlive;
import static io.netty.handler.codec.http.HttpHeaders.setContentLength;

/**
 * Created by liuzhengqiu on 2017/11/13.
 */
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object>
{
    private WebSocketServerHandshaker handshaker;

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception
    {
        if (msg instanceof FullHttpRequest)
        {
            handleHttpRequest(channelHandlerContext,(FullHttpRequest)msg);
        }

        else if (msg instanceof WebSocketFrame)
        {
            handleWebSocketFrame(channelHandlerContext,(WebSocketFrame)msg);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception
    {
        ctx.flush();
    }

    private void handleWebSocketFrame(ChannelHandlerContext ctx,WebSocketFrame frame)
    {
        if (frame instanceof CloseWebSocketFrame)
        {
            handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }

        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();
        ctx.channel().write(
                new TextWebSocketFrame(request+",欢迎使用Netty WebSocket服务,现在时刻:"
                + new java.util.Date().toString())
        );
    }

    private void handleHttpRequest(ChannelHandlerContext ctx,FullHttpRequest request) throws Exception
    {
        if (!request.getDecoderResult().isSuccess()
                || (!"websocket".equals(request.headers().get("Upgrade"))))
        {
            sendHttpResponse(ctx,request,new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }

        WebSocketServerHandshakerFactory webSocketServerHandshakerFactory = new WebSocketServerHandshakerFactory(
                "ws://localhost:8080/websocket",null,false
        );
        handshaker = webSocketServerHandshakerFactory.newHandshaker(request);
        if (handshaker == null)
        {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        }
        else
        {
            handshaker.handshake(ctx.channel(),request);
        }
    }

    private static void sendHttpResponse(ChannelHandlerContext ctx,
                                         FullHttpRequest request, FullHttpResponse response)
    {
        if (response.getStatus().code() != 200)
        {
            ByteBuf buf = Unpooled.copiedBuffer(response.getStatus().toString(), CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
            setContentLength(response,response.content().readableBytes());
        }
        ChannelFuture channelFuture = ctx.channel().writeAndFlush(response);
        if (!isKeepAlive(request) || response.getStatus().code() != 200)
        {
            channelFuture.addListener(ChannelFutureListener.CLOSE);
        }
    }
}

 

客户端代码如下:

<!DOCTYPE html>
<html lang="ch">
<head>
    <meta charset="UTF-8">
    Netty WebSocket时间服务器
    <title>Title</title>
</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" id="message" 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>

 

四,总结

 

通过Netty WebSocket开发,可以更好的掌握如何利用Netty提供的WebSocket协议栈进行WebSocket应用程序的开发。

由于WebSocket本身的复杂性,以及可以通过多种形式承载消息,所以它的API和用法也非常多。希望本章的例程能够起到抛砖引玉的作用。

 

分享到:
评论

相关推荐

    Netty 框架学习 —— 第一个 Netty 应用(csdn)————程序.pdf

    在本篇关于“Netty框架学习——第一个Netty应用”的文章中,我们将深入理解如何使用Netty构建一个简单的Echo服务器和客户端。Netty是一个高性能、异步事件驱动的网络应用程序框架,广泛应用于Java领域的服务器开发。...

    Netty权威指南 PDF电子书下载 带目录书签 完整版

    基础篇 走进Java NIO 入门篇 Netty NIO开发指南 中级篇 Netty编解码开发指南 高级篇 Netty多协议开发和应用 源码分析篇 Netty功能介绍和源码分析 架构和行业应用篇 Netty高级特性

    深入浅出Netty

    《深入浅出Netty》是一本专注于介绍Netty框架的专著,旨在帮助读者全面理解并...通过深入阅读《深入浅出Netty》这本书,读者可以逐步掌握这些核心概念,并将Netty应用到实际的项目开发中,提升网络通信的效率和可靠性。

    Netty最容易懂的实例和教材

    提供的两个Netty精品教材,可能是详尽的教程或者深入的技术指南,涵盖了Netty的基础到高级主题。教材可能包括以下内容: - **基础篇**:介绍Netty的基本架构,如ByteBuf(缓冲区)、ChannelFuture和Promise(异步...

    使用jboss netty 创建高性能webservice客户端及服务端

    本篇文章将深入探讨如何利用JBoss Netty创建高效的Web Service客户端和服务端。 首先,我们需要理解Netty的基本工作原理。Netty采用非阻塞I/O模型,基于Java NIO(非阻塞输入/输出)库,允许在网络操作中进行高并发...

    Netty权威指南 PDF电子书 带目录书签 完整版

    本篇文章将深入探讨Netty的关键知识点,帮助读者理解和掌握这一强大的网络编程工具。 1. **Netty概述** - Netty的起源与背景:Netty由JBoss创始人Markus Stenberg发起,旨在提供一个高效、灵活且易于使用的网络...

    基于Netty实现CometStreaming方式的聊天室

    本篇将深入探讨如何利用Netty实现CometStreaming方式的聊天室。 首先,我们要理解CometStreaming是什么。Comet是一种Web技术,它允许服务器向浏览器推送数据,而不是传统的HTTP请求-响应模式。在聊天室场景中,这种...

    Netty权威指南

    Netty 基础篇:java的IO演进之路; BIO ;NIO;伪异步;NIO类库 ; 入门篇:Jetty简单应用入门;...高级篇:Http协议开发; Netty 协议栈开发(数据结构定义,消息编解码,握手安全认证,心跳检测等); WebSocket等

    Java学习之IO总结及mina和netty

    综上所述,这篇博客可能涵盖了Java基本IO概念、NIO的原理和使用,以及如何使用Mina或Netty构建高性能的网络服务,同时提供了非阻塞服务器和客户端的示例代码。这些内容对于深入理解Java网络编程和提升系统性能至关...

    Netty开发记录三

    在本篇“Netty开发记录三”中,我们将深入探讨Netty框架的使用,这是一个高效、灵活且可扩展的网络应用程序框架,广泛应用于高性能服务器和客户端的开发。Netty简化了网络编程,提供了异步事件驱动的网络通信模型,...

    javaNetty仿微信源码

    本篇将深入探讨如何利用Java Netty框架来实现类似微信的功能,以及相关的技术细节。 1. **Netty基础** - **非阻塞I/O**:Netty基于NIO(Non-blocking I/O)模型,提高了处理大量连接的能力,特别适合于高并发的...

    java高级编程胶片

    虽然描述部分为空,但根据提供的博客链接(https://xgw123485.iteye.com/blog/1178464),我们可以推测这可能是一个在线分享的资源,博主xgw123485在ITEYE平台上发布了一篇关于Java高级编程的文章或者教程。...

    java开发程序指南进阶篇

    同时,NIO(非阻塞I/O)和异步I/O(AIO)为高性能网络应用提供了可能,如Netty框架就是基于这些概念构建的。 异常处理是Java程序健壮性的保障。通过try-catch-finally语句块,我们可以捕获并处理运行时出现的异常,...

    Java高手真经(网络开发卷)源码

    - Netty框架:高性能的异步事件驱动网络应用框架,可能用于构建高效的网络应用服务器。 6. **性能优化与调试** - 网络延迟优化:源码可能包含如何减少网络延迟,提高网络通信效率的策略。 - 错误处理与日志记录...

    jboss-3.2.5.zip

    本篇文章将详细讲解"jboss-3.2.5.zip"这一版本的JBoss安装、配置及EJBCA的安装和设置过程。 一、JBoss 3.2.5简介 JBoss 3.2.5是在2004年发布的一个版本,它是Java社区的重要里程碑,为开发者提供了轻量级、开源的...

    java高手真经:网络开发卷源码

    7. **Java网络框架**:Spring Boot、Netty、Grizzly等框架简化了网络应用的开发,提供了高级功能如异步处理、性能优化和自定义协议支持。 8. **分布式服务**:Java在分布式计算中的应用,如RMI(Remote Method ...

    Java Socket编程

    这篇博客将深入探讨Java Socket编程的核心概念、用法以及相关工具。 首先,我们来了解Socket的基本概念。Socket可以被看作是网络上的端点,它允许两个应用程序通过Internet进行通信。一个Socket由IP地址和端口号...

    2021一线大厂java面试题.zip

    三、高级篇 1. JVM优化:了解JVM内存结构,垃圾回收机制,调优策略。 2. Spring框架:理解IoC、AOP原理,使用Spring Boot和Spring Cloud进行微服务开发。 3. 数据库:MySQL基础,索引原理,事务处理,JDBC操作,...

Global site tag (gtag.js) - Google Analytics