版权声明:如需转载,请标明转载出处! https://blog.csdn.net/Z0157/article/details/82829190
由于http协议有一个缺陷:通信只能由客户端发起。如果服务器有连续的状态变化,客户端要获知只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。
而websocket就这样应用而生。
浏览器与服务器之间的长连接,是websocket的支持。WebSocket是一种在单个TCP连接上进行全双工通信的协议。而netty他就对websocket通信做了支持。
websocket除了上面的支持,还有一些其他的特点:
(1)建立在 TCP 协议之上。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,二进制数据。
(5)客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
下面我用netty对websocket的支持来实现一个完整了实现一个在线聊天功能:
服务器端的代码实现:
package com.zhurong.netty.test5;
import com.zhurong.netty.test4.NettyServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.net.InetSocketAddress;
/**
* Description:
* User: zhurong
* Date: 2018-09-24 23:17
*/
public class NettyWebSocketServer {
public static void main(String[] args) {
//接收连接
EventLoopGroup bossGroup = new NioEventLoopGroup();
//连接发送给work
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
System.out.println("服务器启动成功!");
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).
handler(new LoggingHandler(LogLevel.INFO)).
childHandler(new WebSocketChannelInitalizer());
ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(8000)).sync();
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
package com.zhurong.netty.test5;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
/**
* Description:
* User: zhurong
* Date: 2018-09-24 11:05
*/
public class WebSocketChannelInitalizer 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));
pipeline.addLast(new WebSocketServerProtocolHandler("/test"));
pipeline.addLast(new WebSockethandler());
}
}
package com.zhurong.netty.test5;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.EventExecutorGroup;
import java.util.Date;
/**
* Description:
* User: zhurong
* Date: 2018-09-24 11:15
*/
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("服务器返回消息:" + new Date()));
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerAdded");
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
System.out.println("handlerRemoved");
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("exceptionCaught");
}
}
webapp端的代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webSocketTest客户端</title>
</head>
<script type="text/javascript">
var webSocket;
if(window.WebSocket){
webSocket = new WebSocket("ws://localhost:8000/test");
//客户端收到服务器的方法,这个方法就会被回调
webSocket.onmessage = function (ev) {
var contents = document.getElementById("responseText");
contents.value = contents.value +"\n"+ ev.data;
}
webSocket.onopen = function (ev) {
var contents = document.getElementById("responseText");
contents.value = "与服务器端的websocket连接建立";
}
webSocket.onclose = function (ev) {
var contents = document.getElementById("responseText");
contents.value = contents.value +"\n"+ "与服务器端的websocket连接断开";
}
}else{
alert("该环境不支持websocket")
}
function sendMessage() {
if(window.webSocket){
if(webSocket.readyState == WebSocket.OPEN){
var contents = document.getElementById("message").value;
webSocket.send(contents);
}else{
alert("与服务器连接尚未建立")
}
}
}
</script>
<body>
<form onsubmit="return false;">
<h4>客户端输入:</h4>
<textarea id = "message" name="message" style="width: 200px;height: 100px"></textarea>
<br/>
<input type="button" value="发送到服务器" onclick="sendMessage()">
<h4>服务器返回消息:</h4>
<textarea id = "responseText" name="message" style="width: 200px;height: 100px"></textarea>
<br/>
<input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="clear data">
</form>
</body>
</html>
服务器输出:
这就是一个完整的websocket功能,当然我们其实客户端直接使用浏览器http就可以了,这样更方便。