1 概述
(1) Http协议是无状态的,浏览器和服务器间的请求响应一次,下ー次会重新创建连接。
(2) 要求:实现基于 websockete的长连接的全双工的交互。
(3) 改变Http协议多次请求的约束,实现长连接了,服务器可以发送消息给浏览器。
(4) 客户端浏览器和服务器端会相互感知,比如服务器关闭了,浏览器会感知,同样浏览器关闭了,服务器会感知。
2 实现
2.1 WebSocketServerConsts
public final class WebSocketServerConsts {
private WebSocketServerConsts() {
}
public static final Integer port = 8888;
}
2.2 WebSocketHandler
public class WebSocketHandler 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() + " " + msg.text()));
}
/**
* 客户端连接后
*/
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerAdded 被调用" + ctx.channel().id().asLongText());
System.out.println("handlerAdded 被调用" + ctx.channel().id().asShortText());
}
/**
*客户端断开连接时触发
*/
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerRemoved 被调用" + ctx.channel().id().asLongText());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("发生异常" + cause.getMessage());
ctx.channel();
}
}
2.3 WebSocketServerChannelInitializer
public class WebSocketServerChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//http 编码、解码器
pipeline.addLast(new HttpServerCodec());
//以块的方式写
pipeline.addLast(new ChunkedWriteHandler());
//http数据在传输过程中是分段的,HttpObjectAggregator可以将多个段聚合
pipeline.addLast(new HttpObjectAggregator(8192));
//websocketServerProtocolHandler,将http协议升级为ws协议,浏览器请求时uri为 ws://localhost:8888/hello
pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
//自定义handler
pipeline.addLast(new WebSocketHandler());
}
}
2.4 WebSocketServer
public class WebSocketServer {
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new WebSocketServerChannelInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(WebSocketServerConsts.port).sync();
System.out.println("服务器已经开启......");
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
2.5 hello.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket</title>
</head>
<body>
<form onsubmit="return false">
<textarea name="message" style="height: 300px;width: 300px;"></textarea>
<input type="button" value="发送消息" onclick="send(this.form.message.value)">
<textarea id="responseText" name="responseText" style="height: 300px;width: 300px;"></textarea>
<input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
</form>
<script>
var socket;
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8888/hello");
//接收服务器消息
socket.onmessage = function (ev) {
var rt = document.getElementById("responseText");
rt.value = rt.value + "\n" + ev.data;
}
//连接开启
socket.onopen = function (ev) {
var rt = document.getElementById("responseText");
rt.value = "连接开启......";
}
//连接关闭
socket.onclose = function (ev) {
var rt = document.getElementById("responseText");
rt.value = rt.value + "\n" + "连接关闭了......";
}
} else {
alert("当前浏览器不支持websocket");
}
//发送消息
function send(message) {
if (!window.WebSocket) {
return;
}
if (socket.readyState == WebSocket.OPEN) {
socket.send(message);
} else {
alert("连接没有开启");
}
}
</script>
</body>
</html>
3 测试
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/15101.html