在本章的开头把代码奉上,大家下载下来对照的学习,这些代码都是运行通过的。
上节我们讲解了HTTP协议开发,但是,Http协议的开销问题,导致它们不适用于低延迟的应用。为了解决这些问题,我们引入了webSocket。
我们来总结一下HTTP协议的弊端:
1.HTTP协议是半双工的协议。大家知道对讲机吗?它就是半双工的设备。当对方在说话时,你就不能说话了,也就是说一个时间点上,只能一个人在说话。现在已经被全双工的电话给替代掉了,现在对讲机这东西用的不多了。
2.HTTP协议冗长而繁琐。HTTP消息包含消息头,消息体,换行符等,采用文本形式传播,相比二进制通信协议,非常冗长且繁琐。
3.针对服务器推送的黑客攻击。例如长时间轮询。
为了解决以上HTTP协议效率底下的问题,WebSocket协议应运而生,它能更好的节约带宽和服务器资源并且实时通信。下面就让我们看看websocket协议的神奇之处。
1.单一的TCP连接,采用全双工通信
2.对代理,防火墙,路由器透明。
3.无头部信息,cookie和身份验证
4.无安全开销
5.通过ping帧保持链路激活
6.服务器可以主动的对客户端进行发送消息,而不需要轮询。
总的来说我们webSocket被设计出来的目的就是为了取代轮询这种高消耗的方式。
建立连接过程如下:
1.客户端浏览器首先要向服务器发送一个HTTP请求,这个请求和通常的HTTP请求不同,包含了一些附加信息,其中附加信息“UpgradeWebSocket”表明这是一个申请协议升级的http请求。
2.服务器解析这些附加头信息,然后生成应答信息返回给客户端,这样客户端和服务器端的webSocket连接就建立起来了
3.这个连接会一直持续到客户端或者服务器的某一方主动关闭连接。
我们来看一下客户端向服务器请求的消息:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade:websocket
Connection:Upgrade
Sec-WeSocket-Key:dGhlweidxlkjsdZQ==
Origin:http://example.com
Sec-WebSocket-Protocol:chat,superchat
Sec-webSocket-Version:13
我们来看一下服务器返回给客户端的应答消息:
HTTP/1.1 101 Switching Protocols
Upgrade:websocket
Connection:Upgrade
Sec-WebSocket-accept:siefjosigdfsl=
Sec-WebSocket-Protocol:chat
请求消息中的“Sec-WebSocket-Key”是随机的,服务器端会用这些数据来构造出一个SHA-1的信息摘要,把“Sec-WebSocket-Key”加上一个魔幻字符串“25EAFA5-E914-47DA-95CA-C54B0DC85B11”.使用SHA-1加密,然后进行BASE-64编码,将结果作为Sec-WebSocket-accept头的值,返回给客户端。
连接示意图如下:
握手成功后,客户端和服务器就可以通过”message”方式进行通信了,一个消息由一个或者多个帧组成。
为关闭websocket连接,客户端和服务器需要通过一个安全的方法关闭底层TCP连接及TLS会话。如果合适,丢弃任何可能接收的字节:必要时可以通过任何可用的手段关闭连接。
需求:我们要编写一个webSocket服务器,支持WebSocket的浏览器通过webSocket协议发送请求给我们编写的webSocket服务器,服务器对请求消息进行判断,如果是合法的webSocket请求,则获取请求消息体,并在后面追加字符串:“欢迎使用Netty WebSocket 服务,现在时刻:系统时间”。
当然客户端也是我们编写的,其中内嵌js脚本去创建webSocket连接,如果握手成功打印:“打开websocket 服务正常,浏览器支持Websocket!”
首先对webSocket服务器的功能进行简单讲解。websocket服务端接收到请求消息后,先对消息的类型进行判断,如图所示:
我们就是判断upgrade取它的值判断是否是websocket,如果不是webSocket类型的请求消息,则返回HTTP 400 BAD REQUEST 响应给客户端。如果是服务器返回消息,双方连接正式建立。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//HttpServerCodec的作用是将请求和应答消息编码或者解码为HTTP消息;
ch.pipeline().addLast("http-codec", new HttpServerCodec());
//使用HttpObjectAggregator会把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse
ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
//支持处理异步发送大数据文件,但不占用过多的内存,防止发生内存泄漏,这里是向客户端发送html5文件
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
//这个是我们自定义的,处理文件服务器逻辑。主要功能还是在这个文件中
ch.pipeline().addLast("http-fileServerHandler", new WebSocketServerHandler());
}
});
Channel ch = b.bind(port).sync().channel();//这里写你本机的IP地址
System.out.println("web socket server started at port "+port+".");
System.out.println("open your browser and navigate to http://localhost:"+port+"/");
ch.closeFuture().sync();
} catch (Exception e) {
}finally{
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
讲解:
HttpServerCodec讲解:HTTP协议的请求解码器和响应编码器即HttpServerCodec,它会将HTTP客户端请求转成HttpRequest对象,将HttpResponse对象编码成HTTP响应发送给客户端。
HttpObjectAggregator讲解:使用HttpObjectAggregator会把多个消息转换为一个单一的FullHttpRequest或是FullHttpResponse
/**
* @author 作者 YYD
* @version 创建时间:2016年8月17日 上午6:32:23
* @function 未添加
*/
public class WebSocketServerHandler extends SimpleChannelInboundHandler
上面的代码,我都做的详细的注释,大家可以对照着看看。
在handleHttpRequest方法中我们判断消息头中是否包含“Upgrade”字段,如果不包含就返回Http 400 响应。握手请求经过简单校验后,我们通过构造握手工厂,创建握手出理类webSocketServerHandshaker,通过它构造握手响应消息返回给客户端,同时将webSocket消息的编码和解码类动态添加到ChannelPipeline中,用于websocket消息的编解码。
netty websocket 时间服务器
我们这里不加讲解了,大家看看就明白了。
初始状态效果图
发送消息效果图
在本章的结尾把代码奉上,大家下载下来对照的学习,这些代码都是运行通过的。
好了就讲到这里吧,由于HttP协议本身存在的弊端,产生了websocket协议。希望对大家有所帮助。有不懂的可以联系我[email protected]
同时鼓励自己,坚持就会有奇迹,加油!