使用netty实现websocket长连接通信
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。
实现长连接主要代码为
pipeline.addLast(new WebSocketServerProtocolHandler("/websocket"));
这段代码是指在服务端的处理通道上改变服务之间通信的协议
具体的实现如下
@Author bumpworld
/*服务端代码*/
public static void main(String[] args) {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try{
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
//为通道提供一系列的处理器
//使用http的编码和解码器
pipeline.addLast(new HttpServerCodec());
//因为数据是以块写的,添加ChunkedWriteHandle处理器
pipeline.addLast(new ChunkedWriteHandler());
/*
说明:
http数据在传输过程中是分段的,HttpObjectAggregator,就是可以将对个段聚合
这就是为什么,当浏览器发送大量数据时,就会发出对此http请求
*/
pipeline.addLast(new HttpObjectAggregator(8192));//8k大小
/*
WebSocketFrame下有六个子类
说明
对应websocket, 它的数据是以帧(frame)的形式传递
要使用websocket协议 则用ws:// 用ws传输协议
WebSocketServerProtocolHandle 核心功能是将http协议升级为ws协议 保持长连接
参数为请求的地址ws://127.0.0.1:7878/websocket
websocket是通过状态码101实现的 101 切换协议 通过Upgrade 消息头通知客户端采用不同的协议来完成这个请求
*/
pipeline.addLast(new WebSocketServerProtocolHandler("/websocket"));
//自定义业务的处理器
pipeline.addLast(new NettyWebSocketHandle());
}
});
ChannelFuture sync = bootstrap.bind(7878).sync();
sync.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
服务端处理器
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import java.time.LocalDateTime;
//TextWebSocketFrame 因为websocket是使用帧传输,所以传输类型要是WebSocketFrame类型的
public class NettyWebSocketHandle extends SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
System.out.println(msg.text());
ctx.writeAndFlush(new TextWebSocketFrame("服务端收到消息 "+ LocalDateTime.now()+" "+msg.text()));
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("连接到浏览器:"+ctx.channel().id().asLongText());
System.out.println("连接到浏览器:"+ctx.channel().id().asShortText());
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("浏览器断开连接:"+ctx.channel().id().asLongText());
System.out.println("浏览器断开连接:"+ctx.channel().id().asShortText());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("发生异常");
ctx.close();
}
}
前端测试
我们用一段简单的代码测试即可
<script>
var socket;
if(window.WebSocket){
socket = new WebSocket("ws://127.0.0.1:7878/websocket");
socket.onmessage = function (ev) {
var tr = document.getElementById("resultMsg");
tr.value = tr.value+"\n"+ev.data;
};
socket.onopen = function (ev) {
var tr = document.getElementById("resultMsg");
tr.value = ev.data + " :成功建立连接";
};
socket.onclose = function (ev) {
var tr = document.getElementById("resultMsg");
tr.value = tr.value+"\n"+"断开连接";
};
function send(msg) {
if(!window.socket){
//不支持websocket
return;
}
if(socket.readyState == WebSocket.OPEN){
//是否已连接
socket.send(msg);
}
}
}else {
alert("你的浏览器不支持websocket服务");
}
</script>
<form id="form" onsubmit="return false">
<textarea name="sendMsg" id="sendMsg" style="width: 500px;height: 300px"></textarea>
<input type="button" value="发送" onclick="send(this.form.sendMsg.value)">
<textarea id="resultMsg" style="width: 500px;height: 300px"></textarea>
<input type="button" value="清空内容" onclick="document.getElementById('resultMsg').value = ''">
</form>
效果
这样 一个基本的实现就差不多完成了 (▽)