我是靠谱客的博主 年轻玫瑰,最近开发中收集的这篇文章主要介绍Netty网络编程八:Netty之WebSocket协议栈开发详解一:概述二:websocket握手过程三:Netty中WebSocket协议栈的开发,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
一:概述
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
其使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。其中,浏览器和服务器只需要完成一次握手,
两者之间就直接可以创建持久性的连接,并进行双向数据传输。
二:websocket握手过程
一个典型的WebSocket握手请求如下:
客户端请求:
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
Sec-WebSocket-Key: sN9cRrP/n9NdMgdcy2VJFQ==
Sec-WebSocket-Version: 13
服务端响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: fFBooB7FAkLlXgRSz0BT3v4hq5s=
Sec-WebSocket-Location: ws://example.com/
注意:
- Connection 必须设置 Upgrade,表示客户端希望连接升级。
- Upgrade 字段必须设置 Websocket,表示希望升级到 Websocket 协议。
- Sec-WebSocket-Key 是随机的字符串,服务器端会用这些数据来构造出一个 SHA-1 的信息摘要。把 “Sec-WebSocket-Key” 加上一个特殊字符串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11”,然后计算 SHA-1 摘要,之后进行 BASE-64 编码,将结果做为 “Sec-WebSocket-Accept” 头的值,返回给客户端。如此操作,可以尽量避免普通 HTTP 请求被误认为 Websocket 协议。
- Sec-WebSocket-Version 表示支持的 Websocket 版本。RFC6455 要求使用的版本是 13,之前草案的版本均应当弃用。
- Origin 字段是可选的,通常用来表示在浏览器中发起此 Websocket 连接所在的页面,类似于 Referer。但是,与 Referer 不同的是,Origin 只包含了协议和主机名称。
三:Netty中WebSocket协议栈的开发
WebSocket服务端:
public class NettyWebSocketServer {
private int port;
public NettyWebSocketServer(int port){
this.port = port;
}
public void start(){
// boss 是处理客户端连接的线程池
// worker 是处理从客户端连接转发过来的IO数据的读写线程池
NioEventLoopGroup boss = new NioEventLoopGroup();
NioEventLoopGroup worker = new NioEventLoopGroup();
try{
// ServerBootstrap 对外一个便利创建服务端,Builder建造者设计模式
ServerBootstrap sb = new ServerBootstrap();
// 绑定线程池
sb.group(boss,worker)
// 绑定channel 服务端绑定NioServerSocketChannel,此实现jdk的ServerSocketChannel
.channel(NioServerSocketChannel.class)
// 绑定服务端相关参数,可添加绑定多个参数
.option(ChannelOption.SO_BACKLOG, 1024) //指定此套接口排队的最大连接个数
// IO事件处理类,主要处理IO事件的读写
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 请求和应答消息编码或者解码为HTTP消息
pipeline.addLast(new HttpServerCodec());
// 将HTTP消息的多个部分组合成一条完整的HTTP消息
pipeline.addLast(new HttpObjectAggregator(65535));
// 向客户端发送HTML5文件,主要用于支持浏览器和服务端进行WebSocket通信
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new NettyWebSocketServerHandler());
}
});
// 绑定端口,同步等待成功
ChannelFuture cf = sb.bind(port).sync();
System.out.println("服务已启动.................监听端口:" + port);
// 等待服务器监听端口关闭
cf.channel().closeFuture().sync();
}catch (Exception e){
// 优雅关闭线程资源
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
public static void main(String[] args) {
NettyWebSocketServer nettyServer = new NettyWebSocketServer(8080);
nettyServer.start();
}
}
Handler处理:
public class NettyWebSocketServerHandler extends ChannelInboundHandlerAdapter {
private WebSocketServerHandshaker handshaker;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// 客户端第一次接入,升级Upgrade websocket
if(msg instanceof FullHttpRequest){
handleHttpRequest(ctx, (FullHttpRequest)msg);
}
// websocket数据交互
else if(msg instanceof WebSocketFrame){
handleWebSocketFrame(ctx , (WebSocketFrame)msg);
}
}
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 supportes", frame.getClass().getName()));
}
// 返回应答消息
String request = ((TextWebSocketFrame) frame).text();
System.out.println("当前收到的消息是: " + request);
String timeStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
ctx.channel().write(new TextWebSocketFrame(request+ ", netty webSocket 服务端,time now is " + timeStr));
}
private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) {
// 如果http解析失败,返回异常
if(!req.decoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))){
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
return;
}
// 构造握手响应返回
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://localhost:8080/websocket",null, false);
handshaker = wsFactory.newHandshaker(req);
if(handshaker == null){
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
}else {
handshaker.handshake(ctx.channel(),req);
}
}
private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse response) {
// 返回给客户端
if(response.status().code() != 200){
ByteBuf buf = Unpooled.copiedBuffer(req.toString().toString(), CharsetUtil.UTF_8);
response.content().writeBytes(buf);
buf.release();
response.headers().set("Content-Length",response.content().readableBytes());
}
// 如果是非Keep-Alive,关闭连接
ChannelFuture f = ctx.channel().writeAndFlush(response);
if(!req.headers().get("Connection").equals("keep-alive") || response.status().code()!=200){
f.addListener(ChannelFutureListener.CLOSE);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
客户端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script type="text/javascript">
var socket;
if(window.WebSocket){
socket = new WebSocket("ws://localhost:8080/websocket");
socket.onmessage = function (ev) {
var ta = document.getElementById("responseText");
ta.value = "";
ta.value = ev.data;
};
socket.onopen = function (ev) {
var ta = document.getElementById("responseText");
ta.value = "打开websocket服务端正常,浏览器支持WebSocket!";
}
socket.onclose = function (ev) {
var ta = document.getElementById("responseText");
ta.value = "正在关闭WebSocket.....";
}
}else {
console.info("log......................")
alert("当前浏览器不支持WeboSocket协议!")
}
function send(message) {
if(!window.WebSocket){return;}
if(socket.readyState == WebSocket.OPEN){
socket.send(message);
}else {
alert("webSOcket 连接没有建立成功!")
}
}
</script>
<form "return false;">
<input type="text" name="message" value="Netty webSocket学习"/>
<br/>
<input type="button" value="发送webSocket请求消息" "send(this.form.message.value)"/>
<hr color="blue"/>
<h3>服务端返回应答消息</h3>
<textarea id="responseText" style="width:500px;height:300px;"></textarea>
</form>
</body>
</html>
核心api:
WebSocketServerHandshaker : 主要是负责客户端服务端握手协议的基类
WebSocketFrame : 负责websocket通信的基类,其子类包括
分别对不同类型的数据进行处理。
博客中案例代码:https://download.csdn.net/download/qq_22871607/11072379
最后
以上就是年轻玫瑰为你收集整理的Netty网络编程八:Netty之WebSocket协议栈开发详解一:概述二:websocket握手过程三:Netty中WebSocket协议栈的开发的全部内容,希望文章能够帮你解决Netty网络编程八:Netty之WebSocket协议栈开发详解一:概述二:websocket握手过程三:Netty中WebSocket协议栈的开发所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复