-
Notifications
You must be signed in to change notification settings - Fork 0
5 Websocket
BladeCode edited this page Nov 23, 2019
·
1 revision
WebSocket 是HTML5 规范的一部分,也是基于 HTTP 协议之上的一种协议,WebSocket主要是解决 HTTP 上存在的一些问题;
- HTTP 一种无状态(同一客户端发出的第一次请求接收到响应后,客户端发送第二次请求,这两次请求之间没有任何关联)的协议,HTTP 无法追踪某一请求来自哪一个客户端,客户端之前在服务器上存在一些信息(常见的解决方式:cookie,session)
- HTTP 是基于请求响应模式的协议,请求的发起方一定是客户端,服务器将响应返回给客户端后连接就断掉了(HTTP 1.0),在 1.0 的基础上连接可以短时间的保持,一种 keep-alive 机制(HTTP 1.1)
通常我们所使用的长连接技术
- 早期采用轮询的方式保持与服务器的连接
- 目前通常采用 Websocket 的连接方式保持与服务器的连接
- 客户端(浏览器)与服务器建立连接后,没有其他因素干扰,连接是不会断,一直存在,客户端与服务器双方是对等的,不再区分谁是客户端,谁是服务端,客户端可以发送数据给服务端,服务端也可以发送数据给客户端,在真正意义上实现了服务端的推技术
- 长连接在建立初期会发送带有 header 头信息的网络请求,在连接建立后,在长连接之上只需要发送需要传递的数据(真正的数据)即可
- Websocket 是基于 HTTP 协议
- Websocket 也可以用于非浏览器的场景,只要你的库支持 Websocket 即可
本篇编写一个用 Netty 实现的长连接应用
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
// handler() 与 childHandler() 的区别
// 服务端可以使用 handler() 或者 childHandler(),而对于客户端,一般只使用handler()
// handler()对于处理的 bossGroup 相关的信息,比如链接后,输出日志
// childHandler() 是指连接丢给 workerGroup 之后,对 workerGroup 的 NIO 线程作用
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new WebsocketInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(4096).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
public class WebsocketInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new ChunkedWriteHandler());
// 聚合成完整的请求
pipeline.addLast(new HttpObjectAggregator(8192));
// websocket 协议的地址,这里 context_path 是 websocket
// ws://localhost:port/context_path
pipeline.addLast(new WebSocketServerProtocolHandler("/websocket"));
pipeline.addLast(new WebsocketTextHandler());
}
}
public class WebsocketTextHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
System.out.println("收到消息:" + msg.text());
ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间:" + LocalDateTime.now()));
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("与客户端连接建立" + ctx.channel().id().asLongText());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("连接关闭:" + ctx.channel().id().asLongText());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("异常发生");
ctx.close();
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket demo</title>
</head>
<body>
<form onsubmit="return false;">
<label>
<textarea name="message" style="width: 400px; height:200px"></textarea>
</label>
<input type="button" value="发送数据" onclick="send(this.form.message.value)">
<h3>服务器返回</h3>
<label>
<textarea id="responseText" style="width: 400px; height: 200px"></textarea>
</label>
<input type="button" value="清空内容" onclick="document.getElementById(responseText).value()=''">
</form>
</body>
<script type="text/javascript">
let socket;
if (window.WebSocket) {
// 连接 websocket
socket = new WebSocket("ws://localhost:4096/websocket");
socket.onmessage = function (event) {
const ta = document.getElementById("responseText");
ta.value = ta.value + "\n" + event.data;
};
socket.onopen = function (event) {
const ta = document.getElementById("responseText");
ta.value = "连接开启!\n";
};
socket.onclose = function (event) {
var ta = document.getElementById("responseText");
ta.value = "连接关闭!\n";
}
} else {
alert("浏览器不支持WebSocket!");
}
function send(message) {
if (!window.WebSocket) {
alert("浏览器不支持WebSocket!");
return;
}
if (socket.readyState === WebSocket.OPEN) {
socket.send(message);
} else {
alert("WebSocket连接尚未开启!");
}
}
</script>
</html>