资讯专栏INFORMATION COLUMN

慕课网_《Netty入门之WebSocket初体验》学习总结

Noodles / 1488人阅读

时间:2018年04月11日星期三
说明:本文部分内容均来自慕课网。@慕课网:https://www.imooc.com
教学源码:https://github.com/zccodere/s...
学习源码:https://github.com/zccodere/s...

第一章:课程介绍 1-1 课程介绍

什么是Netty

高性能、事件驱动、异步非阻塞的IO Java开源框架

基于NIO的客户端,服务端编程框架

非常可靠的稳定性和伸缩性

Netty使用场景

高性能领域:游戏、大数据、分布式计算

多线程并发领域:多线程模型、主从多线程模型

异步通信领域:异步非阻塞,主动获取或通过通知机制来得到结果

课程提纲

IO通信:BIO、伪异步IO、NIO、AIO通信

Netty入门:原生NIO的缺陷、Netty的优势

WebSocket入门:什么是WebSocket、如何建立连接、生命周期及关闭

Netty实现WebSocket通信案例

课程要求

有一定的Java基础

有一定的IO编程基础

了解Java的BIO、伪异步IO、NIO和AIO

第二章:IO通信 2-1 IO通信

BIO通信

一个线程负责连接

一请求一应答

缺乏弹性伸缩能力

BIO通信模型

伪异步IO通信

线程池负责连接

M请求N应答

线程池阻塞

伪异步IO通信模型

NIO通信

缓冲区Buffer

通道Channel

多路复用器Selector

AIO通信

连接注册读写事件和回调函数

读写方法异步

主动通知程序

四种IO对比

第三章:Netty入门 3-1 Netty入门

原生NIO的缺陷

类库和API繁杂

入门门槛高

工作量和难度大

JDK NIO存在BUG

Netty的优势

API使用简单,定制能力强,可以通过ChannelHandler对框架进行灵活的扩展

入门门槛低,功能强大,预制了多种编解码功能,支持多种主流协议

性能高,通过与其他的业界主流的NIO框架对比,Netty的综合性能最优

Netty比较成熟稳定,Netty修复了JDK NIO所有发现的BUG

第四章:WebSocket入门 4-1 WebSocket入门

什么是WebSocket

H5协议规范:H5提出的协议规范

握手机制:使客户端与服务器能够建立类似TCP的连接,方便通信

解决客户端与服务端实时通信而产生的技术:基于TCP的协议

WebSocket的优点

节省通信开销:以前使用轮询,开销较大

服务器主动传送数据给客户端:任意时刻,相互传送数据

实时通信:可以彼此相互推送信息

WebSocket建立连接

客户端发起握手请求

服务器响应请求

连接建立

WebSocket生命周期

打开事件:发生新的连接时调用、在端点上建立连接时且在任何事件之前

消息事件:接收WebSocket对话中,另一端发送的消息

错误事件:在进行连接或端点发生错误时产生

关闭事件:表示WebSocket端点的连接关闭

WebSocket关闭连接

服务器关闭底层TCP连接

客户端发起TCP Close

第五章:通信案例 5-1 通信案例

基于Netty实现WebSocket通信案例

功能介绍

Netty开发服务端

Html实现客户端

实现服务端与客户端的实时交互

代码编写

1.创建名为netty-websocket的maven工程pom如下



    4.0.0

    com.myimooc
    netty-websocket
    0.0.1-SNAPSHOT

    
        
            io.netty
            netty-all
            5.0.0.Alpha1
        
    

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.8
                    1.8
                    UTF-8
                
            
        
    
    

2.编写NettyConfig类

package com.myimooc.netty.websocket;

import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

/**
 * 
* 标题: Netty 全局配置类
* 描述: 存储整个工程的全局配置
* * @author zc * @date 2018/04/11 */ public class NettyConfig { /** * 存储每一个客户端接入进来时的 Channel */ public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); }

3.编写MyWebSocketHandler类

package com.myimooc.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 java.util.Date;

