网站首页 > 基础教程 正文
# 使用Netty构建基础的WebSocket
## 目录
第一章 课程介绍
第二章 IO通信类型
第三章 Netty入门
第四章 WebSocket
第五章 Netty实现
第六章 课程总结
## 第一章 课程介绍
### 什么是Netty
- ◆高性能事件驱动、异步非堵塞
- ◆基于NIO的客户端,服务器端编程框架
- ◆稳定性和伸缩性
### Netty使用场景
- ◆>高性能领域
- ◆多线程并发领域
- ◆异步通信领域
### 课程提纲
- ◆IO通信
- Netty入门.
- ◆WebSocket入i ]
### 课程前置知识
- ◆有一定的Java基础
- ◆有一定的IO编程基础
- ◆了解Java的BIO、伪异步IO、NIO和AIO
## 第二章 IO通信类型
### BIO通信
- ◆一个线程负责连接
- ◆--请求--应答
- ◆缺乏弹性伸缩能力
### 伪异步IO通信
- ◆线程池负责连接
- M请求N应答
- 线程池阻塞
### NIO通信
- ◆缓冲区Buffer
- ◆通道Channel
- ◆多路复用器Selector
### AIO通信
- ◆连接注册读写事件和回调函数
- 读写方法异步
- ◆主动通知程序
### 四种IO对比
- ◆客户端个数
- ◆IO类型
- ◆API使用难度
- 调试难度
- ◆可靠性
- 吞吐量
## 第三章 Netty入门
### 原生NIO的缺陷
- ◆>类库和API繁杂
- ◆工作量和难度大
- ◆入门门槛高
- ◆JDK NIO存在Bug
### Netty的优势
- ◆API简单
- ◆性能高
- ◆入门门槛低
- ◆成熟、稳定
## 第四章 WebSocket
### 什么是WebSocket ?
- ◆H5协议规范
- ◆握手机制
- ◆解决客户端与服务端实时通信而产生的技术
### WebSocket的优点
- ◆>节省通信开销
- < I >服务器主动传送数据给客户端
- ◆实时通信
### WebSocket建立连接
- 客户端发起握手请求
- 服务端响应请求
- 连接建立
### WebSocket生命周期
- < >打开事件
- 消息事件
- 错误事件
- 关闭事件
### WebSocket关闭连接
- 服务器关闭底层TCP连接
- ◆>客户端发起TCP Close .
## 第五章 Netty实现WebSocket
### O 5-1 WebSocket.工程全局配置类的编写(03:42)
### O 5-2 WebSocket核心类方法说明(05:54)
### O 5-3 WebSocket握手请求业务的实现(10:59)
### O 5-4 WebSocket连接业务的实现(07:58)
### O 5-5 WebSocket初始化连接时各个组件的类实现(03:11)
### O 5-6 WebSocket启动类的实现(04:35)
### O 5-7 WebSocket客户端网页基本内容的开发(05:20)
### O 5-8 WebSocket客户端JS脚本的实现(08:41)
NettyServerConfig
package com.example.demo.config;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
/**
* 存储全局Netty配置
* @author Marion
* @date 2021/5/2
*/
@Configuration
public class NettyServerConfig {
@Value("${netty.port}")
@Getter
private int port;
/**
* 存储每一个客户端接入进来时的channel对象
*/
public static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
}
WebSocketBootstrap
package com.example.demo.service.connect;
import com.example.demo.config.NettyServerConfig;
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.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;
import java.util.Date;
/**
* 基于Netty构建的WebSocket进入/离开/响应数据
* @author Marion
* @date 2021/5/2
*/
public class WebSocketBootstrap extends SimpleChannelInboundHandler<Object> {
private WebSocketServerHandshaker handshaker;
private static final String WEB_SOCKET_URL = "ws://localhost:8888/websocket";
private static final int HTTP_SUCCESS = 200;
/**
* 客户端与服务器建立连接时候调用
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
NettyServerConfig.group.add(ctx.channel());
}
/**
* 客户端与服务器断开连接时候调用
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
NettyServerConfig.group.remove(ctx.channel());
}
/**
* 服务端接受客户端发送的数据完成之后
*/
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
/**
* 工程出现异常时候调用
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
/**
* 服务端处理客户端websocket请求核心方法
*/
@Override
protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
/**
* 1. 判断是否HTTP请求还是WebSocket请求
*/
if (msg instanceof FullHttpRequest) {
handleHttpRequest(ctx, (FullHttpRequest) msg);
} else if (msg instanceof WebSocketFrame) {
handleWebSocket(ctx, (WebSocketFrame) msg);
}
}
/**
* 处理客户端向服务端发起http握手请求的业务
*/
public void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
/**
* 1. 如果不是WebSocket握手请求,则直接返回
* 1-1. 响应是否成功
* 2-1. Upgrade是否websocket
* 2. 从工厂中创建实例,handshake处理chanel中的request的请求
*/
//1. 如果不是WebSocket握手请求,则直接返回
if (!request.decoderResult().isSuccess()
|| !"websocket".contentEquals(request.headers().get("Upgrade"))) {
sendHttpRequest(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
return;
}
//2. 从工厂中创建实例,handshake处理chanel中的request的请求
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(WEB_SOCKET_URL, null, false);
handshaker = wsFactory.newHandshaker(request);
if (handshaker == null) {
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
} else {
handshaker.handshake(ctx.channel(), request);
}
}
/**
* 处理WebSocket请求
*/
private void handleWebSocket(ChannelHandlerContext ctx, WebSocketFrame frame) {
/**
* 1. 验证消息类型close/ping,目前只支持Text处理
* 2. 处理接受到数据
* 3. 广播给所有channel
*/
// 1. 验证消息类型close/ping,目前只支持Text处理
if (frame instanceof CloseWebSocketFrame) {
handshaker.close(ctx.channel(), ((CloseWebSocketFrame) frame).retain());
}
if (frame instanceof PingWebSocketFrame) {
System.out.println("暂不支持二进制消息");
ctx.channel().write(new PingWebSocketFrame(frame.content().retain()));
return;
}
if (!(frame instanceof TextWebSocketFrame)) {
System.out.println("暂不支持二进制消息");
throw new RuntimeException("【" + this.getClass().getName() + "】不支持消息");
}
// 2. 处理接受到数据
String text = ((TextWebSocketFrame) frame).text();
System.out.println("收到文本数据" + text);
TextWebSocketFrame tws = new TextWebSocketFrame(new Date().toString()
+ ctx.channel().id()
+ " : "
+ text);
// 3. 广播给所有channel
NettyServerConfig.group.writeAndFlush(tws);
}
/**
* 服务端向客户端响应消息
*/
private void sendHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request, DefaultFullHttpResponse response) {
/**
* 1. 如果响应不是200,则关闭资源
* 2. 服务端向客户端发送数据
*/
if (response.status().code() != HTTP_SUCCESS) {
ByteBuf byteBuf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8);
response.content().writeBytes(byteBuf);
byteBuf.release();
}
//2. 服务端向客户端发送数据
ChannelFuture channelFuture = ctx.channel().writeAndFlush(response);
if (response.status().code() != HTTP_SUCCESS) {
channelFuture.addListener(ChannelFutureListener.CLOSE);
}
}
}
## 第六章 课程总结
## 参考资料
- [Netty入门之WebSocket初体验](https://www.imooc.com/learn/941)
猜你喜欢
- 2024-11-08 你居然只知道蓝绿发布?今天教你全链路灰度
- 2024-11-08 redis 分布式锁的 5个坑,真是又大又深
- 2024-11-08 Blazor OIDC 单点登录授权实例7 - Blazor hybird app 端授权
- 2024-11-08 Spring Boot利用filter实现xss防御
- 2024-11-08 Spring连环CVE-2015-5211和CVE-2020-5421漏洞升级教程
- 2024-11-08 如何进行权限系统设计,一文吃透 如何设计一个权限系统
- 2024-11-08 基于Spring Boot的注解驱动式公众号极速开发框架FastBootWeixin
- 2024-11-08 教育平台项目前端:项目前后端接口联调,项目上线部署发布
- 2024-11-08 HTTP通讯框架选型HttpClient/OkHttp
- 2024-11-08 微信公众号自动回复功能开发 微信公众号平台自动回复功能
- 最近发表
- 标签列表
-
- gitpush (61)
- pythonif (68)
- location.href (57)
- tail-f (57)
- pythonifelse (59)
- deletesql (62)
- c++模板 (62)
- css3动画 (57)
- c#event (59)
- linuxgzip (68)
- 字符串连接 (73)
- nginx配置文件详解 (61)
- html标签 (69)
- c++初始化列表 (64)
- exec命令 (59)
- canvasfilltext (58)
- mysqlinnodbmyisam区别 (63)
- arraylistadd (66)
- node教程 (59)
- console.table (62)
- c++time_t (58)
- phpcookie (58)
- mysqldatesub函数 (63)
- window10java环境变量设置 (66)
- c++虚函数和纯虚函数的区别 (66)