WebSocket
为什么使用进阶版呢? 新的技术肯定是解决了 技术的问题
Http协议都的缺陷 :
通信只能由客户端发起,需要一种服务端能够主动推送到能力 –
websocket
**websocket:**这是一种双向通信的能力 也叫做 : “全双工”
websocket
是由浏览器发起的
通常我们使用 http叫做 : 短连接 性能好一点
用 websocket 叫做 : 长连接 复用性高一些
适用于不同场景
这里我们还是使用 http来链连接 只不过 连接之后就传输信息 直到断开为止
应为本质上 http 底层是 tcp 是支持实现全双工的
协议标识符 http://127.0.0.1:8080 ws://127.0.0.1:7777
GET ws://127.0.0.1:7777 HTTP/1.1
Host: 127.0.0.1
Upgrade: websocket # 升级为ws
Connection: Upgrade # 此链接需要升级
Sec-WebSocket-key: client-random-string ... # 标识加密相关信息
响应结果
HTTP/1.1 101
Upgrade: websocket
Connection: Upgrade
响应码 101 代表本次协议需要更改为websocket
连接建立后,支持文本信息及二进制信息。
Websocket实现的原理:
通过http协议进行连接的建立(握手和回答),建立连接后不再使用http,而tcp自身是支持双向通信的,所以能达到“全双工”的效果。
Websocket 应用demo
服务端代码
public class WebSocketServer {
public static void main(String[] args) {
//可以自定义线程的数量
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
// 默认创建的线程数量 = CPU 处理器数量 *2
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler())
//当前连接被阻塞的时候,BACKLOG代表的事 阻塞队列的长度
.option(ChannelOption.SO_BACKLOG, 128)
//设置连接保持为活动状态
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new WebSocketInitialzer());
try {
ChannelFuture future = serverBootstrap.bind(7777).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
服务端初始化器
public class WebSocketInitialzer extends ChannelInitializer<Channel> {
@Override
protected void initChannel(Channel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//增加编解码器 的另一种方式
pipeline.addLast(new HttpServerCodec());
// 块方式写的处理器 适合处理大数据
pipeline.addLast(new ChunkedWriteHandler());
//聚合
pipeline.addLast(new HttpObjectAggregator(512 * 1024));
/*
* 这个时候 我们需要声明我们使用的是 websocket 协议
* netty为websocket也准备了对应处理器 设置的是访问路径
* 这个时候我们只需要访问 ws://127.0.0.1:7777/hello 就可以了
* 这个handler是将http协议升级为websocket 并且使用 101 作为响应码
* */
pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
pipeline.addLast(new WebSocketHandler());
}
}
服务端处理器
通信使用的单位叫帧 frame
客户端:发送时将消息切割成多个帧
服务端:接收时,将关联的帧重新组装
/*
* 泛型 代表的是处理数据的单位
* TextWebSocketFrame : 文本信息帧
* */
public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) throws Exception {
//可以直接调用text 拿到文本信息帧中的信息
System.out.println("msg:" + textWebSocketFrame.text());
Channel channel = ctx.channel();
//我们可以使用新建一个对象 将服务端需要返回的信息放入其中 返回即可
TextWebSocketFrame resp = new TextWebSocketFrame("hello client from websocket server");
channel.writeAndFlush(resp);
}
}
websocket前端编写
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<script>
var socket;
// 判断当前浏览器是否支持websocket
if (!window.WebSocket) {
alert("不支持websocket")
} else {
<!-- 创建websocket 连接对象-->
socket = new WebSocket("ws://127.0.0.1:7777/hello");
//设置开始连接的方法
socket.onopen = function (ev) {
var tmp = document.getElementById("respText");
tmp.value = "连接已经开启";
}
//设置关闭连接的方法
socket.onclose = function (ev) {
var tmp = document.getElementById("respText");
tmp.value = tmp.value + "\n" + "连接已经关闭";
}
//设置接收数据的方法
socket.onmessage = function (ev) {
var tmp = document.getElementById("respText");
tmp.value = tmp.value + "\n" + ev.data;
}
}
function send(message) {
if (!window.socket) {
return
}
/*
* 判断socket的状态
* connecting 正在连接 closing 正在关闭
* closed 已经关闭或打开连接失败
* open 连接可以 已经正常通信
* */
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
} else {
alert("连接未开启");
}
}
</script>
<!--防止表单自动提交-->
<form onsubmit="return false">
<textarea name="message" style="height: 400px;width: 400px"></textarea>
<input type="button" value="发送" onclick="send(this.form.message.value)">
<textarea id="respText" style="height: 400px;width: 400px"></textarea>
</form>
</body>
</html>
【客户端】
var ws = new WebSocket("ws://127.0.0.1:7777/hello");
ws.onopen = function(ev){
ws.send("hello"); //建立连接后发送数据
}
设计一个样式
左右两个各有一个文本框,中间放一个发送按钮。
左侧文本框用来发送数据,右侧文本框用来显示数据。
演示效果
启动服务发送消息
服务器接受到的消息
小结
- websocket 一般用于做可复用连接,http一般做短链接
- websocket解决了http连接只能客户端发起的痛点