/**
 * 
* 标题: 处理客户端WebSocket请求的核心业务处理类
* 描述: 接收/处理/响应 客户端websocket请求的核心业务处理类
* * @author zc * @date 2018/04/11 */ public class MyWebSocketHandler extends SimpleChannelInboundHandler { private WebSocketServerHandshaker handshaker; private static final String WEB_SOCKET_URL = "ws://localhost:8888/websocket"; /** * 服务端处理客户端websocket请求的核心方法 */ @Override protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { if (msg instanceof FullHttpRequest) { // 处理客户端向服务端发起http握手请求的业务 FullHttpRequest request = (FullHttpRequest) msg; this.handHttpRequest(ctx, request); } else if (msg instanceof WebSocketFrame) { // 处理websocket连接的业务 WebSocketFrame frame = (WebSocketFrame) msg; this.handWebSocketFrame(ctx, frame); } } /** * 处理客户端与服务端之前的websocket业务 */ private void handWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { if (frame instanceof CloseWebSocketFrame){ // 如果是关闭websocket的指令 handshaker.close(ctx.channel(),(CloseWebSocketFrame)frame.retain()); } if (frame instanceof PingWebSocketFrame){ // 如果是ping消息 ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); return; } if (!(frame instanceof TextWebSocketFrame)){ // 如果不是文本消息,则抛出异常 System.out.println("目前暂不支持二进制消息"); throw new RuntimeException("【"+this.getClass().getName()+"】不支持二进制消息"); } // 获取客户端向服务端发送的文本消息 String request = ((TextWebSocketFrame) frame).text(); System.out.println("服务端收到客户端的消息=====>>>" + request); // 将客户端发给服务端的消息返回给客户端 TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString() + ctx.channel().id() + "====>>>" + request); // 群发,服务端向每个连接上来的客户端群发消息 NettyConfig.group.writeAndFlush(tws); } /** * 处理客户端向服务端发起http握手请求的业务 */ private void handHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) { if (!request.getDecoderResult().isSuccess() || !("websocket").equals(request.headers().get("Upgrade"))) { // 不是websocket握手请求时 this.sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST)); return; } WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(WEB_SOCKET_URL, null, false); handshaker = wsFactory.newHandshaker(request); if (handshaker == null) { WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); } else { handshaker.handshake(ctx.channel(), request); } } /** * 服务端向客户端响应消息 */ private void sendHttpResponse(ChannelHandlerContext ctc, FullHttpMessage request, DefaultFullHttpResponse response) { if (response.getStatus().code() != 200) { ByteBuf buf = Unpooled.copiedBuffer(response.getStatus().toString(), CharsetUtil.UTF_8); response.content().writeBytes(buf); buf.release(); } // 服务端向客户端发送数据 ChannelFuture future = ctc.channel().writeAndFlush(response); if (response.getStatus().code() != 200) { future.addListener(ChannelFutureListener.CLOSE); } } /** * 工程出现异常时调用 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } /** * 客户端与服务端创建连接时调用 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { NettyConfig.group.add(ctx.channel()); System.out.println("客户端与服务端连接开启..."); } /** * 客户端与服务端断开连接时调用 */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { NettyConfig.group.remove(ctx.channel()); System.out.println("客户端与服务端连接关闭..."); } /** * 服务端接收客户端发送过来的数据结束之后调用 */ @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } }

4.编写MyWebSocketChannelHandler类

package com.myimooc.netty.websocket;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.stream.ChunkedWriteHandler;


/**
 * 
* 标题: 初始化连接时的各个组件
* 描述: 初始化连接时的各个组件
* * @author zc * @date 2018/04/11 */ public class MyWebSocketChannelHandler extends ChannelInitializer { @Override protected void initChannel(SocketChannel ch) throws Exception { // 将请求和应答消息解码为HTTP消息 ch.pipeline().addLast("http-codec",new HttpServerCodec()); // 将HTTP消息的多个部分合成一条完整的HTTP消息 ch.pipeline().addLast("aggregator",new HttpObjectAggregator(65536)); // 向客户端发送HTML5文件 ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler()); ch.pipeline().addLast("handler",new MyWebSocketHandler()); } }

5.编写AppStart类

package com.myimooc.netty.websocket;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

/**
 * 
* 标题: 程序入口
* 描述: 启动应用
* * @author zc * @date 2018/04/11 */ public class AppStart { public static void main(String[] args) { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workGroup = new NioEventLoopGroup(); try { ServerBootstrap serverBootstrap = new ServerBootstrap(); serverBootstrap.group(bossGroup,workGroup); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.childHandler(new MyWebSocketChannelHandler()); System.out.println("服务端开启等待客户端连接..."); Channel channel = serverBootstrap.bind(8888).sync().channel(); channel.closeFuture().sync(); }catch (Exception e){ e.printStackTrace(); }finally { // 优雅的退出程序 bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }

6.编写websocket.html




    
    
    WebSocket客户端

    







客户端接收到服务端返回的应答消息

第六章:课程总结 6-1 课程总结

课程总结

课程介绍

IO通信:四种IO通信

Netty入门:原生NIO的缺点,Netty的优点

WebSocket入门:WebSocket的优点,如何使用

通信案例:Netty实现WebSocket通信案例

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69093.html

相关文章

  • WebSocket就是这么简单

    摘要:是一个持久化的协议,相对于这种非持久的协议来说。最大的特点就是实现全双工通信客户端能够实时推送消息给服务端,服务端也能够实时推送消息给客户端。参考链接知乎问题原理原理知乎问题编码什么用如果文章有错的地方欢迎指正,大家互相交流。 前言 今天在慕课网上看到了Java的新教程(Netty入门之WebSocket初体验):https://www.imooc.com/learn/941 WebS...

    hikui 评论0 收藏0
  • 课网_《第一个docker化的java应用》学习总结

    摘要:时间年月日星期四说明本文部分内容均来自慕课网。仓库构建镜像的目的是为了在其机器上运行镜像程序。使用参数,冒号前面为主机端口,后面为容器端口。 时间:2017年04月27日星期四说明:本文部分内容均来自慕课网。@慕课网:http://www.imooc.com教学示例源码:无个人学习源码:无 第一章:课程介绍 1-1 课程介绍 Docker能火的原因 快速的持续集成 服务的弹性伸缩 部署...

    李涛 评论0 收藏0

发表评论

0条评论

Noodles

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<