赞
踩
WebSocket
协议是基于TCP
的一种新的网络协议,它实现了浏览器与服务器全双工(full-duplex)通信
,允许服务器主动发送信息给客户端
Http协议的弊端:
WebSocket的特性:
在实现Websocket
连线过程中,需要通过浏览器发出Websocket
连线请求,然后服务器发出回应,这个过程通常称为握手 。在 WebSocket API
,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。在此WebSocket
协议中,为我们实现即时服务带来了两大好处:
1.Header 互相沟通的Header是很小的-大概只有 2 Bytes
- GET ws://localhost:5050/websocket HTTP/1.1
- Host: localhost:5050
- Connection: Upgrade
- Pragma: no-cache
- Cache-Control: no-cache
- Upgrade: websocket
- Origin: http://localhost:63342
- Sec-WebSocket-Version: 13
- User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36
- Accept-Encoding: gzip, deflate, br
- Accept-Language: zh-CN,zh;q=0.8
- Cookie: Idea-d796403=9d25c0a7-d062-4c0f-a2ff-e4da09ea564e
- Sec-WebSocket-Key: IzEaiuZLxeIhjjYDdTp+1g==
- Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key
是随机生成的,服务端会使用它加密后作为 Sec-WebSocket-Accept
的值返回;Sec-WebSocket-Protocol
是一个用户定义的字符串,用来区分同URL下,不同的服务所需要的协议;Sec-WebSocket-Version
是告诉服务器所使用的Websocket Draft(协议版本)
2.Server Push 服务器的推送,服务器不再被动的接收到浏览器的请求之后才返回数据,而是在有新数据时就主动推送给浏览器。
- HTTP/1.1 101 Switching Protocols
- upgrade: websocket
- connection: Upgrade
- sec-websocket-accept: nO+qX20rjrTLHaG6iQyllO8KEmA=
经过服务器的返回处理后连接握手成功,后面就可以进行TCP通讯,WebSocket
在握手后发送数据并象下层TCP
协议那样由用户自定义,还是需要遵循对应的应用协议规范…
定义初始化参数
- public interface Init {
- int PORT = 5050;
- String HOST = "localhost";
- String WEB_SOCKET_URL = String.format("ws://%s:%d/websocket", HOST, PORT);
- }
1.创建一个WebSocketServer
类,然后重写初始化事件(基本上与上一章编写的文件下载
类似,都需要依赖HTTP
的解码器与通信支持的模块…)
- public class WebSocketServer {
- private static final Logger LOG = Logger.getLogger(WebSocketServer.class.getName());
-
- public static void run(int port) throws Exception {
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- EventLoopGroup workerGroup = new NioEventLoopGroup();
- try {
- ServerBootstrap bootstrap = new ServerBootstrap();
- bootstrap.group(bossGroup, workerGroup)
- .channel(NioServerSocketChannel.class)
- .childHandler(new ChannelInitializer<Channel>() {
- @Override
- protected void initChannel(Channel channel) throws Exception {
- ChannelPipeline pipeline = channel.pipeline();
- pipeline.addLast("http-codec", new HttpServerCodec()); // Http消息编码解码
- pipeline.addLast("aggregator", new HttpObjectAggregator(65536)); // Http消息组装
- pipeline.addLast("http-chunked", new ChunkedWriteHandler()); // WebSocket通信支持
- pipeline.addLast("handler", new WebSocketServerHandler()); // WebSocket服务端Handler
- }
- });
- Channel channel = bootstrap.bind(port).sync().channel();
- LOG.info("WebSocket 已经启动,端口:" + port + ".");
- channel.closeFuture().sync();
- } finally {
- bossGroup.shutdownGracefully();
- workerGroup.shutdownGracefully();
- }
- }
-
- public static void main(String[] args) throws Exception {
- WebSocketServer.run(Init.PORT);
- }
- }
2.创建WebSocketServerHandler
,重写以下三个方法
Channel
读取完毕后执行的回调操作- public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {
-
- private static final Logger LOG = Logger.getLogger(WebSocketServerHandler.class.getName());
- private WebSocketServerHandshaker handshaker;
-
- @Override
- public void messageReceived(ChannelHandlerContext ctx, Object msg)
- throws Exception {
- // 传统的HTTP接入
- if (msg instanceof FullHttpRequest) {
- handleHttpRequest(ctx, (FullHttpRequest) msg);
- }
- // WebSocket接入
- else if (msg instanceof WebSocketFrame) {
- handleWebSocketFrame(ctx, (WebSocketFrame) msg);
- }
- }
- @Override
- public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
- ctx.flush();
- }
-
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
- throws Exception {
- cause.printStackTrace();
- ctx.close();
- }
- }
第一次握手
请求是由HTTP
协议承载来完成握手请求操作
3.定义handleHttpRequest
与sendHttpResponse
方法,处理HTTP
的请求,首先判断是否为WebSocket握手请求
,如果不是则抛出错误消息
- private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception {
- // 如果HTTP解码失败,返回HHTP异常
- if (!req.decoderResult().isSuccess() || (!"websocket".equals(req.headers().get("Upgrade")))) {
- sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1,
- BAD_REQUEST));
- return;
- }
- // 构造握手响应返回,本机测试
- WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(Init.WEB_SOCKET_URL, null, false);
- handshaker = wsFactory.newHandshaker(req);
- if (handshaker == null) {
- WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
- } else {
- handshaker.handshake(ctx.channel(), req);
- }
- }
-
- private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, FullHttpResponse res) {
- // 返回应答给客户端
- if (res.status().code() != 200) {
- ByteBuf buf = Unpooled.copiedBuffer(res.status().toString(), CharsetUtil.UTF_8);
- res.content().writeBytes(buf);
- buf.release();
- setContentLength(res, res.content().readableBytes());
- }
- // 如果是非Keep-Alive,关闭连接
- ChannelFuture f = ctx.channel().writeAndFlush(res);
- if (!isKeepAlive(req) || res.status().code() != 200) {
- f.addListener(ChannelFutureListener.CLOSE);
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。